diff -Nru ruby-bson-4.2.1/bson.gemspec ruby-bson-4.3.0/bson.gemspec --- ruby-bson-4.2.1/bson.gemspec 2016-12-21 12:48:41.000000000 +0000 +++ ruby-bson-4.3.0/bson.gemspec 2018-02-06 17:17:50.000000000 +0000 @@ -5,12 +5,12 @@ Gem::Specification.new do |s| s.name = "bson" - s.version = "4.2.1" + s.version = "4.3.0" s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version= s.authors = ["Tyler Brock", "Durran Jordan", "Brandon Black", "Emily Stolfo", "Gary Murakami"] - s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMRQwEgYDVQQDDAtkcml2\nZXItcnVieTEVMBMGCgmSJomT8ixkARkWBTEwZ2VuMRMwEQYKCZImiZPyLGQBGRYD\nY29tMB4XDTE2MDQwODE0MTE0NVoXDTE3MDQwODE0MTE0NVowQjEUMBIGA1UEAwwL\nZHJpdmVyLXJ1YnkxFTATBgoJkiaJk/IsZAEZFgUxMGdlbjETMBEGCgmSJomT8ixk\nARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFdSAa8fRm1\nbAM9za6Z0fAH4g02bqM1NGnw8zJQrE/PFrFfY6IFCT2AsLfOwr1maVm7iU1+kdVI\nIQ+iI/9+E+ArJ+rbGV3dDPQ+SLl3mLT+vXjfjcxMqI2IW6UuVtt2U3Rxd4QU0kdT\nJxmcPYs5fDN6BgYc6XXgUjy3m+Kwha2pGctdciUOwEfOZ4RmNRlEZKCMLRHdFP8j\n4WTnJSGfXDiuoXICJb5yOPOZPuaapPSNXp93QkUdsqdKC32I+KMpKKYGBQ6yisfA\n5MyVPPCzLR1lP5qXVGJPnOqUAkvEUfCahg7EP9tI20qxiXrR6TSEraYhIFXL0EGY\nu8KAcPHm5KkCAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O\nBBYEFFt3WbF+9JpUjAoj62cQBgNb8HzXMCAGA1UdEQQZMBeBFWRyaXZlci1ydWJ5\nQDEwZ2VuLmNvbTAgBgNVHRIEGTAXgRVkcml2ZXItcnVieUAxMGdlbi5jb20wDQYJ\nKoZIhvcNAQEFBQADggEBAKe478140EL5v2vJMjRSGCTtv9tD7jH7595aW267n9Tg\nnuFFwHVC3cTz7xbWi8s7Dat3uC1qWhg7qPFjrF5QZymP7R3vC887XiXwJCnkilx4\ntqIhdDCk/PU1G8RvS2GtCjxcDZLzr2sLNlqC1OCplJrL8YAY6vF8i46WKudH/189\nHPLRJ8tx0aFZY7c6MO1NLqCtBs+wdB9sip7CvCA/w4Ly1MkLzVPXvzjkDJPwkoF1\nyVPbgNJA2D/PnLe8ZFMhgJf+VIA1V4oybP/o+9aqlghTtNfYHJpRCAluUiaywwXl\nKzD4mmIBpxtbWrWB3hx7tsVJmWx5zgO9aDAfvWnXfoo=\n-----END CERTIFICATE-----\n"] - s.date = "2016-12-19" + s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMRQwEgYDVQQDDAtkcml2\nZXItcnVieTEVMBMGCgmSJomT8ixkARkWBTEwZ2VuMRMwEQYKCZImiZPyLGQBGRYD\nY29tMB4XDTE3MDcwMzEyMTMyOVoXDTE4MDcwMzEyMTMyOVowQjEUMBIGA1UEAwwL\nZHJpdmVyLXJ1YnkxFTATBgoJkiaJk/IsZAEZFgUxMGdlbjETMBEGCgmSJomT8ixk\nARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFdSAa8fRm1\nbAM9za6Z0fAH4g02bqM1NGnw8zJQrE/PFrFfY6IFCT2AsLfOwr1maVm7iU1+kdVI\nIQ+iI/9+E+ArJ+rbGV3dDPQ+SLl3mLT+vXjfjcxMqI2IW6UuVtt2U3Rxd4QU0kdT\nJxmcPYs5fDN6BgYc6XXgUjy3m+Kwha2pGctdciUOwEfOZ4RmNRlEZKCMLRHdFP8j\n4WTnJSGfXDiuoXICJb5yOPOZPuaapPSNXp93QkUdsqdKC32I+KMpKKYGBQ6yisfA\n5MyVPPCzLR1lP5qXVGJPnOqUAkvEUfCahg7EP9tI20qxiXrR6TSEraYhIFXL0EGY\nu8KAcPHm5KkCAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O\nBBYEFFt3WbF+9JpUjAoj62cQBgNb8HzXMCAGA1UdEQQZMBeBFWRyaXZlci1ydWJ5\nQDEwZ2VuLmNvbTAgBgNVHRIEGTAXgRVkcml2ZXItcnVieUAxMGdlbi5jb20wDQYJ\nKoZIhvcNAQEFBQADggEBABQRiVDNfa+gKXHHgnJdafFGk6c3jzAGqc6YHq6ZIhol\noxxpgDxd3IxQfca7zvs9AYQcOrQtH1xcw3yanJp+pBTTweqYzbwZNe6QTyetjM+L\nhhudM1vXidPJMo7ZzzZ3z0rOpE6aPjqZihSDPcoKYw0FIAqxgrRqitYYdDoq3dup\nYbzsDYb5G4lbzCmqX2pu0ravi0XF9qDOgvm4j7ss9BnGuwQhhb44nevlIEv+sTg4\nUgLdYyyGANc+TOKLFmx0eAJlCljAurbX7ctO2XI0f1AUdEYSqGzut+datXK/9nwQ\nFACf8zd11PEds/Opai2Qp4aOBgaxXhhFG357umy1vRs=\n-----END CERTIFICATE-----\n"] + s.date = "2018-01-17" s.description = "A full featured BSON specification implementation, in Ruby" s.email = ["mongodb-dev@googlegroups.com"] s.extensions = ["ext/bson/extconf.rb"] Binary files /tmp/tmp0qGYh1/do3VmnwWjX/ruby-bson-4.2.1/checksums.yaml.gz.sig and /tmp/tmp0qGYh1/VLLPlgGW7b/ruby-bson-4.3.0/checksums.yaml.gz.sig differ Binary files /tmp/tmp0qGYh1/do3VmnwWjX/ruby-bson-4.2.1/data.tar.gz.sig and /tmp/tmp0qGYh1/VLLPlgGW7b/ruby-bson-4.3.0/data.tar.gz.sig differ diff -Nru ruby-bson-4.2.1/debian/changelog ruby-bson-4.3.0/debian/changelog --- ruby-bson-4.2.1/debian/changelog 2018-03-01 12:30:12.000000000 +0000 +++ ruby-bson-4.3.0/debian/changelog 2018-03-29 21:29:43.000000000 +0000 @@ -1,14 +1,19 @@ -ruby-bson (4.2.1-1build2) bionic; urgency=high +ruby-bson (4.3.0-1) unstable; urgency=medium - * No change rebuild against ruby-defaults without ruby2.3. + * New upstream version 4.3.0 + * Drop d/patches + * Bump Standards-Version to 4.1.3; no changes needed + * Switch Vcs-* URLs to salsa.d.o + * d/copyright: adjust years + * d/control: switch Homepage to https:// + * d/control: convert Priority: extra to optional + * Bump dh compat to 11; no changes needed + * d/watch: switch URL to Github and handle betas + * Add d/ruby-tests.rake + + B-D on ruby-rspec + * Enable autopkgtests using gem2deb-test-runner - -- Dimitri John Ledkov Thu, 01 Mar 2018 12:30:12 +0000 - -ruby-bson (4.2.1-1build1) bionic; urgency=medium - - * No-change rebuild for ruby2.5 update. - - -- Matthias Klose Thu, 01 Feb 2018 19:13:58 +0000 + -- Apollon Oikonomopoulos Fri, 30 Mar 2018 00:29:43 +0300 ruby-bson (4.2.1-1) unstable; urgency=medium diff -Nru ruby-bson-4.2.1/debian/compat ruby-bson-4.3.0/debian/compat --- ruby-bson-4.2.1/debian/compat 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/compat 2018-03-29 21:09:13.000000000 +0000 @@ -1 +1 @@ -9 +11 diff -Nru ruby-bson-4.2.1/debian/control ruby-bson-4.3.0/debian/control --- ruby-bson-4.2.1/debian/control 2018-03-01 12:30:12.000000000 +0000 +++ ruby-bson-4.3.0/debian/control 2018-03-29 21:29:39.000000000 +0000 @@ -1,18 +1,18 @@ Source: ruby-bson Section: ruby Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian Ruby Extras Maintainers +Maintainer: Debian Ruby Extras Maintainers Uploaders: Cédric Boutillier , Prach Pongpanich , Apollon Oikonomopoulos -Build-Depends: debhelper (>= 9~), +Build-Depends: debhelper (>= 11~), gem2deb, - ruby-json -Standards-Version: 3.9.8 -Vcs-Git: https://anonscm.debian.org/git/pkg-ruby-extras/ruby-bson.git -Vcs-Browser: https://anonscm.debian.org/cgit/pkg-ruby-extras/ruby-bson.git -Homepage: http://www.mongodb.org + ruby-json, + ruby-rspec +Standards-Version: 4.1.3 +Vcs-Git: https://salsa.debian.org/ruby-team/ruby-bson.git +Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-bson +Homepage: https://www.mongodb.org XS-Ruby-Versions: all Package: ruby-bson @@ -33,7 +33,6 @@ Package: ruby-bson-ext Architecture: all Section: oldlibs -Priority: extra Depends: ruby-bson (>= 4.2.0~), ${misc:Depends} Description: transitional dummy package This is a transitional dummy package. It can safely be removed. diff -Nru ruby-bson-4.2.1/debian/copyright ruby-bson-4.3.0/debian/copyright --- ruby-bson-4.2.1/debian/copyright 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/copyright 2018-03-29 21:04:52.000000000 +0000 @@ -3,11 +3,12 @@ Source: https://github.com/mongodb/mongo-ruby-driver Files: * -Copyright: 2008-2013 MongoDB, Inc. +Copyright: 2008-2016 MongoDB, Inc. License: Apache-2.0 Files: debian/* Copyright: 2013-2014 Cédric Boutillier + 2016-2018 Apollon Oikonomopoulos License: Apache-2.0 License: Apache-2.0 diff -Nru ruby-bson-4.2.1/debian/patches/add_require_rbconfig.patch ruby-bson-4.3.0/debian/patches/add_require_rbconfig.patch --- ruby-bson-4.2.1/debian/patches/add_require_rbconfig.patch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/patches/add_require_rbconfig.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -Description: add missing requirement on rbconfig -Author: Cédric Boutillier -Bug: https://jira.mongodb.org/browse/RUBY-570 -Last-Updated: 2013-03-20 - ---- a/test/bson/bson_test.rb -+++ b/test/bson/bson_test.rb -@@ -1,6 +1,7 @@ - # encoding:utf-8 - require 'test_helper' - require 'set' -+require 'rbconfig' - - if RUBY_VERSION < '1.9' - silently do diff -Nru ruby-bson-4.2.1/debian/patches/add_test_helper.patch ruby-bson-4.3.0/debian/patches/add_test_helper.patch --- ruby-bson-4.2.1/debian/patches/add_test_helper.patch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/patches/add_test_helper.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,212 +0,0 @@ -Description: add full test_helper from MondoDB github repo -Author: Cédric Boutillier -Bug: https://jira.mongodb.org/browse/RUBY-570 -Last-Updated: 2013-03-20 - ---- /dev/null -+++ b/test/test_helper.rb -@@ -0,0 +1,204 @@ -+require 'rubygems' -+# SimpleCov must load before our code - A coverage report summary line will print after each test suite -+if RUBY_VERSION >= '1.9.0' && RUBY_ENGINE == 'ruby' -+ if ENV.key?('COVERAGE') -+ require 'simplecov' -+ SimpleCov.start do -+ add_group "Mongo", 'lib/mongo' -+ add_group "BSON", 'lib/bson' -+ add_filter "/test/" -+ merge_timeout 3600 -+ command_name ENV['SIMPLECOV_COMMAND_NAME'] if ENV.has_key?('SIMPLECOV_COMMAND_NAME') -+ end -+ end -+end -+gem 'test-unit' # Do NOT remove this line - gem version is needed for Test::Unit::TestCase.shutdown -+require 'test/unit' -+require 'tools/mongo_config' -+ -+class Test::Unit::TestCase -+ -+ TEST_DATA = File.join(File.dirname(__FILE__), 'data') -+ -+ def ensure_cluster(kind=nil, opts={}) -+ @@cluster ||= nil -+ -+ unless @@cluster -+ if kind == :rs -+ cluster_opts = Mongo::Config::DEFAULT_REPLICA_SET.dup -+ else -+ cluster_opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE.dup -+ end -+ -+ cluster_opts.merge!(opts) -+ -+ dbpath = ENV['DBPATH'] || 'data' -+ cluster_opts.merge!(:dbpath => dbpath) -+ -+ #debug 1, opts -+ config = Mongo::Config.cluster(cluster_opts) -+ #debug 1, config -+ @@cluster = Mongo::Config::ClusterManager.new(config) -+ -+ Test::Unit::TestCase.class_eval do -+ @@force_shutdown = false -+ -+ def self.shutdown -+ if @@force_shutdown || /rake_test_loader/ !~ $0 -+ @@cluster.stop -+ @@cluster.clobber -+ end -+ end -+ end -+ end -+ -+ @@cluster.start -+ instance_variable_set("@#{kind}", @@cluster) -+ end -+ -+ # Generic code for rescuing connection failures and retrying operations. -+ # This could be combined with some timeout functionality. -+ def rescue_connection_failure(max_retries=30) -+ retries = 0 -+ begin -+ yield -+ rescue Mongo::ConnectionFailure => ex -+ #puts "Rescue attempt #{retries}: from #{ex}" -+ retries += 1 -+ raise ex if retries > max_retries -+ sleep(2) -+ retry -+ end -+ end -+end -+ -+def silently -+ warn_level = $VERBOSE -+ $VERBOSE = nil -+ begin -+ result = yield -+ ensure -+ $VERBOSE = warn_level -+ end -+ result -+end -+ -+begin -+ silently { require 'shoulda' } -+ silently { require 'mocha/setup' } -+rescue LoadError -+ puts < e -+ if klass.to_s != e.class.to_s -+ flunk "Expected exception class #{klass} but got #{e.class}.\n #{e.backtrace}" -+ end -+ -+ if message && !e.message.include?(message) -+ p e.backtrace -+ flunk "#{e.message} does not include #{message}.\n#{e.backtrace}" -+ end -+ else -+ flunk "Expected assertion #{klass} but none was raised." -+ end -+ end -+end diff -Nru ruby-bson-4.2.1/debian/patches/add_to_bson_code.patch ruby-bson-4.3.0/debian/patches/add_to_bson_code.patch --- ruby-bson-4.2.1/debian/patches/add_to_bson_code.patch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/patches/add_to_bson_code.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Description: add code for to_bson_code for Strings, needed for the tests -Author: Cédric Boutillier -Bug: https://jira.mongodb.org/browse/RUBY-570 -Last-Update: 2013-03-20 - ---- a/test/bson/bson_test.rb -+++ b/test/bson/bson_test.rb -@@ -761,3 +761,11 @@ - end - end - end -+ -+class String -+ def to_bson_code -+ BSON::Code.new(self) -+ end -+end -+ -+ diff -Nru ruby-bson-4.2.1/debian/patches/change_require_activesupport.patch ruby-bson-4.3.0/debian/patches/change_require_activesupport.patch --- ruby-bson-4.2.1/debian/patches/change_require_activesupport.patch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/patches/change_require_activesupport.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -Description: require all active_support to ensure all needed classes/modules - are defined -Author: Cédric Boutillier -Bug: https://jira.mongodb.org/browse/RUBY-570 -Last-Update: 2013-03-20 - ---- a/test/bson/bson_test.rb -+++ b/test/bson/bson_test.rb -@@ -29,7 +29,7 @@ - end - - begin -- require 'active_support/multibyte/chars' -+ require 'active_support' - rescue LoadError - warn 'Mocking ActiveSupport::Multibyte::Chars' - module ActiveSupport diff -Nru ruby-bson-4.2.1/debian/patches/clean_test_helper.patch ruby-bson-4.3.0/debian/patches/clean_test_helper.patch --- ruby-bson-4.2.1/debian/patches/clean_test_helper.patch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/patches/clean_test_helper.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -Description: clean test_helper.rb from stuff not needed for ruby-bson -Author: Cédric Boutillier -Bug: https://jira.mongodb.org/browse/RUBY-570 -Last-Update: 2013-03-20 - ---- a/test/test_helper.rb -+++ b/test/test_helper.rb -@@ -1,77 +1,63 @@ --require 'rubygems' --# SimpleCov must load before our code - A coverage report summary line will print after each test suite --if RUBY_VERSION >= '1.9.0' && RUBY_ENGINE == 'ruby' -- if ENV.key?('COVERAGE') -- require 'simplecov' -- SimpleCov.start do -- add_group "Mongo", 'lib/mongo' -- add_group "BSON", 'lib/bson' -- add_filter "/test/" -- merge_timeout 3600 -- command_name ENV['SIMPLECOV_COMMAND_NAME'] if ENV.has_key?('SIMPLECOV_COMMAND_NAME') -- end -- end --end --gem 'test-unit' # Do NOT remove this line - gem version is needed for Test::Unit::TestCase.shutdown - require 'test/unit' --require 'tools/mongo_config' -- --class Test::Unit::TestCase -- -- TEST_DATA = File.join(File.dirname(__FILE__), 'data') -- -- def ensure_cluster(kind=nil, opts={}) -- @@cluster ||= nil -- -- unless @@cluster -- if kind == :rs -- cluster_opts = Mongo::Config::DEFAULT_REPLICA_SET.dup -- else -- cluster_opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE.dup -- end -- -- cluster_opts.merge!(opts) -- -- dbpath = ENV['DBPATH'] || 'data' -- cluster_opts.merge!(:dbpath => dbpath) -- -- #debug 1, opts -- config = Mongo::Config.cluster(cluster_opts) -- #debug 1, config -- @@cluster = Mongo::Config::ClusterManager.new(config) -- -- Test::Unit::TestCase.class_eval do -- @@force_shutdown = false -- -- def self.shutdown -- if @@force_shutdown || /rake_test_loader/ !~ $0 -- @@cluster.stop -- @@cluster.clobber -- end -- end -- end -- end -- -- @@cluster.start -- instance_variable_set("@#{kind}", @@cluster) -- end -- -- # Generic code for rescuing connection failures and retrying operations. -- # This could be combined with some timeout functionality. -- def rescue_connection_failure(max_retries=30) -- retries = 0 -- begin -- yield -- rescue Mongo::ConnectionFailure => ex -- #puts "Rescue attempt #{retries}: from #{ex}" -- retries += 1 -- raise ex if retries > max_retries -- sleep(2) -- retry -- end -- end --end -- -+require 'bson' -+#require 'tools/mongo_config' -+# -+#class Test::Unit::TestCase -+# -+# TEST_DATA = File.join(File.dirname(__FILE__), 'data') -+# -+# def ensure_cluster(kind=nil, opts={}) -+# @@cluster ||= nil -+# -+# unless @@cluster -+# if kind == :rs -+# cluster_opts = Mongo::Config::DEFAULT_REPLICA_SET.dup -+# else -+# cluster_opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE.dup -+# end -+# -+# cluster_opts.merge!(opts) -+# -+# dbpath = ENV['DBPATH'] || 'data' -+# cluster_opts.merge!(:dbpath => dbpath) -+# -+# #debug 1, opts -+# config = Mongo::Config.cluster(cluster_opts) -+# #debug 1, config -+# @@cluster = Mongo::Config::ClusterManager.new(config) -+# -+# Test::Unit::TestCase.class_eval do -+# @@force_shutdown = false -+# -+# def self.shutdown -+# if @@force_shutdown || /rake_test_loader/ !~ $0 -+# @@cluster.stop -+# @@cluster.clobber -+# end -+# end -+# end -+# end -+# -+# @@cluster.start -+# instance_variable_set("@#{kind}", @@cluster) -+# end -+# -+# # Generic code for rescuing connection failures and retrying operations. -+# # This could be combined with some timeout functionality. -+# def rescue_connection_failure(max_retries=30) -+# retries = 0 -+# begin -+# yield -+# rescue Mongo::ConnectionFailure => ex -+# #puts "Rescue attempt #{retries}: from #{ex}" -+# retries += 1 -+# raise ex if retries > max_retries -+# sleep(2) -+# retry -+# end -+# end -+#end -+# - def silently - warn_level = $VERBOSE - $VERBOSE = nil -@@ -83,35 +69,23 @@ - result - end - --begin -- silently { require 'shoulda' } -- silently { require 'mocha/setup' } --rescue LoadError -- puts <&1 +Depends: @, gem2deb-test-runner, ruby-rspec, rake, ruby-json diff -Nru ruby-bson-4.2.1/debian/watch ruby-bson-4.3.0/debian/watch --- ruby-bson-4.2.1/debian/watch 2016-12-19 15:06:38.000000000 +0000 +++ ruby-bson-4.3.0/debian/watch 2018-03-29 21:23:33.000000000 +0000 @@ -1,2 +1,3 @@ version=3 -http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/bson .*/bson-(.*).tar.gz +opts="uversionmangle=s/\.(alpha|beta|rc)/~$1/" \ + https://github.com/mongodb/bson-ruby/releases .*/archive/v(.*).tar.gz diff -Nru ruby-bson-4.2.1/ext/bson/bson_native.c ruby-bson-4.3.0/ext/bson/bson_native.c --- ruby-bson-4.2.1/ext/bson/bson_native.c 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/ext/bson/bson_native.c 2018-02-06 17:17:50.000000000 +0000 @@ -26,6 +26,14 @@ #define HOST_NAME_HASH_MAX 256 #endif +#define BSON_TYPE_DOUBLE 1 +#define BSON_TYPE_STRING 2 +#define BSON_TYPE_OBJECT 3 +#define BSON_TYPE_ARRAY 4 +#define BSON_TYPE_INT32 16 +#define BSON_TYPE_INT64 18 +#define BSON_TYPE_BOOLEAN 8 + typedef struct { size_t size; size_t write_position; @@ -61,6 +69,8 @@ static VALUE rb_bson_byte_buffer_get_int32(VALUE self); static VALUE rb_bson_byte_buffer_get_int64(VALUE self); static VALUE rb_bson_byte_buffer_get_string(VALUE self); +static VALUE rb_bson_byte_buffer_get_hash(VALUE self); +static VALUE rb_bson_byte_buffer_get_array(VALUE self); static VALUE rb_bson_byte_buffer_put_byte(VALUE self, VALUE byte); static VALUE rb_bson_byte_buffer_put_bytes(VALUE self, VALUE bytes); static VALUE rb_bson_byte_buffer_put_cstring(VALUE self, VALUE string); @@ -69,6 +79,8 @@ static VALUE rb_bson_byte_buffer_put_int32(VALUE self, VALUE i); static VALUE rb_bson_byte_buffer_put_int64(VALUE self, VALUE i); static VALUE rb_bson_byte_buffer_put_string(VALUE self, VALUE string); +static VALUE rb_bson_byte_buffer_put_hash(VALUE self, VALUE hash, VALUE validating_keys); +static VALUE rb_bson_byte_buffer_put_array(VALUE self, VALUE array, VALUE validating_keys); static VALUE rb_bson_byte_buffer_read_position(VALUE self); static VALUE rb_bson_byte_buffer_replace_int32(VALUE self, VALUE index, VALUE i); static VALUE rb_bson_byte_buffer_rewind(VALUE self); @@ -87,6 +99,28 @@ { NULL, rb_bson_byte_buffer_free, rb_bson_byte_buffer_memsize } }; +static uint8_t pvt_get_type_byte(byte_buffer_t *b); +static VALUE pvt_get_int32(byte_buffer_t *b); +static VALUE pvt_get_int64(byte_buffer_t *b); +static VALUE pvt_get_double(byte_buffer_t *b); +static VALUE pvt_get_string(byte_buffer_t *b); +static VALUE pvt_get_boolean(byte_buffer_t *b); + + +static VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type); +static void pvt_replace_int32(byte_buffer_t *b, int32_t position, int32_t newval); +static void pvt_skip_cstring(byte_buffer_t *b); +static void pvt_validate_length(byte_buffer_t *b); + + +static void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validating_keys); +static void pvt_put_byte(byte_buffer_t *b, const char byte); +static void pvt_put_int32(byte_buffer_t *b, const int32_t i32); +static void pvt_put_int64(byte_buffer_t *b, const int64_t i); +static void pvt_put_double(byte_buffer_t *b, double f); +static void pvt_put_cstring(byte_buffer_t *b, VALUE string); +static void pvt_put_bson_key(byte_buffer_t *b, VALUE string, VALUE validating_keys); + /** * Holds the machine id hash for object id generation. */ @@ -97,6 +131,10 @@ */ static uint32_t rb_bson_object_id_counter; + +static VALUE rb_bson_registry; + +static VALUE rb_bson_illegal_key; /** * Initialize the bson_native extension. */ @@ -111,6 +149,8 @@ VALUE rb_digest_class = rb_const_get(rb_cObject, rb_intern("Digest")); VALUE rb_md5_class = rb_const_get(rb_digest_class, rb_intern("MD5")); + rb_bson_illegal_key = rb_const_get(rb_const_get(rb_bson_module, rb_intern("String")),rb_intern("IllegalKey")); + rb_define_alloc_func(rb_byte_buffer_class, rb_bson_byte_buffer_allocate); rb_define_method(rb_byte_buffer_class, "initialize", rb_bson_byte_buffer_initialize, -1); rb_define_method(rb_byte_buffer_class, "length", rb_bson_byte_buffer_length, 0); @@ -119,6 +159,9 @@ rb_define_method(rb_byte_buffer_class, "get_cstring", rb_bson_byte_buffer_get_cstring, 0); rb_define_method(rb_byte_buffer_class, "get_decimal128_bytes", rb_bson_byte_buffer_get_decimal128_bytes, 0); rb_define_method(rb_byte_buffer_class, "get_double", rb_bson_byte_buffer_get_double, 0); + rb_define_method(rb_byte_buffer_class, "get_hash", rb_bson_byte_buffer_get_hash, 0); + rb_define_method(rb_byte_buffer_class, "get_array", rb_bson_byte_buffer_get_array, 0); + rb_define_method(rb_byte_buffer_class, "get_int32", rb_bson_byte_buffer_get_int32, 0); rb_define_method(rb_byte_buffer_class, "get_int64", rb_bson_byte_buffer_get_int64, 0); rb_define_method(rb_byte_buffer_class, "get_string", rb_bson_byte_buffer_get_string, 0); @@ -137,6 +180,9 @@ rb_define_method(rb_byte_buffer_class, "to_s", rb_bson_byte_buffer_to_s, 0); rb_define_method(rb_bson_object_id_generator_class, "next_object_id", rb_bson_object_id_generator_next, -1); + rb_define_method(rb_byte_buffer_class, "put_hash", rb_bson_byte_buffer_put_hash, 2); + rb_define_method(rb_byte_buffer_class, "put_array", rb_bson_byte_buffer_put_array, 2); + // Get the object id machine id and hash it. rb_require("digest/md5"); gethostname(rb_bson_machine_id, sizeof(rb_bson_machine_id)); @@ -145,6 +191,8 @@ // Set the object id counter to a random number rb_bson_object_id_counter = FIX2INT(rb_funcall(rb_mKernel, rb_intern("rand"), 1, INT2FIX(0x1000000))); + + rb_bson_registry = rb_const_get(rb_bson_module, rb_intern("Registry")); } void rb_bson_generate_machine_id(VALUE rb_md5_class, char *rb_bson_machine_id) @@ -180,6 +228,285 @@ return self; } +static int fits_int32(int64_t i64){ + return i64 >= INT32_MIN && i64 <= INT32_MAX; +} + +/* write the byte denoting the BSON type for the passed object*/ +void pvt_put_type_byte(byte_buffer_t *b, VALUE val){ + switch(TYPE(val)){ + case T_BIGNUM: + case T_FIXNUM: + if(fits_int32(NUM2LL(val))){ + pvt_put_byte(b, BSON_TYPE_INT32); + }else{ + pvt_put_byte(b, BSON_TYPE_INT64); + } + break; + case T_STRING: + pvt_put_byte(b, BSON_TYPE_STRING); + break; + case T_ARRAY: + pvt_put_byte(b, BSON_TYPE_ARRAY); + break; + case T_TRUE: + case T_FALSE: + pvt_put_byte(b, BSON_TYPE_BOOLEAN); + break; + case T_HASH: + pvt_put_byte(b, BSON_TYPE_OBJECT); + break; + case T_FLOAT: + pvt_put_byte(b, BSON_TYPE_DOUBLE); + break; + default:{ + VALUE type = rb_funcall(val, rb_intern("bson_type"),0); + pvt_put_byte(b, *RSTRING_PTR(type)); + break; + } + } +} + +void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validating_keys){ + switch(TYPE(val)){ + case T_BIGNUM: + case T_FIXNUM:{ + int64_t i64= NUM2LL(val); + if(fits_int32(i64)){ + pvt_put_int32(b, (int32_t)i64); + }else{ + pvt_put_int64(b, i64); + } + break; + } + case T_FLOAT: + pvt_put_double(b, NUM2DBL(val)); + break; + case T_ARRAY: + rb_bson_byte_buffer_put_array(rb_buffer, val, validating_keys); + break; + case T_TRUE: + pvt_put_byte(b, 1); + break; + case T_FALSE: + pvt_put_byte(b, 0); + break; + case T_HASH: + rb_bson_byte_buffer_put_hash(rb_buffer, val, validating_keys); + break; + default:{ + rb_funcall(val, rb_intern("to_bson"), 2, rb_buffer, validating_keys); + break; + } + } +} + +typedef struct{ + byte_buffer_t *b; + VALUE buffer; + VALUE validating_keys; +} put_hash_context; + +static int put_hash_callback(VALUE key, VALUE val, VALUE context){ + VALUE buffer = ((put_hash_context*)context)->buffer; + VALUE validating_keys = ((put_hash_context*)context)->validating_keys; + byte_buffer_t *b = ((put_hash_context*)context)->b; + + pvt_put_type_byte(b, val); + + switch(TYPE(key)){ + case T_STRING: + pvt_put_bson_key(b, key, validating_keys); + break; + case T_SYMBOL: + pvt_put_bson_key(b, rb_sym_to_s(key), validating_keys); + break; + default: + rb_bson_byte_buffer_put_cstring(buffer, rb_funcall(key, rb_intern("to_bson_key"), 1, validating_keys)); + } + + pvt_put_field(b, buffer, val, validating_keys); + return ST_CONTINUE; +} + +/** + * serializes a hash into the byte buffer + */ +VALUE rb_bson_byte_buffer_put_hash(VALUE self, VALUE hash, VALUE validating_keys){ + byte_buffer_t *b = NULL; + put_hash_context context = {0}; + size_t position = 0; + size_t new_position = 0; + int32_t new_length = 0; + + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + Check_Type(hash, T_HASH); + + position = READ_SIZE(b); + + /* insert length placeholder */ + pvt_put_int32(b, 0); + context.buffer = self; + context.validating_keys = validating_keys; + context.b = b; + + rb_hash_foreach(hash, put_hash_callback, (VALUE)&context); + pvt_put_byte(b, 0); + + /* update length placeholder with actual value */ + new_position = READ_SIZE(b); + new_length = new_position - position; + pvt_replace_int32(b, position, new_length); + + return self; +} + +static const char *index_strings[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", + "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", + "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", + "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", + "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", + "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", + "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", + "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", + "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", "120", + "121", "122", "123", "124", "125", "126", "127", "128", "129", "130", "131", + "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", + "143", "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", + "154", "155", "156", "157", "158", "159", "160", "161", "162", "163", "164", + "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", + "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", + "187", "188", "189", "190", "191", "192", "193", "194", "195", "196", "197", + "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", "208", + "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", "230", + "231", "232", "233", "234", "235", "236", "237", "238", "239", "240", "241", + "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", + "253", "254", "255", "256", "257", "258", "259", "260", "261", "262", "263", + "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", + "275", "276", "277", "278", "279", "280", "281", "282", "283", "284", "285", + "286", "287", "288", "289", "290", "291", "292", "293", "294", "295", "296", + "297", "298", "299", "300", "301", "302", "303", "304", "305", "306", "307", + "308", "309", "310", "311", "312", "313", "314", "315", "316", "317", "318", + "319", "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", + "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", "340", + "341", "342", "343", "344", "345", "346", "347", "348", "349", "350", "351", + "352", "353", "354", "355", "356", "357", "358", "359", "360", "361", "362", + "363", "364", "365", "366", "367", "368", "369", "370", "371", "372", "373", + "374", "375", "376", "377", "378", "379", "380", "381", "382", "383", "384", + "385", "386", "387", "388", "389", "390", "391", "392", "393", "394", "395", + "396", "397", "398", "399", "400", "401", "402", "403", "404", "405", "406", + "407", "408", "409", "410", "411", "412", "413", "414", "415", "416", "417", + "418", "419", "420", "421", "422", "423", "424", "425", "426", "427", "428", + "429", "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", + "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", "450", + "451", "452", "453", "454", "455", "456", "457", "458", "459", "460", "461", + "462", "463", "464", "465", "466", "467", "468", "469", "470", "471", "472", + "473", "474", "475", "476", "477", "478", "479", "480", "481", "482", "483", + "484", "485", "486", "487", "488", "489", "490", "491", "492", "493", "494", + "495", "496", "497", "498", "499", "500", "501", "502", "503", "504", "505", + "506", "507", "508", "509", "510", "511", "512", "513", "514", "515", "516", + "517", "518", "519", "520", "521", "522", "523", "524", "525", "526", "527", + "528", "529", "530", "531", "532", "533", "534", "535", "536", "537", "538", + "539", "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", + "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", "560", + "561", "562", "563", "564", "565", "566", "567", "568", "569", "570", "571", + "572", "573", "574", "575", "576", "577", "578", "579", "580", "581", "582", + "583", "584", "585", "586", "587", "588", "589", "590", "591", "592", "593", + "594", "595", "596", "597", "598", "599", "600", "601", "602", "603", "604", + "605", "606", "607", "608", "609", "610", "611", "612", "613", "614", "615", + "616", "617", "618", "619", "620", "621", "622", "623", "624", "625", "626", + "627", "628", "629", "630", "631", "632", "633", "634", "635", "636", "637", + "638", "639", "640", "641", "642", "643", "644", "645", "646", "647", "648", + "649", "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", + "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", "670", + "671", "672", "673", "674", "675", "676", "677", "678", "679", "680", "681", + "682", "683", "684", "685", "686", "687", "688", "689", "690", "691", "692", + "693", "694", "695", "696", "697", "698", "699", "700", "701", "702", "703", + "704", "705", "706", "707", "708", "709", "710", "711", "712", "713", "714", + "715", "716", "717", "718", "719", "720", "721", "722", "723", "724", "725", + "726", "727", "728", "729", "730", "731", "732", "733", "734", "735", "736", + "737", "738", "739", "740", "741", "742", "743", "744", "745", "746", "747", + "748", "749", "750", "751", "752", "753", "754", "755", "756", "757", "758", + "759", "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", + "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", "780", + "781", "782", "783", "784", "785", "786", "787", "788", "789", "790", "791", + "792", "793", "794", "795", "796", "797", "798", "799", "800", "801", "802", + "803", "804", "805", "806", "807", "808", "809", "810", "811", "812", "813", + "814", "815", "816", "817", "818", "819", "820", "821", "822", "823", "824", + "825", "826", "827", "828", "829", "830", "831", "832", "833", "834", "835", + "836", "837", "838", "839", "840", "841", "842", "843", "844", "845", "846", + "847", "848", "849", "850", "851", "852", "853", "854", "855", "856", "857", + "858", "859", "860", "861", "862", "863", "864", "865", "866", "867", "868", + "869", "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", + "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", "890", + "891", "892", "893", "894", "895", "896", "897", "898", "899", "900", "901", + "902", "903", "904", "905", "906", "907", "908", "909", "910", "911", "912", + "913", "914", "915", "916", "917", "918", "919", "920", "921", "922", "923", + "924", "925", "926", "927", "928", "929", "930", "931", "932", "933", "934", + "935", "936", "937", "938", "939", "940", "941", "942", "943", "944", "945", + "946", "947", "948", "949", "950", "951", "952", "953", "954", "955", "956", + "957", "958", "959", "960", "961", "962", "963", "964", "965", "966", "967", + "968", "969", "970", "971", "972", "973", "974", "975", "976", "977", "978", + "979", "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", + "990", "991", "992", "993", "994", "995", "996", "997", "998", "999"}; + +/** + * Writes an array index to the byte buffer. + */ +void pvt_put_array_index(byte_buffer_t *b, int32_t index) +{ + char buffer[16]; + const char *c_str = NULL; + + if (index < 1000) { + c_str = index_strings[index]; + } else { + c_str = buffer; + snprintf(buffer, sizeof(buffer), "%d", index); + } + size_t length = strlen(c_str) + 1; + ENSURE_BSON_WRITE(b, length); + memcpy(WRITE_PTR(b), c_str, length); + b->write_position += length; +} + +/** + * serializes an array into the byte buffer + */ +VALUE rb_bson_byte_buffer_put_array(VALUE self, VALUE array, VALUE validating_keys){ + byte_buffer_t *b = NULL; + size_t new_position = 0; + int32_t new_length = 0; + size_t position = 0; + VALUE *array_element = NULL; + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + Check_Type(array, T_ARRAY); + + position = READ_SIZE(b); + /* insert length placeholder */ + pvt_put_int32(b, 0); + + array_element = RARRAY_PTR(array); + + for(int32_t index=0; index < RARRAY_LEN(array); index++, array_element++){ + pvt_put_type_byte(b, *array_element); + pvt_put_array_index(b,index); + pvt_put_field(b, self, *array_element, validating_keys); + } + pvt_put_byte(b, 0); + + /* update length placeholder */ + new_position = READ_SIZE(b); + new_length = new_position - position; + pvt_replace_int32(b, position, new_length); + + return self; +} + /** * Get the length of the buffer. */ @@ -205,6 +532,13 @@ return byte; } +uint8_t pvt_get_type_byte(byte_buffer_t *b){ + ENSURE_BSON_READ(b, 1); + int8_t byte = *READ_PTR(b); + b->read_position += 1; + return (uint8_t)byte; +} + /** * Get bytes from the buffer. */ @@ -221,6 +555,14 @@ return bytes; } +VALUE pvt_get_boolean(byte_buffer_t *b){ + VALUE result = Qnil; + ENSURE_BSON_READ(b, 1); + result = *READ_PTR(b) == 1 ? Qtrue: Qfalse; + b->read_position += 1; + return result; +} + /** * Get a cstring from the buffer. */ @@ -239,6 +581,17 @@ } /** + * Reads but does not return a cstring from the buffer. + */ +void pvt_skip_cstring(byte_buffer_t *b) +{ + int length; + length = (int)strlen(READ_PTR(b)); + ENSURE_BSON_READ(b, length); + b->read_position += length + 1; +} + +/** * Get the 16 bytes representing the decimal128 from the buffer. */ VALUE rb_bson_byte_buffer_get_decimal128_bytes(VALUE self) @@ -259,9 +612,15 @@ VALUE rb_bson_byte_buffer_get_double(VALUE self) { byte_buffer_t *b; - double d; TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + return pvt_get_double(b); +} + +VALUE pvt_get_double(byte_buffer_t *b) +{ + double d; + ENSURE_BSON_READ(b, 8); memcpy(&d, READ_PTR(b), 8); b->read_position += 8; @@ -274,9 +633,15 @@ VALUE rb_bson_byte_buffer_get_int32(VALUE self) { byte_buffer_t *b; - int32_t i32; TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + return pvt_get_int32(b); +} + +VALUE pvt_get_int32(byte_buffer_t *b) +{ + int32_t i32; + ENSURE_BSON_READ(b, 4); memcpy(&i32, READ_PTR(b), 4); b->read_position += 4; @@ -289,9 +654,14 @@ VALUE rb_bson_byte_buffer_get_int64(VALUE self) { byte_buffer_t *b; + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + return pvt_get_int64(b); +} + +VALUE pvt_get_int64(byte_buffer_t *b) +{ int64_t i64; - TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); ENSURE_BSON_READ(b, 8); memcpy(&i64, READ_PTR(b), 8); b->read_position += 8; @@ -304,11 +674,17 @@ VALUE rb_bson_byte_buffer_get_string(VALUE self) { byte_buffer_t *b; + + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + return pvt_get_string(b); +} + +VALUE pvt_get_string(byte_buffer_t *b) +{ int32_t length; int32_t length_le; VALUE string; - TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); ENSURE_BSON_READ(b, 4); memcpy(&length, READ_PTR(b), 4); length_le = BSON_UINT32_FROM_LE(length); @@ -319,6 +695,64 @@ return string; } + +VALUE rb_bson_byte_buffer_get_hash(VALUE self){ + VALUE doc = Qnil; + byte_buffer_t *b=NULL; + uint8_t type; + VALUE cDocument = rb_const_get(rb_const_get(rb_cObject, rb_intern("BSON")), rb_intern("Document")); + + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + + pvt_validate_length(b); + + doc = rb_funcall(cDocument, rb_intern("allocate"),0); + + while((type = pvt_get_type_byte(b)) != 0){ + VALUE field = rb_bson_byte_buffer_get_cstring(self); + rb_hash_aset(doc, field, pvt_read_field(b, self, type)); + } + return doc; +} + +VALUE rb_bson_byte_buffer_get_array(VALUE self){ + byte_buffer_t *b; + VALUE array = Qnil; + uint8_t type; + + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + + pvt_validate_length(b); + + array = rb_ary_new(); + while((type = pvt_get_type_byte(b)) != 0){ + pvt_skip_cstring(b); + rb_ary_push(array, pvt_read_field(b, self, type)); + } + return array; +} + +/** + * Read a single field from a hash or array + */ +VALUE pvt_read_field(byte_buffer_t *b, VALUE rb_buffer, uint8_t type){ + switch(type) { + case BSON_TYPE_INT32: return pvt_get_int32(b); + case BSON_TYPE_INT64: return pvt_get_int64(b); + case BSON_TYPE_DOUBLE: return pvt_get_double(b); + case BSON_TYPE_STRING: return pvt_get_string(b); + case BSON_TYPE_ARRAY: return rb_bson_byte_buffer_get_array(rb_buffer); + case BSON_TYPE_OBJECT: return rb_bson_byte_buffer_get_hash(rb_buffer); + case BSON_TYPE_BOOLEAN: return pvt_get_boolean(b); + default: + { + VALUE klass = rb_funcall(rb_bson_registry,rb_intern("get"),1, INT2FIX(type)); + VALUE value = rb_funcall(klass, rb_intern("from_bson"),1, rb_buffer); + return value; + } + } +} + /** * Writes a byte to the byte buffer. */ @@ -335,6 +769,13 @@ return self; } +void pvt_put_byte( byte_buffer_t *b, const char byte) +{ + ENSURE_BSON_WRITE(b, 1); + *WRITE_PTR(b) = byte; + b->write_position += 1; + +} /** * Writes bytes to the byte buffer. */ @@ -357,18 +798,37 @@ VALUE rb_bson_byte_buffer_put_cstring(VALUE self, VALUE string) { byte_buffer_t *b; + TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + pvt_put_cstring(b, string); + return self; +} + +void pvt_put_cstring(byte_buffer_t *b, VALUE string) +{ char *c_str = RSTRING_PTR(string); size_t length = RSTRING_LEN(string) + 1; if (!rb_bson_utf8_validate(c_str, length - 1, false)) { rb_raise(rb_eArgError, "String %s is not a valid UTF-8 CString.", c_str); } - - TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); ENSURE_BSON_WRITE(b, length); memcpy(WRITE_PTR(b), c_str, length); b->write_position += length; - return self; +} + +/** + * Write a hash key to the byte buffer, validating it if requested + */ +void pvt_put_bson_key(byte_buffer_t *b, VALUE string, VALUE validating_keys){ + if(RTEST(validating_keys)){ + char *c_str = RSTRING_PTR(string); + size_t length = RSTRING_LEN(string); + if(length > 0 && (c_str[0] == '$' || memchr(c_str, '.', length))){ + rb_exc_raise(rb_funcall(rb_bson_illegal_key, rb_intern("new"),1, string)); + } + } + + pvt_put_cstring(b, string); } /** @@ -398,13 +858,18 @@ VALUE rb_bson_byte_buffer_put_double(VALUE self, VALUE f) { byte_buffer_t *b; - const double d = BSON_DOUBLE_TO_LE(NUM2DBL(f)); TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + pvt_put_double(b,NUM2DBL(f)); + + return self; +} + +void pvt_put_double(byte_buffer_t *b, double f) +{ + const double d = BSON_DOUBLE_TO_LE(f); ENSURE_BSON_WRITE(b, 8); memcpy(WRITE_PTR(b), &d, 8); b->write_position += 8; - - return self; } /** @@ -413,14 +878,19 @@ VALUE rb_bson_byte_buffer_put_int32(VALUE self, VALUE i) { byte_buffer_t *b; - const int32_t i32 = BSON_UINT32_TO_LE(NUM2INT(i)); + const int32_t i32 = NUM2INT(i); TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + pvt_put_int32(b, i32); + return self; +} + +void pvt_put_int32(byte_buffer_t *b, const int32_t i) +{ + const int32_t i32 = BSON_UINT32_TO_LE(i); ENSURE_BSON_WRITE(b, 4); memcpy(WRITE_PTR(b), &i32, 4); b->write_position += 4; - - return self; } /** @@ -429,14 +899,49 @@ VALUE rb_bson_byte_buffer_put_int64(VALUE self, VALUE i) { byte_buffer_t *b; - const int64_t i64 = BSON_UINT64_TO_LE(NUM2LL(i)); + const int64_t i64 = NUM2LL(i); TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); + pvt_put_int64(b, i64); + + return self; +} + +void pvt_put_int64(byte_buffer_t *b, const int64_t i) +{ + const int64_t i64 = BSON_UINT64_TO_LE(i); + ENSURE_BSON_WRITE(b, 8); memcpy(WRITE_PTR(b), &i64, 8); b->write_position += 8; - return self; +} + +/** + * validate the buffer contains the amount of bytes the array / hash claimns + * and that it is null terminated + */ +void pvt_validate_length(byte_buffer_t *b) +{ + int32_t length; + + ENSURE_BSON_READ(b, 4); + memcpy(&length, READ_PTR(b), 4); + length = BSON_UINT32_TO_LE(length); + + /* minimum valid length is 4 (byte count) + 1 (terminating byte) */ + if(length >= 5){ + ENSURE_BSON_READ(b, length); + + /* The last byte should be a null byte: it should be at length - 1 */ + if( *(READ_PTR(b) + length - 1) != 0 ){ + rb_raise(rb_eRangeError, "Buffer should have contained null terminator at %zu but contained %d", b->read_position + (size_t)length, (int)*(READ_PTR(b) + length)); + } + b->read_position += 4; + } + else{ + rb_raise(rb_eRangeError, "Buffer contained invalid length %d at %zu", length, b->read_position); + } } /** @@ -481,16 +986,19 @@ VALUE rb_bson_byte_buffer_replace_int32(VALUE self, VALUE index, VALUE i) { byte_buffer_t *b; - const int32_t position = NUM2LONG(index); - const int32_t i32 = BSON_UINT32_TO_LE(NUM2LONG(i)); TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b); - - memcpy(READ_PTR(b) + position, &i32, 4); + pvt_replace_int32(b, NUM2LONG(index), NUM2LONG(i)); return self; } +void pvt_replace_int32(byte_buffer_t *b, int32_t position, int32_t newval) +{ + const int32_t i32 = BSON_UINT32_TO_LE(newval); + memcpy(READ_PTR(b) + position, &i32, 4); +} + /** * Reset the read position to the beginning of the byte buffer. */ diff -Nru ruby-bson-4.2.1/lib/bson/array.rb ruby-bson-4.3.0/lib/bson/array.rb --- ruby-bson-4.2.1/lib/bson/array.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/array.rb 2018-02-06 17:17:50.000000000 +0000 @@ -41,15 +41,19 @@ # # @since 2.0.0 def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?) - position = buffer.length - buffer.put_int32(0) - each_with_index do |value, index| - buffer.put_byte(value.bson_type) - buffer.put_cstring(index.to_s) - value.to_bson(buffer, validating_keys) + if buffer.respond_to?(:put_array) + buffer.put_array(self, validating_keys) + else + position = buffer.length + buffer.put_int32(0) + each_with_index do |value, index| + buffer.put_byte(value.bson_type) + buffer.put_cstring(index.to_s) + value.to_bson(buffer, validating_keys) + end + buffer.put_byte(NULL_BYTE) + buffer.replace_int32(position, buffer.length - position) end - buffer.put_byte(NULL_BYTE) - buffer.replace_int32(position, buffer.length - position) end # Convert the array to an object id. This will only work for arrays of size @@ -60,7 +64,7 @@ # # @note This is used for repairing legacy bson data. # - # @raise [ InvalidObjectId ] If the array is not 12 elements. + # @raise [ BSON::ObjectId::Invalid ] If the array is not 12 elements. # # @return [ String ] The raw object id bytes. # @@ -93,13 +97,17 @@ # # @since 2.0.0 def from_bson(buffer) - array = new - buffer.get_int32 # throw away the length - while (type = buffer.get_byte) != NULL_BYTE - buffer.get_cstring - array << BSON::Registry.get(type).from_bson(buffer) + if buffer.respond_to?(:get_array) + buffer.get_array + else + array = new + buffer.get_int32 # throw away the length + while (type = buffer.get_byte) != NULL_BYTE + buffer.get_cstring + array << BSON::Registry.get(type).from_bson(buffer) + end + array end - array end end diff -Nru ruby-bson-4.2.1/lib/bson/decimal128.rb ruby-bson-4.3.0/lib/bson/decimal128.rb --- ruby-bson-4.2.1/lib/bson/decimal128.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/decimal128.rb 2018-02-06 17:17:50.000000000 +0000 @@ -18,6 +18,7 @@ module BSON class Decimal128 + include JSON # A Decimal128 is type 0x13 in the BSON spec. # diff -Nru ruby-bson-4.2.1/lib/bson/document.rb ruby-bson-4.3.0/lib/bson/document.rb --- ruby-bson-4.2.1/lib/bson/document.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/document.rb 2018-02-06 17:17:50.000000000 +0000 @@ -172,6 +172,26 @@ alias :update :merge! + if instance_methods.include?(:dig) + # Retrieves the value object corresponding to the each key objects repeatedly. + # Will normalize symbol keys into strings. + # + # @example Get value from nested sub-documents, handling missing levels. + # document # => { :key1 => { "key2" => "value"}} + # document.dig(:key1, :key2) # => "value" + # document.dig("key1", "key2") # => "value" + # document.dig("foo", "key2") # => nil + # + # @param [ Array ] *keys Keys, which constitute a "path" to the nested value. + # + # @return [ Object, NilClass ] The requested value or nil. + # + # @since 3.0.0 + def dig(*keys) + super(*keys.map{|key| convert_key(key)}) + end + end + private def convert_key(key) diff -Nru ruby-bson-4.2.1/lib/bson/hash.rb ruby-bson-4.3.0/lib/bson/hash.rb --- ruby-bson-4.2.1/lib/bson/hash.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/hash.rb 2018-02-06 17:17:50.000000000 +0000 @@ -38,15 +38,19 @@ # # @since 2.0.0 def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?) - position = buffer.length - buffer.put_int32(0) - each do |field, value| - buffer.put_byte(value.bson_type) - buffer.put_cstring(field.to_bson_key(validating_keys)) - value.to_bson(buffer, validating_keys) + if buffer.respond_to?(:put_hash) + buffer.put_hash(self, validating_keys) + else + position = buffer.length + buffer.put_int32(0) + each do |field, value| + buffer.put_byte(value.bson_type) + buffer.put_cstring(field.to_bson_key(validating_keys)) + value.to_bson(buffer, validating_keys) + end + buffer.put_byte(NULL_BYTE) + buffer.replace_int32(position, buffer.length - position) end - buffer.put_byte(NULL_BYTE) - buffer.replace_int32(position, buffer.length - position) end # Converts the hash to a normalized value in a BSON document. @@ -73,13 +77,17 @@ # # @since 2.0.0 def from_bson(buffer) - hash = Document.allocate - buffer.get_int32 # Throw away the size. - while (type = buffer.get_byte) != NULL_BYTE - field = buffer.get_cstring - hash.store(field, BSON::Registry.get(type, field).from_bson(buffer)) + if buffer.respond_to?(:get_hash) + buffer.get_hash + else + hash = Document.allocate + buffer.get_int32 # Throw away the size. + while (type = buffer.get_byte) != NULL_BYTE + field = buffer.get_cstring + hash.store(field, BSON::Registry.get(type, field).from_bson(buffer)) + end + hash end - hash end end diff -Nru ruby-bson-4.2.1/lib/bson/object_id.rb ruby-bson-4.3.0/lib/bson/object_id.rb --- ruby-bson-4.2.1/lib/bson/object_id.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/object_id.rb 2018-02-06 17:17:50.000000000 +0000 @@ -193,6 +193,11 @@ private + def initialize_copy(other) + generate_data + other.instance_variable_set(:@raw_data, @raw_data) + end + def generate_data repair if defined?(@data) @raw_data ||= @@generator.next_object_id diff -Nru ruby-bson-4.2.1/lib/bson/registry.rb ruby-bson-4.3.0/lib/bson/registry.rb --- ruby-bson-4.2.1/lib/bson/registry.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/registry.rb 2018-02-06 17:17:50.000000000 +0000 @@ -40,7 +40,7 @@ # # @since 2.0.0 def get(byte, field = nil) - if type = MAPPINGS[byte] + if type = MAPPINGS[byte] || (byte.is_a?(String) && type = MAPPINGS[byte.ord]) type else handle_unsupported_type!(byte, field) @@ -59,7 +59,7 @@ # # @since 2.0.0 def register(byte, type) - MAPPINGS.store(byte, type) + MAPPINGS[byte.ord] = type define_type_reader(type) end diff -Nru ruby-bson-4.2.1/lib/bson/string.rb ruby-bson-4.3.0/lib/bson/string.rb --- ruby-bson-4.2.1/lib/bson/string.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/string.rb 2018-02-06 17:17:50.000000000 +0000 @@ -79,7 +79,7 @@ # # @note This is used for repairing legacy bson data. # - # @raise [ InvalidObjectId ] If the string is not 12 elements. + # @raise [ BSON::ObjectId::Invalid ] If the string is not 12 elements. # # @return [ String ] The raw object id bytes. # diff -Nru ruby-bson-4.2.1/lib/bson/symbol.rb ruby-bson-4.3.0/lib/bson/symbol.rb --- ruby-bson-4.2.1/lib/bson/symbol.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/symbol.rb 2018-02-06 17:17:50.000000000 +0000 @@ -105,7 +105,7 @@ # Register this type when the module is loaded. # # @since 2.0.0 - Registry::MAPPINGS.store(BSON_TYPE, ::Symbol) + Registry::MAPPINGS[BSON_TYPE.ord] = ::Symbol end # Enrich the core Symbol class with this module. diff -Nru ruby-bson-4.2.1/lib/bson/timestamp.rb ruby-bson-4.3.0/lib/bson/timestamp.rb --- ruby-bson-4.2.1/lib/bson/timestamp.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/timestamp.rb 2018-02-06 17:17:50.000000000 +0000 @@ -21,12 +21,18 @@ # @since 2.0.0 class Timestamp include JSON + include Comparable # A timestamp is type 0x11 in the BSON spec. # # @since 2.0.0 BSON_TYPE = 17.chr.force_encoding(BINARY).freeze + # Error message if an object other than a Timestamp is compared with this object. + # + # @since 4.3.0 + COMPARISON_ERROR_MESSAGE = 'comparison of %s with Timestamp failed'.freeze + # @!attribute seconds # @return [ Integer ] The number of seconds. # @since 2.0.0 @@ -52,6 +58,24 @@ seconds == other.seconds && increment == other.increment end + # Determine if this timestamp is greater or less than another object. + # + # @example Compare the timestamp. + # timestamp < other + # + # @param [ Object ] other The object to compare against. + # + # @return [ true, false ] The result of the comparison. + # + # @since 4.3.0 + def <=>(other) + raise ArgumentError.new(COMPARISON_ERROR_MESSAGE % other.class) unless other.is_a?(Timestamp) + return 0 if self == other + a = [ seconds, increment ] + b = [ other.seconds, other.increment ] + [ a, b ].sort[0] == a ? -1 : 1 + end + # Get the timestamp as JSON hash data. # # @example Get the timestamp as a JSON hash. diff -Nru ruby-bson-4.2.1/lib/bson/version.rb ruby-bson-4.3.0/lib/bson/version.rb --- ruby-bson-4.2.1/lib/bson/version.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/lib/bson/version.rb 2018-02-06 17:17:50.000000000 +0000 @@ -13,5 +13,5 @@ # limitations under the License. module BSON - VERSION = "4.2.1".freeze + VERSION = "4.3.0".freeze end diff -Nru ruby-bson-4.2.1/metadata.gz.sig ruby-bson-4.3.0/metadata.gz.sig --- ruby-bson-4.2.1/metadata.gz.sig 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/metadata.gz.sig 2018-02-06 17:17:50.000000000 +0000 @@ -1 +1,3 @@ -jS#uSGM1;Hod0*ePY<5JgI[uB |x7tLV/̖}F2&:O+ެDCr:o*|3S篷)q&ݵAn9UżRc.?J\תئ?T3bwYhoSN?Mc`*B`gGSՉ4ՅG?0ONൿmK"pqވ \ No newline at end of file +'ɱmyRuWGN< dK n[Rv@|f\N1RZRav'mmc{]O\{3Ӵi53k%K` 'xL@$NCDiRmdӢMfK +-y@ɧ_ object.to_s }) end + + it_behaves_like "a JSON serializable object" end describe "::BSON_TYPE" do diff -Nru ruby-bson-4.2.1/spec/bson/document_spec.rb ruby-bson-4.3.0/spec/bson/document_spec.rb --- ruby-bson-4.2.1/spec/bson/document_spec.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/spec/bson/document_spec.rb 2018-02-06 17:17:50.000000000 +0000 @@ -96,6 +96,28 @@ end end + if described_class.instance_methods.include?(:dig) + describe "#dig" do + let(:document) do + described_class.new("key1" => { :key2 => "value" }) + end + + context "when provided string keys" do + + it "returns the value" do + expect(document.dig("key1", "key2")).to eq("value") + end + end + + context "when provided symbol keys" do + + it "returns the value" do + expect(document.dig(:key1, :key2)).to eq("value") + end + end + end + end + describe "#delete" do shared_examples_for "a document with deletable pairs" do @@ -756,6 +778,24 @@ end end + context "when the hash contains an array of hashes" do + let(:obj) do + described_class["key",[{"a" => 1}, {"b" => 2}]] + end + + let(:bson) do + "#{45.to_bson}#{Array::BSON_TYPE}key#{BSON::NULL_BYTE}" + + "#{35.to_bson}"+ + "#{BSON::Document::BSON_TYPE}0#{BSON::NULL_BYTE}#{12.to_bson}#{BSON::Int32::BSON_TYPE}a#{BSON::NULL_BYTE}#{1.to_bson}#{BSON::NULL_BYTE}" + + "#{BSON::Document::BSON_TYPE}1#{BSON::NULL_BYTE}#{12.to_bson}#{BSON::Int32::BSON_TYPE}b#{BSON::NULL_BYTE}#{2.to_bson}#{BSON::NULL_BYTE}" + + "#{BSON::NULL_BYTE}" + + "#{BSON::NULL_BYTE}" + end + + it_behaves_like "a serializable bson element" + it_behaves_like "a deserializable bson element" + end + context "when the hash is a single level" do let(:obj) do diff -Nru ruby-bson-4.2.1/spec/bson/int64_spec.rb ruby-bson-4.3.0/spec/bson/int64_spec.rb --- ruby-bson-4.2.1/spec/bson/int64_spec.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/spec/bson/int64_spec.rb 2018-02-06 17:17:50.000000000 +0000 @@ -64,10 +64,10 @@ context "when the integer is within the MRI Fixnum range" do - let(:integer) { BSON::Integer::MAX_32BIT + 1 } + let(:integer) { 2**30 - 1 } let(:bson) do - BSON::ByteBuffer.new(integer.to_bson.to_s) + BSON::ByteBuffer.new(BSON::Int64.new(integer).to_bson.to_s) end context "when on JRuby", if: BSON::Environment.jruby? do @@ -77,15 +77,22 @@ end end - context "when using MRI", unless: BSON::Environment.jruby? do + context "when using MRI < 2.4", if: (!BSON::Environment.jruby? && RUBY_VERSION < '2.4') do it "deserializes to a Fixnum object" do expect(described_class.from_bson(bson).class).to be(Fixnum) end end + + context "when using MRI >= 2.4", if: (!BSON::Environment.jruby? && RUBY_VERSION >= '2.4') do + + it "deserializes to an Integer object" do + expect(described_class.from_bson(bson).class).to be(Integer) + end + end end - context "when the 64-bit integer is the BSON max and thus larger than the MRI Fixnum range" do + context "when the 64-bit integer is the BSON max and thus larger than the MRI Fixnum range on all architectures" do let(:integer) { Integer::MAX_64BIT } @@ -100,12 +107,19 @@ end end - context "when using MRI", unless: BSON::Environment.jruby? do + context "when using MRI < 2.4", if: (!BSON::Environment.jruby? && RUBY_VERSION < '2.4') do it "deserializes to a Bignum object" do expect(described_class.from_bson(bson).class).to be(Bignum) end end + + context "when using MRI >= 2.4", if: (!BSON::Environment.jruby? && RUBY_VERSION >= '2.4') do + + it "deserializes to an Integer object" do + expect(described_class.from_bson(bson).class).to be(Integer) + end + end end end diff -Nru ruby-bson-4.2.1/spec/bson/object_id_spec.rb ruby-bson-4.3.0/spec/bson/object_id_spec.rb --- ruby-bson-4.2.1/spec/bson/object_id_spec.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/spec/bson/object_id_spec.rb 2018-02-06 17:17:50.000000000 +0000 @@ -392,6 +392,43 @@ end end + describe "#clone" do + + context "when the data has not been generated yet" do + + let!(:object_id) do + described_class.new + end + + let!(:clone) do + object_id.clone + end + + it "generates and copies the data" do + expect(clone).to eq(object_id) + end + end + + context "when the data has been generated" do + + let!(:object_id) do + described_class.new + end + + let(:clone) do + object_id.clone + end + + before do + object_id.to_s + end + + it "copies the data" do + expect(clone).to eq(object_id) + end + end + end + describe "#inspect" do let(:object_id) do diff -Nru ruby-bson-4.2.1/spec/bson/timestamp_spec.rb ruby-bson-4.3.0/spec/bson/timestamp_spec.rb --- ruby-bson-4.2.1/spec/bson/timestamp_spec.rb 2016-12-21 12:48:40.000000000 +0000 +++ ruby-bson-4.3.0/spec/bson/timestamp_spec.rb 2018-02-06 17:17:50.000000000 +0000 @@ -48,6 +48,61 @@ end end + describe "#<=>" do + + let(:timestamp) do + described_class.new(1, 10) + end + + context "when the objects are equal" do + + let(:other) { described_class.new(1, 10) } + + it "returns 0" do + expect(timestamp).to eq(other) + expect(timestamp < other).to be(false) + expect(timestamp > other).to be(false) + expect(timestamp >= other).to be(true) + expect(timestamp <= other).to be(true) + end + end + + context "when the first object is less than the second" do + + let(:other) { described_class.new(1, 15) } + + it "returns -1" do + expect(timestamp <=> other).to be(-1) + expect(timestamp < other).to be(true) + expect(timestamp > other).to be(false) + expect(timestamp >= other).to be(false) + expect(timestamp <= other).to be(true) + end + end + + context "when the first object is greater than the second" do + + let(:other) { described_class.new(1, 5) } + + it "returns 1" do + expect(timestamp <=> other).to be(1) + expect(timestamp < other).to be(false) + expect(timestamp > other).to be(true) + expect(timestamp >= other).to be(true) + expect(timestamp <= other).to be(false) + end + end + + context "when the other object is not a timestamp" do + + it "raises an ArgumentError" do + expect { + timestamp < 1 + }.to raise_exception(ArgumentError) + end + end + end + describe "#as_json" do let(:object) do