├── .evergreen ├── config.yml ├── config │ ├── axes.yml.erb │ ├── config.yml.erb │ ├── functions.yml.erb │ ├── settings.yml.erb │ └── variants.yml.erb ├── functions.sh ├── run-tests.sh └── update-evergreen-configs ├── .github ├── CODEOWNERS └── workflows │ ├── bson-ruby.yml │ ├── cleanup.yml │ ├── codeql.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .rspec ├── .rubocop.yml ├── .yardopts ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── NOTICE ├── README.md ├── Rakefile ├── bson.gemspec ├── ext └── bson │ ├── bson-endian.h │ ├── bson-native.h │ ├── bytebuf.c │ ├── endian.c │ ├── extconf.rb │ ├── init.c │ ├── libbson-utf8.c │ ├── read.c │ ├── util.c │ └── write.c ├── lib ├── bson.rb └── bson │ ├── active_support.rb │ ├── array.rb │ ├── big_decimal.rb │ ├── binary.rb │ ├── boolean.rb │ ├── code.rb │ ├── code_with_scope.rb │ ├── config.rb │ ├── date.rb │ ├── date_time.rb │ ├── db_pointer.rb │ ├── dbref.rb │ ├── decimal128.rb │ ├── decimal128 │ └── builder.rb │ ├── document.rb │ ├── environment.rb │ ├── error.rb │ ├── error │ ├── bson_decode_error.rb │ ├── ext_json_parse_error.rb │ ├── illegal_key.rb │ ├── invalid_binary_type.rb │ ├── invalid_dbref_argument.rb │ ├── invalid_decimal128_argument.rb │ ├── invalid_decimal128_range.rb │ ├── invalid_decimal128_string.rb │ ├── invalid_key.rb │ ├── invalid_object_id.rb │ ├── invalid_regexp_pattern.rb │ ├── unrepresentable_precision.rb │ ├── unserializable_class.rb │ ├── unsupported_binary_subtype.rb │ └── unsupported_type.rb │ ├── ext_json.rb │ ├── false_class.rb │ ├── float.rb │ ├── hash.rb │ ├── int32.rb │ ├── int64.rb │ ├── integer.rb │ ├── json.rb │ ├── max_key.rb │ ├── min_key.rb │ ├── nil_class.rb │ ├── object.rb │ ├── object_id.rb │ ├── open_struct.rb │ ├── regexp.rb │ ├── registry.rb │ ├── specialized.rb │ ├── string.rb │ ├── symbol.rb │ ├── time.rb │ ├── time_with_zone.rb │ ├── timestamp.rb │ ├── true_class.rb │ ├── undefined.rb │ ├── vector.rb │ └── version.rb ├── perf ├── README.md ├── Rakefile ├── bench.rb └── bench_test.rb ├── sbom.json ├── spec ├── README.md ├── bson │ ├── array_spec.rb │ ├── big_decimal_spec.rb │ ├── binary_spec.rb │ ├── binary_uuid_spec.rb │ ├── boolean_spec.rb │ ├── byte_buffer_read_spec.rb │ ├── byte_buffer_spec.rb │ ├── byte_buffer_write_spec.rb │ ├── code_spec.rb │ ├── code_with_scope_spec.rb │ ├── config_spec.rb │ ├── date_spec.rb │ ├── date_time_spec.rb │ ├── dbref_legacy_spec.rb │ ├── dbref_spec.rb │ ├── decimal128_spec.rb │ ├── document_as_spec.rb │ ├── document_spec.rb │ ├── ext_json_parse_spec.rb │ ├── false_class_spec.rb │ ├── float_spec.rb │ ├── hash_as_spec.rb │ ├── hash_spec.rb │ ├── int32_spec.rb │ ├── int64_spec.rb │ ├── integer_spec.rb │ ├── json_spec.rb │ ├── max_key_spec.rb │ ├── min_key_spec.rb │ ├── nil_class_spec.rb │ ├── object_id_spec.rb │ ├── object_spec.rb │ ├── open_struct_spec.rb │ ├── raw_spec.rb │ ├── regexp_spec.rb │ ├── registry_spec.rb │ ├── string_spec.rb │ ├── symbol_raw_spec.rb │ ├── symbol_spec.rb │ ├── time_spec.rb │ ├── time_with_zone_spec.rb │ ├── timestamp_spec.rb │ ├── true_class_spec.rb │ ├── undefined_spec.rb │ └── vector_spec.rb ├── bson_spec.rb ├── runners │ ├── binary_vector.rb │ ├── common_driver.rb │ ├── corpus.rb │ └── corpus_legacy.rb ├── spec_helper.rb ├── spec_tests │ ├── binary_vector_spec.rb │ ├── common_driver_spec.rb │ ├── corpus_legacy_spec.rb │ ├── corpus_spec.rb │ └── data │ │ ├── binary_vector │ │ ├── README.md │ │ ├── float32.json │ │ ├── int8.json │ │ └── packed_bit.json │ │ ├── corpus │ │ ├── README.md │ │ ├── array.json │ │ ├── binary.json │ │ ├── boolean.json │ │ ├── code.json │ │ ├── code_w_scope.json │ │ ├── datetime.json │ │ ├── dbpointer.json │ │ ├── dbref.json │ │ ├── decimal128-1.json │ │ ├── decimal128-2.json │ │ ├── decimal128-3.json │ │ ├── decimal128-4.json │ │ ├── decimal128-5.json │ │ ├── decimal128-6.json │ │ ├── decimal128-7.json │ │ ├── document.json │ │ ├── double.json │ │ ├── int32.json │ │ ├── int64.json │ │ ├── maxkey.json │ │ ├── minkey.json │ │ ├── multi-type-deprecated.json │ │ ├── multi-type.json │ │ ├── null.json │ │ ├── oid.json │ │ ├── regex.json │ │ ├── string.json │ │ ├── symbol.json │ │ ├── timestamp.json │ │ ├── top.json │ │ └── undefined.json │ │ ├── corpus_legacy │ │ ├── array.json │ │ ├── binary.json │ │ ├── boolean.json │ │ ├── code.json │ │ ├── code_w_scope.json │ │ ├── document.json │ │ ├── double.json │ │ ├── failures │ │ │ ├── datetime.json │ │ │ ├── dbpointer.json │ │ │ ├── int64.json │ │ │ └── symbol.json │ │ ├── int32.json │ │ ├── maxkey.json │ │ ├── minkey.json │ │ ├── null.json │ │ ├── oid.json │ │ ├── regex.json │ │ ├── string.json │ │ ├── timestamp.json │ │ ├── top.json │ │ └── undefined.json │ │ └── decimal128 │ │ ├── decimal128-1.json │ │ ├── decimal128-2.json │ │ ├── decimal128-3.json │ │ ├── decimal128-4.json │ │ ├── decimal128-5.json │ │ ├── decimal128-6.json │ │ └── decimal128-7.json └── support │ ├── shared_examples.rb │ ├── spec_config.rb │ └── utils.rb └── src └── main └── org └── bson_ruby ├── ByteBuf.java ├── GeneratorExtension.java └── NativeService.java /.evergreen/config/axes.yml.erb: -------------------------------------------------------------------------------- 1 | axes: 2 | - id: "all-os" 3 | display_name: OS 4 | values: 5 | - id: debian 6 | display_name: "Debian 11" 7 | run_on: debian11-small 8 | variables: 9 | MACHINE: "debian11" 10 | - id: "ubuntu2204" 11 | display_name: "Ubuntu 22.04" 12 | run_on: ubuntu2204-small 13 | variables: 14 | MACHINE: "ubuntu2204" 15 | - id: "ubuntu2004" 16 | display_name: "Ubuntu 20.04" 17 | run_on: ubuntu2004-small 18 | variables: 19 | MACHINE: "ubuntu2004" 20 | 21 | - id: "special-os" 22 | display_name: OS 23 | values: 24 | - id: "ubuntu2204-arm64" 25 | display_name: "Ubuntu 22.04 ARM64" 26 | run_on: ubuntu2204-arm64-small 27 | variables: 28 | MACHINE: ubuntu2204-arm64 29 | 30 | - id: ruby 31 | display_name: Ruby Version 32 | values: 33 | <% supported_rubies.each do |ruby| %> 34 | - id: <%= ruby %> 35 | display_name: <%= ruby %> 36 | variables: 37 | RVM_RUBY: <%= ruby %> 38 | <% end %> 39 | 40 | - id: "as" 41 | display_name: ActiveSupport 42 | values: 43 | - id: "5.1" 44 | display_name: 5.1 45 | variables: 46 | WITH_ACTIVE_SUPPORT: "~> 5.1.0" 47 | - id: "5.2" 48 | display_name: 5.2 49 | variables: 50 | WITH_ACTIVE_SUPPORT: "~> 5.2.0" 51 | - id: "6.0" 52 | display_name: 6.0 53 | variables: 54 | WITH_ACTIVE_SUPPORT: "~> 6.0.0" 55 | - id: "6.1" 56 | display_name: 6.1 57 | variables: 58 | WITH_ACTIVE_SUPPORT: "~> 6.1.0" 59 | - id: "7.0" 60 | display_name: 7.0 61 | variables: 62 | WITH_ACTIVE_SUPPORT: "~> 7.0.0" 63 | - id: "7.1" 64 | display_name: 7.1 65 | variables: 66 | WITH_ACTIVE_SUPPORT: "~> 7.1.0" 67 | - id: "8.0" 68 | display_name: 8.0 69 | variables: 70 | WITH_ACTIVE_SUPPORT: "~> 8.0.0" 71 | 72 | - id: "compact" 73 | display_name: GC.compact 74 | values: 75 | - id: "on" 76 | display_name: with GC.compact 77 | variables: 78 | COMPACT: true 79 | -------------------------------------------------------------------------------- /.evergreen/config/config.yml.erb: -------------------------------------------------------------------------------- 1 | <%= template 'settings' %> 2 | <%= template 'functions' %> 3 | <%= template 'axes' %> 4 | <%= template 'variants' %> 5 | -------------------------------------------------------------------------------- /.evergreen/config/settings.yml.erb: -------------------------------------------------------------------------------- 1 | # When a task that used to pass starts to fail 2 | # Go through all versions that may have been skipped to detect 3 | # when the task started failing 4 | stepback: true 5 | 6 | # Fail builds when pre tasks fail. 7 | pre_error_fails_task: true 8 | 9 | # Mark a failure as a system/bootstrap failure (purple box) rather then a task 10 | # failure by default. 11 | # Actual testing tasks are marked with `type: test` 12 | command_type: system 13 | 14 | # Protect ourself against rogue test case, or curl gone wild, that runs forever 15 | # 12 minutes is the longest we'll ever run 16 | exec_timeout_secs: 3600 # 12 minutes is the longest we'll ever run 17 | 18 | # What to do when evergreen hits the timeout (`post:` tasks are run automatically) 19 | timeout: 20 | - command: shell.exec 21 | params: 22 | script: | 23 | ls -la 24 | -------------------------------------------------------------------------------- /.evergreen/config/variants.yml.erb: -------------------------------------------------------------------------------- 1 | buildvariants: 2 | - matrix_name: "mri-latest" 3 | matrix_spec: { ruby: <%= latest_mri_ruby %>, all-os: '*' } 4 | display_name: "${ruby}, ${all-os}" 5 | tasks: 6 | - name: "test" 7 | 8 | - matrix_name: "mri-sample" 9 | matrix_spec: { ruby: <%= sample_without_latest %>, all-os: ubuntu2004 } 10 | display_name: "${ruby}, ${all-os}" 11 | tasks: 12 | - name: "test" 13 | 14 | - matrix_name: "activesupport-5-6" 15 | matrix_spec: 16 | ruby: <%= older_rubies %> 17 | all-os: ubuntu2004 18 | as: [ '5.1', '5.2', '6.0', '6.1' ] 19 | display_name: "AS ${as} ${ruby}, ${all-os}" 20 | tasks: 21 | - name: "test" 22 | 23 | - matrix_name: "activesupport-7" 24 | matrix_spec: 25 | ruby: <%= latest_mri_ruby %> 26 | all-os: ubuntu2004 27 | as: [ '7.0', '7.1' ] 28 | display_name: "AS ${as} ${ruby}, ${all-os}" 29 | tasks: 30 | - name: "test" 31 | 32 | - matrix_name: "activesupport-8" 33 | matrix_spec: 34 | ruby: <%= latest_mri_ruby %> 35 | all-os: ubuntu2004 36 | as: '8.0' 37 | display_name: "AS ${as} ${ruby}, ${all-os}" 38 | tasks: 39 | - name: "test" 40 | 41 | - matrix_name: "special-os" 42 | matrix_spec: { ruby: <%= recent_rubies %>, special-os: '*' } 43 | display_name: "${ruby}, ${special-os}" 44 | tasks: 45 | - name: "test" 46 | 47 | - matrix_name: "jruby" 48 | matrix_spec: { ruby: <%= jrubies %>, all-os: ubuntu2204 } 49 | display_name: "${ruby}, ${all-os}" 50 | tasks: 51 | - name: "test" 52 | 53 | - matrix_name: "compact" 54 | matrix_spec: 55 | ruby: <%= sample_mri_rubies %> 56 | all-os: ubuntu2004 57 | compact: "on" 58 | display_name: "${ruby} with GC.compact" 59 | tasks: 60 | - name: "test" 61 | -------------------------------------------------------------------------------- /.evergreen/functions.sh: -------------------------------------------------------------------------------- 1 | set_home() { 2 | if test -z "$HOME"; then 3 | export HOME=$(pwd) 4 | fi 5 | } 6 | 7 | set_env_vars() { 8 | export CI=evergreen 9 | 10 | # JRUBY_OPTS were initially set for Mongoid 11 | export JRUBY_OPTS="--server -J-Xms512m -J-Xmx2G" 12 | 13 | if test "$BSON" = min; then 14 | export BUNDLE_GEMFILE=gemfiles/bson_min.gemfile 15 | elif test "$BSON" = master; then 16 | export BUNDLE_GEMFILE=gemfiles/bson_master.gemfile 17 | fi 18 | } 19 | 20 | bundle_install() { 21 | #which bundle 22 | #bundle --version 23 | args=--quiet 24 | if test -n "$BUNDLE_GEMFILE"; then 25 | args="$args --gemfile=$BUNDLE_GEMFILE" 26 | fi 27 | echo "Running bundle install $args" 28 | bundle install $args 29 | } 30 | 31 | install_deps() { 32 | bundle_install 33 | bundle exec rake clean 34 | } 35 | 36 | kill_jruby() { 37 | jruby_running=`ps -ef | grep 'jruby' | grep -v grep | awk '{print $2}'` 38 | if [ -n "$jruby_running" ];then 39 | echo "terminating remaining jruby processes" 40 | for pid in $(ps -ef | grep "jruby" | grep -v grep | awk '{print $2}'); do kill -9 $pid; done 41 | fi 42 | } 43 | -------------------------------------------------------------------------------- /.evergreen/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o xtrace # Write all commands first to stderr 4 | set -o errexit # Exit the script with error if any of the commands fail 5 | 6 | # Supported/used environment variables: 7 | # RVM_RUBY Define the Ruby version to test with, using its RVM identifier. 8 | # For example: "ruby-2.4" or "jruby-9.2" 9 | 10 | . `dirname "$0"`/../spec/shared/shlib/distro.sh 11 | . `dirname "$0"`/../spec/shared/shlib/set_env.sh 12 | . `dirname "$0"`/functions.sh 13 | 14 | set_env_vars 15 | 16 | set_env_ruby 17 | 18 | install_deps 19 | 20 | echo "Running specs" 21 | bundle exec rake spec 22 | test_status=$? 23 | echo "TEST STATUS" 24 | echo ${test_status} 25 | 26 | kill_jruby 27 | 28 | exit ${test_status} 29 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @mongodb/dbx-ruby 2 | -------------------------------------------------------------------------------- /.github/workflows/bson-ruby.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | rubocop: 7 | runs-on: ubuntu-latest 8 | env: 9 | CI: true 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Set up Ruby 3.4 14 | uses: ruby/setup-ruby@v1 15 | with: 16 | ruby-version: 3.4 17 | bundler-cache: true 18 | - name: Run RuboCop 19 | run: bundle exec rubocop --parallel 20 | 21 | build: 22 | name: >- 23 | ${{ matrix.os }} ${{ matrix.ruby }} 24 | env: 25 | CI: true 26 | TESTOPTS: -v 27 | 28 | runs-on: ${{ matrix.os }}-latest 29 | if: | 30 | !( contains(github.event.pull_request.title, '[ci skip]') 31 | || contains(github.event.pull_request.title, '[skip ci]') 32 | || contains(github.event.head_commit.message, '[ci skip]') 33 | || contains(github.event.head_commit.message, '[skip ci]')) 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | os: [ ubuntu, macos, windows ] 38 | ruby: [ 2.7, '3.0', 3.1, 3.2, 3.3, 3.4, head ] 39 | include: 40 | - { os: windows , ruby: ucrt } 41 | exclude: 42 | - { os: windows , ruby: head } 43 | 44 | steps: 45 | - name: repo checkout 46 | uses: actions/checkout@v4 47 | with: 48 | submodules: recursive 49 | 50 | - name: load ruby 51 | uses: ruby/setup-ruby@v1 52 | with: 53 | ruby-version: ${{ matrix.ruby }} 54 | rubygems: latest 55 | bundler-cache: true 56 | 57 | - name: compile 58 | run: bundle exec rake compile 59 | 60 | - name: test 61 | timeout-minutes: 10 62 | run: bundle exec rake spec 63 | -------------------------------------------------------------------------------- /.github/workflows/cleanup.yml: -------------------------------------------------------------------------------- 1 | name: "Dry-Run Cleanup" 2 | run-name: "Dry Run Cleanup for ${{ github.ref }}" 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | confirm: 8 | description: Indicate whether you want this workflow to run (must be "true") 9 | required: true 10 | type: string 11 | tag: 12 | description: The name of the tag (and release) to clean up 13 | required: true 14 | type: string 15 | 16 | jobs: 17 | release: 18 | name: "Dry-Run Cleanup" 19 | environment: release 20 | runs-on: 'ubuntu-latest' 21 | if: ${{ inputs.confirm == 'true' }} 22 | 23 | permissions: 24 | # required for all workflows 25 | security-events: write 26 | 27 | # required to fetch internal or private CodeQL packs 28 | packages: read 29 | 30 | # only required for workflows in private repositories 31 | actions: read 32 | contents: write 33 | 34 | # required by the mongodb-labs/drivers-github-tools/setup@v2 step 35 | # also required by `rubygems/release-gem` 36 | id-token: write 37 | 38 | steps: 39 | - name: "Run the cleanup action" 40 | uses: mongodb-labs/drivers-github-tools/ruby/cleanup@v2 41 | with: 42 | app_id: ${{ vars.APP_ID }} 43 | app_private_key: ${{ secrets.APP_PRIVATE_KEY }} 44 | tag: ${{ inputs.tag }} 45 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | analyze: 7 | name: Analyze (${{ matrix.language }}) 8 | # Runner size impacts CodeQL analysis time. To learn more, please see: 9 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 10 | # - https://gh.io/supported-runners-and-hardware-resources 11 | # - https://gh.io/using-larger-runners (GitHub.com only) 12 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 13 | runs-on: 'ubuntu-latest' 14 | timeout-minutes: 360 15 | permissions: 16 | # required for all workflows 17 | security-events: write 18 | 19 | # required to fetch internal or private CodeQL packs 20 | packages: read 21 | 22 | # only required for workflows in private repositories 23 | actions: read 24 | contents: read 25 | 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | include: 30 | - language: ruby 31 | build-mode: none 32 | - language: c 33 | build-mode: manual 34 | - language: java 35 | build-mode: none 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v4 39 | 40 | - name: Setup Ruby 41 | if: matrix.build-mode == 'manual' 42 | uses: ruby/setup-ruby@v1 43 | with: 44 | ruby-version: '3.2' 45 | bundler-cache: true 46 | 47 | # Initializes the CodeQL tools for scanning. 48 | - name: Initialize CodeQL 49 | uses: github/codeql-action/init@v3 50 | with: 51 | languages: ${{ matrix.language }} 52 | build-mode: ${{ matrix.build-mode }} 53 | config: | 54 | paths-ignore: 55 | - .evergreen 56 | - spec 57 | - perf 58 | - vendor 59 | 60 | - name: Manually build the native code 61 | if: matrix.build-mode == 'manual' 62 | run: | 63 | bundle exec rake compile 64 | 65 | - name: Perform CodeQL Analysis 66 | uses: github/codeql-action/analyze@v3 67 | with: 68 | category: "/language:${{matrix.language}}" 69 | 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | build/* 3 | *.swp 4 | .#* 5 | \#*# 6 | *__pycache__* 7 | *.pyc 8 | *.pyo 9 | *.bak 10 | .DS_Store 11 | .rvmrc 12 | tmp/* 13 | *.o 14 | *.bundle 15 | *.so 16 | core 17 | .idea/ 18 | *.gem 19 | perf/profile.rb 20 | perf/*.pdf 21 | perf/*.profile 22 | perf/*.symbols 23 | TODO 24 | .rbx/ 25 | gem-private_key.pem 26 | .ruby-version 27 | .ruby-gemset 28 | yard-docs 29 | lib/*.jar 30 | .yardoc 31 | .byebug_history 32 | .env 33 | .env.private* 34 | docs/_build 35 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spec/shared"] 2 | path = spec/shared 3 | url = https://github.com/mongodb-labs/mongo-ruby-spec-shared 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format <%= ENV["CI"] ? 'Rfc::Riff' : 'Fuubar'%> 3 | --tty 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - rubocop-performance 3 | - rubocop-rake 4 | - rubocop-rspec 5 | 6 | AllCops: 7 | TargetRubyVersion: 2.6 8 | NewCops: enable 9 | Exclude: 10 | - 'spec/shared/**/*' 11 | - 'tmp/**/*' 12 | - 'vendor/**/*' 13 | 14 | Bundler: 15 | Enabled: true 16 | 17 | Gemspec: 18 | Enabled: true 19 | 20 | Layout: 21 | Enabled: true 22 | 23 | Lint: 24 | Enabled: true 25 | 26 | Metrics: 27 | Enabled: true 28 | 29 | Naming: 30 | Enabled: true 31 | 32 | Performance: 33 | Enabled: true 34 | 35 | Rake: 36 | Enabled: true 37 | 38 | RSpec: 39 | Enabled: true 40 | 41 | Security: 42 | Exclude: 43 | - 'spec/**/*' 44 | 45 | Style: 46 | Enabled: true 47 | 48 | # -------------------------------------- 49 | # Cops below this line set intentionally 50 | # -------------------------------------- 51 | 52 | Bundler/OrderedGems: 53 | Enabled: false 54 | 55 | Gemspec/OrderedDependencies: 56 | Enabled: false 57 | 58 | Layout/SpaceInsideArrayLiteralBrackets: 59 | EnforcedStyle: space 60 | 61 | Layout/SpaceInsidePercentLiteralDelimiters: 62 | Enabled: false 63 | 64 | Metrics/ClassLength: 65 | Max: 200 66 | 67 | Metrics/ModuleLength: 68 | Enabled: false 69 | 70 | Metrics/MethodLength: 71 | Max: 20 72 | 73 | RSpec/BeforeAfterAll: 74 | Enabled: false 75 | 76 | # Ideally, we'd use this one, too, but our tests have not historically followed 77 | # this style and it's not worth changing right now, IMO 78 | RSpec/DescribeClass: 79 | Enabled: false 80 | 81 | Style/FetchEnvVar: 82 | Enabled: false 83 | 84 | Style/FormatString: 85 | Enabled: false 86 | 87 | RSpec/ImplicitExpect: 88 | EnforcedStyle: is_expected 89 | 90 | RSpec/MultipleExpectations: 91 | Enabled: false 92 | 93 | RSpec/MultipleMemoizedHelpers: 94 | Enabled: false 95 | 96 | RSpec/NestedGroups: 97 | Enabled: false 98 | 99 | Style/ClassVars: 100 | Enabled: false 101 | 102 | Style/Documentation: 103 | Exclude: 104 | - 'spec/**/*' 105 | 106 | Style/ModuleFunction: 107 | EnforcedStyle: extend_self 108 | 109 | Style/OptionalBooleanParameter: 110 | Enabled: false 111 | 112 | Style/ParallelAssignment: 113 | Enabled: false 114 | 115 | Style/TernaryParentheses: 116 | EnforcedStyle: require_parentheses_when_complex 117 | 118 | Style/TrailingCommaInArrayLiteral: 119 | Enabled: false 120 | 121 | Style/TrailingCommaInHashLiteral: 122 | Enabled: false 123 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | ext/bson/*.c 3 | -o yard-docs -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Code Conventions 5 | ---------------- 6 | 7 | Code style should fall in line with the style guide outlined by 8 | [Github](https://github.com/styleguide/ruby) 9 | 10 | Testing 11 | ------- 12 | 13 | Bug fixes and new features should always have the appropriate specs, and the 14 | specs should follow the following guidelines: 15 | 16 | - Prefer `let` and `let!` over the use of instance variables and `subject`. 17 | - Prefer `expect(...).to eq(...) syntax over `...should eq(...)`. 18 | - Use shared examples to reduce duplication. 19 | - Use `describe "#method"` for instance method specs. 20 | - Use `describe ".method"` for class method specs. 21 | - Use `context` blocks to set up conditions. 22 | - Always provide descriptive specifications via `it`. 23 | 24 | Specs can be automatically run with Guard, via `bundle exec guard` 25 | 26 | Before commiting, run `rake` to ensure all specs pass with both pure Ruby and 27 | the native extensions. 28 | 29 | Git Etiquette 30 | ------------- 31 | 32 | Please follow the commit message guidelines as outlined 33 | [in this blog post](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 34 | 35 | If the commit fixes a bug, please add the JIRA number on the last line: 36 | 37 | ``` 38 | [ close RUBY-492 ] 39 | ``` 40 | 41 | Please ensure that only one feature/bug fix is in each pull request, and 42 | that it is squashed into a single commit. 43 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | source 'https://rubygems.org' 5 | 6 | gemspec 7 | 8 | group :development, :test do 9 | gem 'rake' 10 | gem 'rake-compiler' 11 | gem 'yard' 12 | 13 | gem 'rspec', '~> 3' 14 | gem 'json' 15 | if ENV['WITH_ACTIVE_SUPPORT'] =~ /[0-9]/ && ENV['WITH_ACTIVE_SUPPORT'] != '0' 16 | gem 'activesupport', ENV['WITH_ACTIVE_SUPPORT'] 17 | else 18 | gem 'activesupport', '<8.1' 19 | end 20 | gem 'concurrent-ruby', '1.3.4' 21 | gem 'ruby-prof', platforms: :mri 22 | 23 | gem 'byebug', platforms: :mri 24 | # https://github.com/jruby/jruby/wiki/UsingTheJRubyDebugger 25 | gem 'ruby-debug', platforms: :jruby 26 | 27 | # JRuby 9.3 reports RUBY_VERSION as 2.6, and the latest versions of Rubocop 28 | # wants 2.7 or higher. It enough to use rubocop only on MRI, so we can skip 29 | # it on JRuby. 30 | unless RUBY_PLATFORM =~ /java/ 31 | gem 'rubocop', '~> 1.75.5' 32 | gem 'rubocop-performance', '~> 1.25.0' 33 | gem 'rubocop-rake', '~> 0.7.1' 34 | gem 'rubocop-rspec', '~> 3.6.0' 35 | end 36 | end 37 | 38 | group :test do 39 | gem 'fuubar' 40 | gem 'rfc' 41 | end 42 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Ruby BSON 2 | Copyright (C) 2009-2013 MongoDB, Inc. 3 | -------------------------------------------------------------------------------- /bson.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | lib = File.expand_path('../lib', __FILE__) 5 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 6 | require 'bson/version' 7 | 8 | Gem::Specification.new do |s| 9 | s.name = 'bson' 10 | s.version = BSON::VERSION 11 | s.authors = ["The MongoDB Ruby Team"] 12 | s.email = "dbx-ruby@mongodb.com" 13 | s.homepage = 'https://www.mongodb.com/docs/ruby-driver/current/tutorials/bson-v4/' 14 | s.summary = 'Ruby implementation of the BSON specification' 15 | s.description = 'A fully featured BSON specification implementation in Ruby' 16 | s.license = 'Apache-2.0' 17 | 18 | s.metadata = { 19 | 'bug_tracker_uri' => 'https://jira.mongodb.org/projects/RUBY', 20 | 'changelog_uri' => 'https://github.com/mongodb/bson-ruby/releases', 21 | 'documentation_uri' => 'https://www.mongodb.com/docs/ruby-driver/current/tutorials/bson-v4/', 22 | 'homepage_uri' => 'https://www.mongodb.com/docs/ruby-driver/current/tutorials/bson-v4/', 23 | 'source_code_uri' => 'https://github.com/mongodb/bson-ruby' 24 | } 25 | 26 | s.files = %w(CONTRIBUTING.md CHANGELOG.md LICENSE NOTICE README.md Rakefile) 27 | s.files += Dir.glob('lib/**/*') 28 | 29 | unless RUBY_PLATFORM =~ /java/ 30 | s.platform = Gem::Platform::RUBY 31 | s.files += Dir.glob('ext/**/*.{c,h,rb}') 32 | s.extensions = ['ext/bson/extconf.rb'] 33 | else 34 | s.platform = 'java' 35 | end 36 | 37 | if RUBY_VERSION > '3.2.99' 38 | s.add_dependency 'base64' 39 | s.add_dependency 'bigdecimal' 40 | s.add_dependency 'ostruct' 41 | end 42 | 43 | s.test_files = Dir.glob('spec/**/*') 44 | 45 | s.require_path = 'lib' 46 | s.required_ruby_version = '>= 2.6' 47 | s.required_rubygems_version = '>= 1.3.6' 48 | end 49 | -------------------------------------------------------------------------------- /ext/bson/extconf.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:disable all 3 | 4 | require 'mkmf' 5 | 6 | append_cflags(["-Wall", "-g", "-std=c99"]) 7 | 8 | create_makefile('bson_native') 9 | -------------------------------------------------------------------------------- /lib/bson.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require "bson/environment" 18 | 19 | # The core namespace for all BSON related behaviour. 20 | # 21 | # @since 0.0.0 22 | module BSON 23 | 24 | # Create a new object id from a string using ObjectId.from_string 25 | # 26 | # @example Create an object id from the string. 27 | # BSON::ObjectId(id) 28 | # 29 | # @param [ String ] string The string to create the id from. 30 | # 31 | # @raise [ BSON::Error::InvalidObjectId ] If the provided string is invalid. 32 | # 33 | # @return [ BSON::ObjectId ] The new object id. 34 | # 35 | # @see ObjectId.from_string 36 | def self.ObjectId(string) 37 | self::ObjectId.from_string(string) 38 | end 39 | 40 | # Constant for binary string encoding. 41 | # 42 | # @since 2.0.0 43 | BINARY = "BINARY" 44 | 45 | # Constant for bson types that don't actually serialize a value. 46 | # 47 | # @since 2.0.0 48 | NO_VALUE = ::String.new(encoding: BINARY).freeze 49 | 50 | # Constant for a null byte (0x00). 51 | # 52 | # @since 2.0.0 53 | NULL_BYTE = ::String.new(0.chr, encoding: BINARY).freeze 54 | 55 | # Constant for UTF-8 string encoding. 56 | # 57 | # @since 2.0.0 58 | UTF8 = "UTF-8" 59 | end 60 | 61 | require "bson/config" 62 | require "bson/error" 63 | require "bson/registry" 64 | require "bson/specialized" 65 | require "bson/json" 66 | require "bson/int32" 67 | require "bson/int64" 68 | require "bson/integer" 69 | require "bson/array" 70 | require "bson/binary" 71 | require "bson/boolean" 72 | require "bson/code" 73 | require "bson/code_with_scope" 74 | require "bson/date" 75 | require "bson/date_time" 76 | require "bson/db_pointer" 77 | require "bson/decimal128" 78 | require "bson/big_decimal" 79 | require "bson/document" 80 | require "bson/ext_json" 81 | require "bson/false_class" 82 | require "bson/float" 83 | require "bson/hash" 84 | require "bson/dbref" 85 | require "bson/open_struct" 86 | require "bson/max_key" 87 | require "bson/min_key" 88 | require "bson/nil_class" 89 | require "bson/object" 90 | require "bson/object_id" 91 | require "bson/regexp" 92 | require "bson/string" 93 | require "bson/symbol" 94 | require "bson/time" 95 | require "bson/timestamp" 96 | require "bson/true_class" 97 | require "bson/undefined" 98 | require "bson/vector" 99 | require "bson/version" 100 | 101 | # If we are using JRuby, attempt to load the Java extensions, if we are using 102 | # MRI or Rubinius, attempt to load the C extensions. 103 | # 104 | # @since 2.0.0 105 | begin 106 | if BSON::Environment.jruby? 107 | require "bson-ruby.jar" 108 | JRuby::Util.load_ext("org.bson_ruby.NativeService") 109 | else 110 | require "bson_native" 111 | end 112 | rescue LoadError => e 113 | $stderr.puts("Failed to load the necessary extensions: #{e.class}: #{e}") 114 | raise 115 | end 116 | -------------------------------------------------------------------------------- /lib/bson/active_support.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2018-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Require this file if using BSON with ActiveSupport. 18 | 19 | require "bson/time_with_zone" 20 | -------------------------------------------------------------------------------- /lib/bson/big_decimal.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2021 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for encoding and decoding BigDecimals 20 | # to and from raw bytes as specified by the BSON spec. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | module BigDecimal 24 | 25 | # BigDecimals are serialized as Decimal128s under the hood. A Decimal128 26 | # is type 0x13 in the BSON spec. 27 | BSON_TYPE = ::String.new(19.chr, encoding: BINARY).freeze 28 | 29 | # Get the BigDecimal as encoded BSON. 30 | # 31 | # @example Get the BigDecimal as encoded BSON. 32 | # BigDecimal("1").to_bson 33 | # 34 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 35 | # 36 | # @see http://bsonspec.org/#/specification 37 | def to_bson(buffer = ByteBuffer.new) 38 | BSON::Decimal128.new(to_s).to_bson(buffer) 39 | end 40 | 41 | # Get the BSON type for BigDecimal. This is the same BSON type as 42 | # BSON::Decimal128. 43 | def bson_type 44 | BSON_TYPE 45 | end 46 | 47 | module ClassMethods 48 | 49 | # Deserialize the BigDecimal from raw BSON bytes. If the :mode option 50 | # is set to BSON, this will return a BSON::Decimal128 51 | # 52 | # @example Get the BigDecimal from BSON. 53 | # BigDecimal.from_bson(bson) 54 | # 55 | # @param [ ByteBuffer ] buffer The byte buffer. 56 | # 57 | # @option options [ nil | :bson ] :mode Decoding mode to use. 58 | # 59 | # @return [ BigDecimal | BSON::Decimal128 ] The decimal object. 60 | def from_bson(buffer, **options) 61 | dec128 = Decimal128.from_bson(buffer, **options) 62 | if options[:mode] == :bson 63 | dec128 64 | else 65 | dec128.to_d 66 | end 67 | end 68 | end 69 | 70 | # Register this type when the module is loaded. 71 | Registry.register(BSON_TYPE, ::BigDecimal) 72 | end 73 | 74 | # Enrich the core BigDecimal class with this module. 75 | ::BigDecimal.send(:include, BigDecimal) 76 | ::BigDecimal.send(:extend, BigDecimal::ClassMethods) 77 | end 78 | -------------------------------------------------------------------------------- /lib/bson/boolean.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Represents a boolean type, which compares less than any other value in the 20 | # specification. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | class Boolean 26 | 27 | # A boolean is type 0x08 in the BSON spec. 28 | # 29 | # @since 2.0.0 30 | BSON_TYPE = ::String.new(8.chr, encoding: BINARY).freeze 31 | 32 | # Deserialize a boolean from BSON. 33 | # 34 | # @param [ ByteBuffer ] buffer The byte buffer. 35 | # 36 | # @option options [ nil | :bson ] :mode Decoding mode to use. 37 | # 38 | # @return [ TrueClass, FalseClass ] The decoded boolean. 39 | # 40 | # @see http://bsonspec.org/#/specification 41 | # 42 | # @since 2.0.0 43 | def self.from_bson(buffer, **options) 44 | case v = buffer.get_byte 45 | when TrueClass::TRUE_BYTE 46 | true 47 | when FalseClass::FALSE_BYTE 48 | false 49 | else 50 | raise Error::BSONDecodeError, "Invalid boolean byte value: #{v}" 51 | end 52 | end 53 | 54 | # Register this type when the module is loaded. 55 | # 56 | # @since 2.0.0 57 | Registry.register(BSON_TYPE, self) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/bson/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2016-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Provides configuration options for the BSON library. 20 | # 21 | # @since 4.1.0 22 | module Config 23 | extend self 24 | 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/bson/date.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require 'date' 18 | 19 | module BSON 20 | 21 | # Julian day of Date 1970-01-01 - UNIX timestamp reference. 22 | # 23 | # @api private 24 | DATE_REFERENCE = ::Date.new(1970, 1, 1).jd 25 | 26 | # Number of miliseconds in a day. 27 | # 28 | # @api private 29 | MILLISECONDS_IN_DAY = 60 * 60 * 24 * 1_000 30 | 31 | # Injects behaviour for encoding date values to raw bytes as specified by 32 | # the BSON spec for time. 33 | # 34 | # @see http://bsonspec.org/#/specification 35 | # 36 | # @since 2.1.0 37 | module Date 38 | 39 | # Get the date as encoded BSON. 40 | # 41 | # @example Get the date as encoded BSON. 42 | # Date.new(2012, 1, 1).to_bson 43 | # 44 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 45 | # 46 | # @see http://bsonspec.org/#/specification 47 | # 48 | # @since 2.1.0 49 | def to_bson(buffer = ByteBuffer.new) 50 | buffer.put_int64((jd - DATE_REFERENCE) * MILLISECONDS_IN_DAY) 51 | end 52 | 53 | # Get the BSON type for the date. 54 | # 55 | # As the date is converted to a time, this returns the BSON type for time. 56 | def bson_type 57 | ::Time::BSON_TYPE 58 | end 59 | end 60 | 61 | # Enrich the core Date class with this module. 62 | # 63 | # @since 2.1.0 64 | ::Date.send(:include, Date) 65 | end 66 | -------------------------------------------------------------------------------- /lib/bson/date_time.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require "time" 18 | 19 | module BSON 20 | 21 | # Injects behaviour for encoding date time values to raw bytes as specified by 22 | # the BSON spec for time. 23 | # 24 | # @see http://bsonspec.org/#/specification 25 | # 26 | # @since 2.1.0 27 | module DateTime 28 | 29 | # Get the date time as encoded BSON. 30 | # 31 | # @example Get the date time as encoded BSON. 32 | # DateTime.new(2012, 1, 1, 0, 0, 0).to_bson 33 | # 34 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 35 | # 36 | # @see http://bsonspec.org/#/specification 37 | # 38 | # @since 2.1.0 39 | def to_bson(buffer = ByteBuffer.new) 40 | gregorian.to_time.to_bson(buffer) 41 | end 42 | end 43 | 44 | # Enrich the core DateTime class with this module. 45 | # 46 | # @since 2.1.0 47 | ::DateTime.send(:include, DateTime) 48 | end 49 | -------------------------------------------------------------------------------- /lib/bson/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Provides static helper methods around determining what environment is 20 | # running without polluting the global namespace. 21 | # 22 | # @since 2.0.0 23 | module Environment 24 | extend self 25 | 26 | # Determine if we are using JRuby or not. 27 | # 28 | # @example Are we running with JRuby? 29 | # Environment.jruby? 30 | # 31 | # @return [ true, false ] If JRuby is our vm. 32 | # 33 | # @since 2.0.0 34 | def jruby? 35 | @jruby ||= defined?(JRUBY_VERSION) 36 | end 37 | 38 | # Determine if we are using Ruby version 1.9. 39 | # 40 | # @example Are we running with Ruby version 1.9? 41 | # Environment.ruby_1_9? 42 | # 43 | # @return [ true, false ] If the Ruby version is 1.9. 44 | # 45 | # @since 4.2.0 46 | # @deprecated 47 | def ruby_1_9? 48 | @ruby_1_9 ||= RUBY_VERSION < '2.0.0' 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/bson/error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | module BSON 4 | # Base exception class for all BSON-related errors. 5 | class Error < StandardError 6 | end 7 | end 8 | 9 | require 'bson/error/bson_decode_error' 10 | require 'bson/error/ext_json_parse_error' 11 | require 'bson/error/invalid_binary_type' 12 | require 'bson/error/invalid_dbref_argument' 13 | require 'bson/error/invalid_decimal128_argument' 14 | require 'bson/error/invalid_decimal128_range' 15 | require 'bson/error/invalid_decimal128_string' 16 | require 'bson/error/invalid_key' 17 | require 'bson/error/invalid_object_id' 18 | require 'bson/error/invalid_regexp_pattern' 19 | require 'bson/error/unrepresentable_precision' 20 | require 'bson/error/unserializable_class' 21 | require 'bson/error/unsupported_binary_subtype' 22 | require 'bson/error/unsupported_type' 23 | -------------------------------------------------------------------------------- /lib/bson/error/bson_decode_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Exception raised when BSON decoding fails. 8 | class BSONDecodeError < Error 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/bson/error/ext_json_parse_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Exception raised when Extended JSON parsing fails. 8 | class ExtJSONParseError < Error 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/bson/error/illegal_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when validating keys and a key is illegal in MongoDB 8 | class IllegalKey < Error 9 | 10 | # Instantiate the exception. 11 | # 12 | # @example Instantiate the exception. 13 | # BSON::Error::IllegalKey.new(string) 14 | # 15 | # @param [ String ] string The illegal string. 16 | # 17 | # @api private 18 | def initialize(string) 19 | super("'#{string}' is an illegal key in MongoDB. Keys may not start with '$' or contain a '.'.") 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_binary_type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when providing an invalid type to the Binary. 8 | class InvalidBinaryType < Error 9 | 10 | # @return [ Object ] The invalid type. 11 | attr_reader :type 12 | 13 | # Instantiate the new error. 14 | # 15 | # @example Instantiate the error. 16 | # InvalidBinaryType.new(:error) 17 | # 18 | # @param [ Object ] type The invalid type. 19 | # 20 | # @api private 21 | def initialize(type) 22 | @type = type 23 | end 24 | 25 | # Get the custom error message for the exception. 26 | # 27 | # @example Get the message. 28 | # error.message 29 | # 30 | # @return [ String ] The error message. 31 | def message 32 | "#{type.inspect} is not a valid binary type. " + 33 | "Please use one of #{BSON::Binary::SUBTYPES.keys.map(&:inspect).join(", ")}." 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_dbref_argument.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to create a BSON::DBRef from an object that is an invalid DBRef. 8 | class InvalidDBRefArgument < Error 9 | end 10 | end 11 | end 12 | 13 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_decimal128_argument.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to create a Decimal128 from an object that is neither a String nor a BigDecimal. 8 | class InvalidDecimal128Argument < Error 9 | 10 | # The custom error message for this error. 11 | MESSAGE = 'A Decimal128 can only be created from a String or BigDecimal.' 12 | 13 | # Get the custom error message for the exception. 14 | # 15 | # @example Get the message. 16 | # error.message 17 | # 18 | # @return [ String ] The error message. 19 | def message 20 | MESSAGE 21 | end 22 | end 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_decimal128_range.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when the exponent is outside the valid range. 8 | class InvalidDecimal128Range < Error 9 | 10 | # The custom error message for this error. 11 | # 12 | # @deprecated 13 | MESSAGE = 'Value out of range for Decimal128 representation.' 14 | 15 | # Get the custom error message for the exception. 16 | # 17 | # @example Get the message. 18 | # error.message 19 | # 20 | # @return [ String ] The error message. 21 | def message 22 | MESSAGE 23 | end 24 | end 25 | end 26 | end 27 | 28 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_decimal128_string.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to create a Decimal128 from a string with 8 | # an invalid format. 9 | class InvalidDecimal128String < Error 10 | 11 | # The custom error message for this error. 12 | MESSAGE = 'Invalid string format for creating a Decimal128 object.' 13 | 14 | # Get the custom error message for the exception. 15 | # 16 | # @example Get the message. 17 | # error.message 18 | # 19 | # @return [ String ] The error message. 20 | def message 21 | MESSAGE 22 | end 23 | end 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to serialize an object into a key. 8 | class InvalidKey < Error 9 | 10 | # Instantiate the exception. 11 | # 12 | # @example Instantiate the exception. 13 | # BSON::Object::InvalidKey.new(object) 14 | # 15 | # @param [ Object ] object The object that was meant for the key. 16 | # 17 | # @api private 18 | def initialize(object) 19 | super("#{object.class} instances are not allowed as keys in a BSON document.") 20 | end 21 | end 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_object_id.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to create an object id with invalid data. 8 | class InvalidObjectId < Error; end 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /lib/bson/error/invalid_regexp_pattern.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Exception raised when there is an invalid argument passed into the 8 | # constructor of regexp object. This includes when the argument contains 9 | # a null byte. 10 | class InvalidRegexpPattern < Error 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/bson/error/unrepresentable_precision.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when the significand provided is outside the valid range. 8 | class UnrepresentablePrecision < Error 9 | 10 | # Get the custom error message for the exception. 11 | # 12 | # @return [ String ] The error message. 13 | def message 14 | 'The value contains too much precision for Decimal128 representation' 15 | end 16 | end 17 | end 18 | end 19 | 20 | -------------------------------------------------------------------------------- /lib/bson/error/unserializable_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Exception raised when serializing an Array or Hash to BSON and an 8 | # array or hash element is of a class that does not define how to serialize 9 | # itself to BSON. 10 | class UnserializableClass < Error 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/bson/error/unsupported_binary_subtype.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Exception raised when decoding BSON and the data contains an 8 | # unsupported binary subtype. 9 | class UnsupportedBinarySubtype < Error 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/bson/error/unsupported_type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | module BSON 5 | class Error 6 | 7 | # Raised when trying to get a type from the registry that doesn't exist. 8 | class UnsupportedType < Error; end 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /lib/bson/false_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for encoding and decoding false values to and from 20 | # raw bytes as specified by the BSON spec. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | module FalseClass 26 | 27 | # A false value in the BSON spec is 0x00. 28 | # 29 | # @since 2.0.0 30 | FALSE_BYTE = String.new(0.chr, encoding: BINARY).freeze 31 | 32 | # The BSON type for false values is the general boolean type of 0x08. 33 | # 34 | # @example Get the bson type. 35 | # false.bson_type 36 | # 37 | # @return [ String ] The character 0x08. 38 | # 39 | # @since 2.0.0 40 | def bson_type 41 | Boolean::BSON_TYPE 42 | end 43 | 44 | # Get the false boolean as encoded BSON. 45 | # 46 | # @example Get the false boolean as encoded BSON. 47 | # false.to_bson 48 | # 49 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 50 | # 51 | # @see http://bsonspec.org/#/specification 52 | # 53 | # @since 2.0.0 54 | def to_bson(buffer = ByteBuffer.new) 55 | buffer.put_byte(FALSE_BYTE) 56 | end 57 | end 58 | 59 | # Enrich the core FalseClass class with this module. 60 | # 61 | # @since 2.0.0 62 | ::FalseClass.send(:include, FalseClass) 63 | end 64 | -------------------------------------------------------------------------------- /lib/bson/json.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Provides common behaviour for JSON serialization of objects. 20 | # 21 | # @since 2.0.0 22 | module JSON 23 | 24 | # Converting an object to JSON simply gets it's hash representation via 25 | # as_json, then converts it to a string. 26 | # 27 | # @example Convert the object to JSON 28 | # object.to_json 29 | # 30 | # @note All types must implement as_json. 31 | # 32 | # @return [ String ] The object as JSON. 33 | # 34 | # @since 2.0.0 35 | def to_json(*args) 36 | as_json.to_json(*args) 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/bson/max_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Represents a $maxKey type, which compares less than any other value in the 20 | # specification. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | class MaxKey 26 | include Comparable 27 | include JSON 28 | include Specialized 29 | 30 | # A $maxKey is type 0x7F in the BSON spec. 31 | # 32 | # @since 2.0.0 33 | BSON_TYPE = ::String.new(127.chr, encoding: BINARY).freeze 34 | 35 | # Constant for always evaluating greater in a comparison. 36 | # 37 | # @since 2.0.0 38 | GREATER = 1 39 | 40 | # When comparing a max key with any other object, the max key will always 41 | # be greater. 42 | # 43 | # @example Compare with another object. 44 | # max_key <=> 1000 45 | # 46 | # @param [ Object ] other The object to compare against. 47 | # 48 | # @return [ Integer ] Always 1. 49 | # 50 | # @since 2.0.0 51 | def <=>(other) 52 | GREATER 53 | end 54 | 55 | # Return a representation of the object for use in 56 | # application-level JSON serialization. Since BSON::MaxKey 57 | # is used exclusively in BSON-related contexts, this 58 | # method returns the canonical Extended JSON representation. 59 | # 60 | # @return [ Hash ] The extended json representation. 61 | def as_json(*_args) 62 | as_extended_json 63 | end 64 | 65 | # Converts this object to a representation directly serializable to 66 | # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md). 67 | # 68 | # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode 69 | # (default is canonical extended JSON) 70 | # 71 | # @return [ Hash ] The extended json representation. 72 | def as_extended_json(**options) 73 | { "$maxKey" => 1 } 74 | end 75 | 76 | # Register this type when the module is loaded. 77 | # 78 | # @since 2.0.0 79 | Registry.register(BSON_TYPE, self) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/bson/min_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Represents a $minKey type, which compares less than any other value in the 20 | # specification. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | class MinKey 26 | include Comparable 27 | include JSON 28 | include Specialized 29 | 30 | # A $minKey is type 0xFF in the BSON spec. 31 | # 32 | # @since 2.0.0 33 | BSON_TYPE = ::String.new(255.chr, encoding: BINARY).freeze 34 | 35 | # Constant for always evaluating lesser in a comparison. 36 | # 37 | # @since 2.0.0 38 | LESSER = -1 39 | 40 | # When comparing a min key with any other object, the min key will always 41 | # be lesser. 42 | # 43 | # @example Compare with another object. 44 | # min_key <=> 1000 45 | # 46 | # @param [ Object ] other The object to compare against. 47 | # 48 | # @return [ Integer ] Always -1. 49 | # 50 | # @since 2.0.0 51 | def <=>(other) 52 | LESSER 53 | end 54 | 55 | # Return a representation of the object for use in 56 | # application-level JSON serialization. Since BSON::MinKey 57 | # is used exclusively in BSON-related contexts, this 58 | # method returns the canonical Extended JSON representation. 59 | # 60 | # @return [ Hash ] The extended json representation. 61 | def as_json(*_args) 62 | as_extended_json 63 | end 64 | 65 | # Converts this object to a representation directly serializable to 66 | # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md). 67 | # 68 | # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode 69 | # (default is canonical extended JSON) 70 | # 71 | # @return [ Hash ] The extended json representation. 72 | def as_extended_json(**options) 73 | { "$minKey" => 1 } 74 | end 75 | 76 | # Register this type when the module is loaded. 77 | # 78 | # @since 2.0.0 79 | Registry.register(BSON_TYPE, self) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/bson/nil_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for encoding and decoding nil values to and from 20 | # raw bytes as specified by the BSON spec. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | module NilClass 26 | include Specialized 27 | 28 | # A nil is type 0x0A in the BSON spec. 29 | # 30 | # @since 2.0.0 31 | BSON_TYPE = ::String.new(10.chr, encoding: BINARY).freeze 32 | 33 | module ClassMethods 34 | 35 | # Deserialize NilClass from BSON. 36 | # 37 | # @param [ ByteBuffer ] buffer The byte buffer. 38 | # 39 | # @option options [ nil | :bson ] :mode Decoding mode to use. 40 | # 41 | # @return [ nil ] The decoded nil value. 42 | # 43 | # @see http://bsonspec.org/#/specification 44 | # 45 | # @since 2.0.0 46 | def from_bson(buffer, **options) 47 | nil 48 | end 49 | end 50 | 51 | # Register this type when the module is loaded. 52 | # 53 | # @since 2.0.0 54 | Registry.register(BSON_TYPE, ::NilClass) 55 | end 56 | 57 | # Enrich the core NilClass class with this module. 58 | # 59 | # @since 2.0.0 60 | ::NilClass.send(:include, NilClass) 61 | ::NilClass.send(:extend, NilClass::ClassMethods) 62 | end 63 | -------------------------------------------------------------------------------- /lib/bson/object.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for all Ruby objects. 20 | # 21 | # @since 2.2.4 22 | module Object 23 | 24 | # Objects that don't override this method will raise an error when trying 25 | # to use them as keys in a BSON document. This is only overridden in String 26 | # and Symbol. 27 | # 28 | # @example Convert the object to a BSON key. 29 | # object.to_bson_key 30 | # 31 | # @raise [ BSON::Error::InvalidKey ] Always raises an exception. 32 | # 33 | # @see http://bsonspec.org/#/specification 34 | # 35 | # @since 2.2.4 36 | def to_bson_key 37 | raise Error::InvalidKey.new(self) 38 | end 39 | 40 | # Converts the object to a normalized key in a BSON document. 41 | # 42 | # @example Convert the object to a normalized key. 43 | # object.to_bson_normalized_key 44 | # 45 | # @return [ Object ] self. 46 | # 47 | # @since 3.0.0 48 | def to_bson_normalized_key 49 | self 50 | end 51 | 52 | # Converts the object to a normalized value in a BSON document. 53 | # 54 | # @example Convert the object to a normalized value. 55 | # object.to_bson_normalized_value 56 | # 57 | # @return [ Object ] self. 58 | # 59 | # @since 3.0.0 60 | def to_bson_normalized_value 61 | self 62 | end 63 | 64 | # Serializes this object to Extended JSON 65 | # (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md). 66 | # 67 | # Subclasses should override +as_extended_json+ rather than this method. 68 | # 69 | # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode 70 | # (default is canonical extended JSON) 71 | # 72 | # @return [ String ] The extended json serialization. 73 | def to_extended_json(**options) 74 | as_extended_json(**options).to_json 75 | end 76 | 77 | # Converts this object to a representation directly serializable to 78 | # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md). 79 | # 80 | # Subclasses should override this method to provide custom serialization 81 | # to Extended JSON. 82 | # 83 | # @return [ Object ] The extended json representation. 84 | def as_extended_json(**_options) 85 | self 86 | end 87 | end 88 | 89 | # Enrich the core Object class with this module. 90 | # 91 | # @since 2.2.4 92 | ::Object.send(:include, Object) 93 | end 94 | -------------------------------------------------------------------------------- /lib/bson/open_struct.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2016-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for encoding OpenStruct objects using hashes 20 | # to raw bytes as specified by the BSON spec. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 4.2.0 25 | module OpenStruct 26 | 27 | # Get the OpenStruct as encoded BSON. 28 | # 29 | # @example Get the OpenStruct object as encoded BSON. 30 | # OpenStruct.new({ "field" => "value" }).to_bson 31 | # 32 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 33 | # 34 | # @see http://bsonspec.org/#/specification 35 | # 36 | # @since 4.2.0 37 | def to_bson(buffer = ByteBuffer.new) 38 | if Environment.ruby_1_9? 39 | marshal_dump.dup 40 | else 41 | to_h 42 | end.to_bson(buffer) 43 | end 44 | 45 | # The BSON type for OpenStruct objects is the Hash type of 0x03. 46 | # 47 | # @example Get the bson type. 48 | # struct.bson_type 49 | # 50 | # @return [ String ] The character 0x03. 51 | # 52 | # @since 4.2.0 53 | def bson_type 54 | ::Hash::BSON_TYPE 55 | end 56 | end 57 | 58 | ::OpenStruct.send(:include, OpenStruct) if defined?(::OpenStruct) 59 | end 60 | -------------------------------------------------------------------------------- /lib/bson/registry.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Provides constant values for each to the BSON types and mappings from raw 20 | # bytes back to these types. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | module Registry 26 | extend self 27 | 28 | # A Mapping of all the BSON types to their corresponding Ruby classes. 29 | # 30 | # @since 2.0.0 31 | MAPPINGS = {} 32 | 33 | # Get the class for the single byte identifier for the type in the BSON 34 | # specification. 35 | # 36 | # @example Get the type for the byte. 37 | # BSON::Registry.get("\x01") 38 | # 39 | # @return [ Class ] The corresponding Ruby class for the type. 40 | # 41 | # @see http://bsonspec.org/#/specification 42 | # 43 | # @since 2.0.0 44 | def get(byte, field = nil) 45 | if type = MAPPINGS[byte] || (byte.is_a?(String) && type = MAPPINGS[byte.ord]) 46 | type 47 | else 48 | handle_unsupported_type!(byte, field) 49 | end 50 | end 51 | 52 | # Register the Ruby type for the corresponding single byte. 53 | # 54 | # @example Register the type. 55 | # BSON::Registry.register("\x01", Float) 56 | # 57 | # @param [ String ] byte The single byte. 58 | # @param [ Class ] type The class the byte maps to. 59 | # 60 | # @return [ Class ] The class. 61 | # 62 | # @since 2.0.0 63 | def register(byte, type) 64 | MAPPINGS[byte.ord] = type 65 | define_type_reader(type) 66 | end 67 | 68 | private 69 | 70 | def define_type_reader(type) 71 | type.module_eval <<-MOD 72 | def bson_type; BSON_TYPE; end 73 | MOD 74 | end 75 | 76 | def handle_unsupported_type!(byte, field) 77 | message = "Detected unknown BSON type #{byte.inspect} " 78 | message += (field ? "for fieldname \"#{field}\". " : "in array. ") 79 | message +="Are you using the latest BSON version?" 80 | raise Error::UnsupportedType.new(message) 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/bson/specialized.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Provides behaviour to special values that exist in the BSON spec that don't 20 | # have a native type, like $minKey and $maxKey. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | module Specialized 26 | 27 | # Determine if the min key is equal to another object. 28 | # 29 | # @example Check min key equality. 30 | # BSON::MinKey.new == object 31 | # 32 | # @param [ Object ] other The object to check against. 33 | # 34 | # @return [ true, false ] If the objects are equal. 35 | # 36 | # @since 2.0.0 37 | def ==(other) 38 | self.class == other.class 39 | end 40 | 41 | # Encode the min key - has no value since it only needs the type and field 42 | # name when being encoded. 43 | # 44 | # @example Encode the min key value. 45 | # min_key.to_bson 46 | # 47 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 48 | # 49 | # @since 2.0.0 50 | def to_bson(buffer = ByteBuffer.new) 51 | buffer 52 | end 53 | 54 | private 55 | 56 | def self.included(klass) 57 | klass.extend(ClassMethods) 58 | end 59 | 60 | module ClassMethods 61 | 62 | # Deserialize from BSON. 63 | # 64 | # @param [ ByteBuffer ] buffer The byte buffer. 65 | # 66 | # @option options [ nil | :bson ] :mode Decoding mode to use. 67 | # 68 | # @return [ Specialized ] The decoded specialized class. 69 | # 70 | # @see http://bsonspec.org/#/specification 71 | # 72 | # @since 2.0.0 73 | def from_bson(buffer, **options) 74 | new 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/bson/time_with_zone.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2018-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require "active_support/time_with_zone" 18 | 19 | module BSON 20 | 21 | # Injects behaviour for encoding ActiveSupport::TimeWithZone values to 22 | # raw bytes as specified by the BSON spec for time. 23 | # 24 | # @see http://bsonspec.org/#/specification 25 | # 26 | # @since 4.4.0 27 | module TimeWithZone 28 | 29 | # Get the ActiveSupport::TimeWithZone as encoded BSON. 30 | # 31 | # @example Get the ActiveSupport::TimeWithZone as encoded BSON. 32 | # Time.utc(2012, 12, 12, 0, 0, 0).in_time_zone("Pacific Time (US & Canada)").to_bson 33 | # 34 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 35 | # 36 | # @see http://bsonspec.org/#/specification 37 | # 38 | # @since 4.4.0 39 | def to_bson(buffer = ByteBuffer.new) 40 | buffer.put_int64((to_i * 1000) + (usec / 1000)) 41 | end 42 | 43 | # Get the BSON type for the ActiveSupport::TimeWithZone. 44 | # 45 | # As the ActiveSupport::TimeWithZone is converted to a time, this returns 46 | # the BSON type for time. 47 | def bson_type 48 | ::Time::BSON_TYPE 49 | end 50 | 51 | # @api private 52 | def _bson_to_i 53 | # Workaround for JRuby's #to_i rounding negative timestamps up 54 | # rather than down (https://github.com/jruby/jruby/issues/6104) 55 | if BSON::Environment.jruby? 56 | (self - usec.to_r/1000000).to_i 57 | else 58 | to_i 59 | end 60 | end 61 | end 62 | 63 | # Enrich the ActiveSupport::TimeWithZone class with this module. 64 | # 65 | # @since 4.4.0 66 | ActiveSupport::TimeWithZone.send(:include, TimeWithZone) 67 | end 68 | -------------------------------------------------------------------------------- /lib/bson/true_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Injects behaviour for encoding and decoding true values to and from 20 | # raw bytes as specified by the BSON spec. 21 | # 22 | # @see http://bsonspec.org/#/specification 23 | # 24 | # @since 2.0.0 25 | module TrueClass 26 | 27 | # A true value in the BSON spec is 0x01. 28 | # 29 | # @since 2.0.0 30 | TRUE_BYTE = ::String.new(1.chr, encoding: BINARY).freeze 31 | 32 | # The BSON type for true values is the general boolean type of 0x08. 33 | # 34 | # @example Get the bson type. 35 | # false.bson_type 36 | # 37 | # @return [ String ] The character 0x08. 38 | # 39 | # @since 2.0.0 40 | def bson_type 41 | Boolean::BSON_TYPE 42 | end 43 | 44 | # Get the true boolean as encoded BSON. 45 | # 46 | # @example Get the true boolean as encoded BSON. 47 | # true.to_bson 48 | # 49 | # @return [ BSON::ByteBuffer ] The buffer with the encoded object. 50 | # 51 | # @see http://bsonspec.org/#/specification 52 | # 53 | # @since 2.0.0 54 | def to_bson(buffer = ByteBuffer.new) 55 | buffer.put_byte(TRUE_BYTE) 56 | end 57 | end 58 | 59 | # Enrich the core TrueClass class with this module. 60 | # 61 | # @since 2.0.0 62 | ::TrueClass.send(:include, TrueClass) 63 | end 64 | -------------------------------------------------------------------------------- /lib/bson/undefined.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | 19 | # Represents the Undefined BSON type 20 | # 21 | # @see http://bsonspec.org/#/specification 22 | # 23 | # @since 2.0.0 24 | class Undefined 25 | include JSON 26 | include Specialized 27 | 28 | # Undefined is type 0x06 in the BSON spec. 29 | # 30 | # @since 2.0.0 31 | BSON_TYPE = ::String.new(6.chr, encoding: BINARY).freeze 32 | 33 | # Determine if undefined is equal to another object. 34 | # 35 | # @example Check undefined equality. 36 | # BSON::Undefined.new == object 37 | # 38 | # @param [ Object ] other The object to check against. 39 | # 40 | # @return [ true, false ] If the objects are equal. 41 | # 42 | # @since 2.0.0 43 | def ==(other) 44 | self.class == other.class 45 | end 46 | 47 | # Return a string representation of the BSON::Undefined for use in 48 | # application-level JSON serialization. This method is intentionally 49 | # different from #as_extended_json. 50 | # 51 | # @example Get the undefined as a JSON-serializable object. 52 | # undefined.as_json 53 | # 54 | # @return [ nil ] The undefined as nil. 55 | def as_json(*args) 56 | nil 57 | end 58 | 59 | # Converts this object to a representation directly serializable to 60 | # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md). 61 | # 62 | # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode 63 | # (default is canonical extended JSON) 64 | # 65 | # @return [ Hash ] The extended json representation. 66 | def as_extended_json(**options) 67 | { "$undefined" => true } 68 | end 69 | 70 | # Register this type when the module is loaded. 71 | # 72 | # @since 2.0.0 73 | Registry.register(BSON_TYPE, self) 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /lib/bson/vector.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (C) 2025-present MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | module BSON 18 | # Vector of numbers along with metadata for binary interoperability. 19 | class Vector < ::Array 20 | # @return [ Integer ] The data type stored in the vector. 21 | attr_reader :dtype 22 | 23 | # @return [ Integer ] The number of bits in the final byte that are to 24 | # be ignored when a vector element's size is less than a byte 25 | # and the length of the vector is not a multiple of 8. 26 | attr_reader :padding 27 | 28 | # @return [ BSON::ByteBuffer ] The data in the vector. 29 | def data 30 | self 31 | end 32 | 33 | # @param [ ::Array ] data The data to initialize the vector with. 34 | # @param [ Integer ] dtype The data type of the vector. 35 | # @param [ Integer ] padding The number of bits in the final byte that are to 36 | # be ignored when a vector element's size is less than a byte 37 | # and the length of the vector is not a multiple of 8. 38 | def initialize(data, dtype, padding = 0) 39 | @dtype = dtype 40 | @padding = padding 41 | super(data.dup) 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/bson/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # rubocop:todo all 3 | 4 | # Copyright (C) 2009-2020 MongoDB Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | module BSON 19 | VERSION = "5.0.2" 20 | end 21 | -------------------------------------------------------------------------------- /perf/README.md: -------------------------------------------------------------------------------- 1 | Performance Notes 2 | ================= 3 | 4 | Pending 5 | ------- 6 | 7 | - codepoints and getbyte, setbyte, << for BSON_TYPE 8 | - string.force_encoding("UTF-8").valid_encoding? 9 | 10 | Top concerns 11 | ------------ 12 | 13 | - String - pure - cext@ - Tyler has experience, says UTF8 (with LATIN1 subset) is sufficient, note keys need special char check 14 | - to_utf8_binary@ 15 | - to_bson_string@ 16 | - to_bson_cstring@ 17 | - append_bson_int32 18 | - to_bson_key - rb_string_to_bson_key 19 | - check_for_illegal_characters 20 | - encode 21 | - set_int32 22 | - force_encoding 23 | - to_bson 24 | - Symbol 25 | - to_bson 26 | - to_bson_key 27 | - rb_symbol_to_bson_key 28 | - Binary 29 | - rb_binary_to_bson 30 | - Integer - sizing done twice for serialization - bson_type and to_bson 31 | - discarded as not worthy 32 | - new_hash_to_bson_hint 33 | - new_hash_to_bson_integer 34 | - Array to_bson - repeat above 35 | 36 | TODO: Review 37 | ------------ 38 | 39 | - key optimization 40 | - note threading concerns 41 | - no safety limit needed for non-pathological use (review this) 42 | - symbol ~ gain: 0.25 (36 --> 27) Xeon, gain: 0.34 (41 --> 27) Core 2 43 | - string ~ gain: 0.15 (33 --> 28) Xeon, gain: 0.24 (39 --> 29) Core 2 44 | - with safety limit, mutex overhead eats up the benefit on Xeon 45 | - symbol ~ gain: 0.15 (41 --> 35) Core 2 46 | - string ~ gain: 0.05 (39 --> 37) Core 2 47 | - rb_float_to_bson ~ gain: 0.61 (15 --> 6, allocated: 3 --> 1) Core 2 48 | 49 | Performance gains 50 | ----------------- 51 | 52 | - catalog, extract techniques, tech talk 53 | 54 | Driver notes 55 | ------------ 56 | 57 | Check for initial '$' or inclusion of '.' is purposely left to the driver. 58 | See bench.rb: test_string_to_bson_key_mongodb 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /perf/Rakefile: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2013 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require 'json' 17 | 18 | # test framework for C extension versus pure Ruby 19 | # (consider ENV for C versus C) 20 | DEFAULT_TARGET = 'test_ext_rb_float_to_bson' 21 | NON_ZERO_TIME = 0.0000000001 # 10^-10 22 | 23 | def sh_eval(command) 24 | puts command 25 | text = `#{command}` 26 | lines = text.split("\n") 27 | ruby_on = lines.grep(/^[^.#\w]/).first 28 | if lines.grep(/Error/).first 29 | print text 30 | raise "sh_eval error" 31 | elsif ruby_on 32 | eval lines.grep(/^[^.#\w]/).first 33 | else 34 | raise "no Ruby data - check the TARGET" 35 | end 36 | end 37 | 38 | def hash_f(hash, precision) 39 | hash.each_pair do |key, value| 40 | hash[key] = "%.#{precision}f" % value if value.kind_of?(Float) 41 | end 42 | end 43 | 44 | def print_gain(measurement) 45 | measurement = measurement.collect do |h| 46 | h[:allocated] = h[:allocated] / h[:count] 47 | h[:label] = "\"#{h[:label]}\"" 48 | h.select{|k,v| [:label, :utime, :real, :allocated].include?(k)} 49 | end 50 | gain = 1.0 - measurement[1][:utime]/(measurement[0][:utime] + NON_ZERO_TIME) 51 | measurement.each do |t| 52 | puts hash_f(t, 1).each_pair.collect{|key, value| "#{key}: #{value}" }.join(', ') 53 | end 54 | puts "gain: #{'%.2f' % gain}" 55 | end 56 | 57 | $measurement = [] 58 | 59 | task :default do 60 | TARGET = ENV['TARGET'] || DEFAULT_TARGET 61 | $measurement = [] 62 | [:clean, :compile].each do |t| 63 | Rake::Task[t].execute 64 | Rake::Task[:test].execute 65 | end 66 | print_gain($measurement) 67 | end 68 | 69 | task :clean do 70 | sh "(cd .. && rake clean)" 71 | end 72 | 73 | task :compile do 74 | sh "(cd .. && rake compile)" 75 | end 76 | 77 | task :test do 78 | $measurement << sh_eval("ruby bench_test.rb --name #{TARGET}") 79 | end 80 | -------------------------------------------------------------------------------- /sbom.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "timestamp": "2024-06-12T07:06:17.526721+00:00", 4 | "tools": [ 5 | { 6 | "externalReferences": [ 7 | { 8 | "type": "build-system", 9 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" 10 | }, 11 | { 12 | "type": "distribution", 13 | "url": "https://pypi.org/project/cyclonedx-python-lib/" 14 | }, 15 | { 16 | "type": "documentation", 17 | "url": "https://cyclonedx-python-library.readthedocs.io/" 18 | }, 19 | { 20 | "type": "issue-tracker", 21 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" 22 | }, 23 | { 24 | "type": "license", 25 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" 26 | }, 27 | { 28 | "type": "release-notes", 29 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" 30 | }, 31 | { 32 | "type": "vcs", 33 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib" 34 | }, 35 | { 36 | "type": "website", 37 | "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" 38 | } 39 | ], 40 | "name": "cyclonedx-python-lib", 41 | "vendor": "CycloneDX", 42 | "version": "6.4.4" 43 | } 44 | ] 45 | }, 46 | "serialNumber": "urn:uuid:555775d6-266f-4f33-84a0-e8ab6508d27e", 47 | "version": 1, 48 | "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", 49 | "bomFormat": "CycloneDX", 50 | "specVersion": "1.5" 51 | } 52 | -------------------------------------------------------------------------------- /spec/README.md: -------------------------------------------------------------------------------- 1 | # Running BSON Ruby Tests 2 | 3 | ## Quick Start 4 | 5 | The test suite requires shared tooling that is stored in a separate repository 6 | and is referenced as a submodule. After checking out the desired bson-ruby 7 | branch, check out the matching submodules: 8 | 9 | git submodule init 10 | git submodule update 11 | 12 | Then, to run the test suite: 13 | 14 | rake 15 | -------------------------------------------------------------------------------- /spec/bson/boolean_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Boolean do 19 | 20 | describe "::BSON_TYPE" do 21 | 22 | it "returns 8" do 23 | expect(BSON::Boolean::BSON_TYPE).to eq(8.chr) 24 | end 25 | end 26 | 27 | describe "#from_bson" do 28 | 29 | let(:type) { 8.chr } 30 | 31 | it_behaves_like "a bson element" 32 | 33 | context "when the boolean is true" do 34 | 35 | let(:obj) { true } 36 | let(:bson) { 1.chr } 37 | 38 | it_behaves_like "a deserializable bson element" 39 | end 40 | 41 | context "when the boolean is false" do 42 | 43 | let(:obj) { false } 44 | let(:bson) { 0.chr } 45 | 46 | it_behaves_like "a deserializable bson element" 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/bson/code_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Code do 19 | 20 | describe "#as_extended_json" do 21 | 22 | let(:object) do 23 | described_class.new("this.value = 5") 24 | end 25 | 26 | it "returns the binary data plus type" do 27 | expect(object.as_extended_json).to eq({ "$code" => "this.value = 5" }) 28 | end 29 | 30 | it_behaves_like 'an Extended JSON serializable object' 31 | it_behaves_like '#as_json calls #as_extended_json' 32 | end 33 | 34 | describe "#to_bson/#from_bson" do 35 | 36 | let(:type) { 13.chr } 37 | let(:obj) { described_class.new("this.value = 5") } 38 | let(:bson) { "#{15.to_bson}this.value = 5#{BSON::NULL_BYTE}" } 39 | 40 | it_behaves_like "a bson element" 41 | it_behaves_like "a serializable bson element" 42 | it_behaves_like "a deserializable bson element" 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/bson/code_with_scope_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::CodeWithScope do 19 | 20 | describe "#==" do 21 | 22 | let(:object) do 23 | BSON::CodeWithScope.new("this.value = val", "test") 24 | end 25 | 26 | context "when the objects are equal" do 27 | let(:other) { described_class.new("this.value = val", "test") } 28 | 29 | it "returns true" do 30 | expect(object).to eq(other) 31 | end 32 | end 33 | 34 | context "when the other object is not equal" do 35 | let(:other) { described_class.new("this.value = otherVal", "test") } 36 | 37 | it "returns false" do 38 | expect(object).to_not eq(other) 39 | end 40 | end 41 | end 42 | 43 | describe "#as_extended_json" do 44 | 45 | let(:object) do 46 | described_class.new("this.value = val", :val => "test") 47 | end 48 | 49 | it "returns the binary data plus type" do 50 | expect(object.as_extended_json).to eq( 51 | { "$code" => "this.value = val", "$scope" => { :val => "test" }} 52 | ) 53 | end 54 | 55 | it_behaves_like 'an Extended JSON serializable object' 56 | it_behaves_like '#as_json calls #as_extended_json' 57 | end 58 | 59 | describe "#to_bson" do 60 | 61 | let(:type) { 15.chr } 62 | let(:code) { "this.value == name" } 63 | let(:scope) do 64 | { :name => "test" } 65 | end 66 | let(:obj) { described_class.new(code, scope) } 67 | let(:bson) do 68 | "#{47.to_bson.to_s}#{(code.length + 1).to_bson.to_s}#{code}#{BSON::NULL_BYTE}" + 69 | "#{scope.to_bson.to_s}" 70 | end 71 | 72 | it_behaves_like "a bson element" 73 | it_behaves_like "a serializable bson element" 74 | end 75 | 76 | describe "#from_bson" do 77 | 78 | let(:type) { 15.chr } 79 | let(:code) { "this.value == name" } 80 | let(:scope) do 81 | { "name" => "test" } 82 | end 83 | let(:obj) { described_class.new(code, scope) } 84 | let(:bson) { BSON::ByteBuffer.new(obj.to_bson.to_s) } 85 | let!(:deserialized) { described_class.from_bson(bson) } 86 | 87 | it "deserializes the javascript" do 88 | expect(deserialized.javascript).to eq(code) 89 | end 90 | 91 | it "deserializes the scope" do 92 | expect(deserialized.scope).to eq(scope) 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /spec/bson/config_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | require "spec_helper" 3 | 4 | describe BSON::Config do 5 | 6 | end 7 | -------------------------------------------------------------------------------- /spec/bson/date_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe Date do 19 | 20 | it_behaves_like "a class which converts to Time" 21 | 22 | describe "#to_bson" do 23 | 24 | context "when the date is post epoch" do 25 | 26 | let(:obj) { Date.new(2012, 1, 1) } 27 | let(:time) { Time.utc(2012, 1, 1) } 28 | let(:bson) { [ (time.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 29 | 30 | it_behaves_like "a serializable bson element" 31 | end 32 | 33 | context "when the date is pre epoch" do 34 | 35 | let(:obj) { Date.new(1969, 1, 1) } 36 | let(:time) { Time.utc(1969, 1, 1) } 37 | let(:bson) { [ (time.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 38 | 39 | it_behaves_like "a serializable bson element" 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/bson/date_time_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe DateTime do 19 | 20 | it_behaves_like "a class which converts to Time" 21 | 22 | describe "#to_bson" do 23 | 24 | context "when the date time is post epoch" do 25 | 26 | let(:obj) { DateTime.new(2012, 1, 1, 0, 0, 0) } 27 | let(:bson) { [ (obj.to_time.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 28 | 29 | it_behaves_like "a serializable bson element" 30 | end 31 | 32 | context "when the date time is pre epoch" do 33 | 34 | let(:obj) { DateTime.new(1969, 1, 1, 0, 0, 0) } 35 | let(:bson) { [ (obj.to_time.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 36 | 37 | it_behaves_like "a serializable bson element" 38 | end 39 | 40 | context "when the dates don't both use Gregorian" do 41 | 42 | let(:shakespeare_datetime) do 43 | DateTime.iso8601('1616-04-23', Date::ENGLAND) 44 | end 45 | 46 | let(:gregorian_datetime) do 47 | DateTime.iso8601('1616-04-23', Date::GREGORIAN) 48 | end 49 | 50 | context "when putting to bson" do 51 | 52 | let(:shakespeare) do 53 | { a: shakespeare_datetime }.to_bson 54 | end 55 | 56 | let(:gregorian) do 57 | { a: gregorian_datetime }.to_bson 58 | end 59 | 60 | it "does not equal each other" do 61 | expect(shakespeare.to_s).to_not eq(gregorian.to_s) 62 | end 63 | 64 | it "the english date is 10 days later" do 65 | expect(shakespeare.to_s).to eq({ a: DateTime.iso8601('1616-05-03', Date::GREGORIAN) }.to_bson.to_s) 66 | end 67 | end 68 | 69 | context "when putting and receiving from bson" do 70 | 71 | let(:shakespeare) do 72 | Hash.from_bson(BSON::ByteBuffer.new({ a: shakespeare_datetime }.to_bson.to_s)) 73 | end 74 | 75 | let(:gregorian) do 76 | Hash.from_bson(BSON::ByteBuffer.new({ a: gregorian_datetime }.to_bson.to_s)) 77 | end 78 | 79 | it "does not equal each other" do 80 | expect(shakespeare).to_not eq(gregorian) 81 | end 82 | 83 | it "the english date is 10 days later" do 84 | expect(shakespeare[:a]).to eq(DateTime.iso8601('1616-05-03', Date::GREGORIAN).to_time) 85 | end 86 | 87 | it "the gregorian date is the same" do 88 | expect(gregorian[:a]).to eq(DateTime.iso8601('1616-04-23', Date::GREGORIAN).to_time) 89 | end 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /spec/bson/document_as_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2021 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | # BSON::Document ActiveSupport extensions 19 | describe BSON::Document do 20 | require_active_support 21 | 22 | describe '#symbolize_keys' do 23 | context 'string keys' do 24 | let(:doc) do 25 | described_class.new('foo' => 'bar') 26 | end 27 | 28 | it 'works correctly' do 29 | doc.symbolize_keys.should == {foo: 'bar'} 30 | end 31 | end 32 | end 33 | 34 | describe '#symbolize_keys!' do 35 | context 'string keys' do 36 | let(:doc) do 37 | described_class.new('foo' => 'bar') 38 | end 39 | 40 | it 'raises ArgumentError' do 41 | lambda do 42 | doc.symbolize_keys! 43 | end.should raise_error(ArgumentError, /symbolize_keys! is not supported on BSON::Document instances/) 44 | end 45 | end 46 | end 47 | 48 | describe '#deep_symbolize_keys!' do 49 | context 'string keys' do 50 | let(:doc) do 51 | described_class.new('foo' => 'bar') 52 | end 53 | 54 | it 'raises ArgumentError' do 55 | expect do 56 | doc.deep_symbolize_keys! 57 | end.to output(/\[DEPRECATION\] `deep_symbolize_keys!` is not supported on BSON::Document instances./).to_stderr 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/bson/false_class_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe FalseClass do 19 | 20 | describe "#to_bson" do 21 | 22 | let(:obj) { false } 23 | let(:bson) { 0.chr } 24 | let(:type) { 8.chr } 25 | 26 | it_behaves_like "a bson element" 27 | it_behaves_like "a serializable bson element" 28 | end 29 | 30 | describe '#as_extended_json' do 31 | let(:object) { false } 32 | 33 | it_behaves_like '#as_extended_json returns self' 34 | it_behaves_like 'an Extended JSON serializable object' 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/bson/float_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe Float do 19 | 20 | describe "#to_bson/#from_bson" do 21 | 22 | let(:type) { 1.chr } 23 | let(:obj) { 1.2332 } 24 | let(:bson) { [ obj ].pack(Float::PACK) } 25 | 26 | it_behaves_like "a bson element" 27 | it_behaves_like "a serializable bson element" 28 | it_behaves_like "a deserializable bson element" 29 | end 30 | 31 | describe '#to_json' do 32 | it 'returns float' do 33 | 42.0.to_json.should == '42.0' 34 | end 35 | end 36 | 37 | describe '#as_extended_json' do 38 | let(:object) { 42.0 } 39 | 40 | context 'canonical mode' do 41 | it 'returns $numberDouble' do 42 | object.as_extended_json.should == {'$numberDouble' => '42.0'} 43 | end 44 | end 45 | 46 | context 'relaxed mode' do 47 | let(:serialized) do 48 | object.as_extended_json(mode: :relaxed) 49 | end 50 | 51 | it 'returns float' do 52 | serialized.should be_a(Float) 53 | serialized.should be_within(0.00001).of(42) 54 | end 55 | end 56 | 57 | context 'legacy mode' do 58 | let(:serialized) do 59 | object.as_extended_json(mode: :legacy) 60 | end 61 | 62 | it 'returns float' do 63 | serialized.should be_a(Float) 64 | serialized.should be_within(0.00001).of(42) 65 | end 66 | end 67 | 68 | it_behaves_like "an Extended JSON serializable object" 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/bson/hash_as_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2021 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe 'Hash ActiveSupport extensions' do 19 | require_active_support 20 | 21 | describe '#symbolize_keys' do 22 | let(:symbolized) { hash.symbolize_keys } 23 | 24 | shared_examples 'works correctly' do 25 | it 'returns a hash' do 26 | symbolized.class.should be Hash 27 | end 28 | 29 | it 'works correctly' do 30 | hash.symbolize_keys.should == {foo: 'bar'} 31 | end 32 | end 33 | 34 | context 'string keys' do 35 | let(:hash) do 36 | {'foo' => 'bar'} 37 | end 38 | 39 | include_examples 'works correctly' 40 | end 41 | 42 | context 'symbol keys' do 43 | let(:hash) do 44 | {foo: 'bar'} 45 | end 46 | 47 | include_examples 'works correctly' 48 | end 49 | 50 | context 'both string and symbol keys' do 51 | let(:hash) do 52 | {'foo' => 42, foo: 'bar'} 53 | end 54 | 55 | include_examples 'works correctly' 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/bson/json_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::JSON do 19 | 20 | describe "#to_json" do 21 | 22 | let(:klass) do 23 | Class.new do 24 | include BSON::JSON 25 | 26 | def as_json(*args) 27 | { :test => "value" } 28 | end 29 | end 30 | end 31 | 32 | context "when provided no arguments" do 33 | 34 | let(:json) do 35 | klass.new.to_json 36 | end 37 | 38 | it "returns the object as json" do 39 | expect(json).to eq("{\"test\":\"value\"}") 40 | end 41 | end 42 | 43 | context "when provided arguments" do 44 | 45 | let(:json) do 46 | klass.new.to_json(:test) 47 | end 48 | 49 | it "returns the object as json" do 50 | expect(json).to eq("{\"test\":\"value\"}") 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/bson/max_key_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::MaxKey do 19 | 20 | describe "#==" do 21 | 22 | context "when the objects are equal" do 23 | 24 | let(:other) { described_class.new } 25 | 26 | it "returns true" do 27 | expect(subject).to eq(other) 28 | end 29 | end 30 | 31 | context "when the other object is not a max_key" do 32 | 33 | it "returns false" do 34 | expect(subject).to_not eq("test") 35 | end 36 | end 37 | end 38 | 39 | describe "#>" do 40 | 41 | it "always returns true" do 42 | expect(subject > Integer::MAX_64BIT).to be true 43 | end 44 | end 45 | 46 | describe "#<" do 47 | 48 | it "always returns false" do 49 | expect(subject < Integer::MAX_64BIT).to be false 50 | end 51 | end 52 | 53 | describe "#as_extended_json" do 54 | 55 | let(:object) do 56 | described_class.new 57 | end 58 | 59 | it "returns the binary data plus type" do 60 | expect(object.as_extended_json).to eq({ "$maxKey" => 1 }) 61 | end 62 | 63 | it_behaves_like 'an Extended JSON serializable object' 64 | it_behaves_like '#as_json calls #as_extended_json' 65 | end 66 | 67 | describe "#to_bson/#from_bson" do 68 | 69 | let(:type) { 127.chr } 70 | let(:obj) { described_class.new } 71 | let(:bson) { BSON::NO_VALUE } 72 | 73 | it_behaves_like "a bson element" 74 | it_behaves_like "a serializable bson element" 75 | it_behaves_like "a deserializable bson element" 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/bson/min_key_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::MinKey do 19 | 20 | describe "#as_extended_json" do 21 | 22 | let(:object) do 23 | described_class.new 24 | end 25 | 26 | it "returns the binary data plus type" do 27 | expect(object.as_extended_json).to eq({ "$minKey" => 1 }) 28 | end 29 | 30 | it_behaves_like 'an Extended JSON serializable object' 31 | it_behaves_like '#as_json calls #as_extended_json' 32 | end 33 | 34 | describe "#==" do 35 | 36 | context "when the objects are equal" do 37 | 38 | let(:other) { described_class.new } 39 | 40 | it "returns true" do 41 | expect(subject).to eq(other) 42 | end 43 | end 44 | 45 | context "when the other object is not a max_key" do 46 | 47 | it "returns false" do 48 | expect(subject).to_not eq("test") 49 | end 50 | end 51 | end 52 | 53 | describe "#>" do 54 | 55 | it "always returns false" do 56 | expect(subject > Integer::MAX_64BIT).to be false 57 | end 58 | end 59 | 60 | describe "#<" do 61 | 62 | it "always returns true" do 63 | expect(subject < Integer::MAX_64BIT).to be true 64 | end 65 | end 66 | 67 | describe "#to_bson/#from_bson" do 68 | 69 | let(:type) { 255.chr } 70 | let(:obj) { described_class.new } 71 | let(:bson) { BSON::NO_VALUE } 72 | 73 | it_behaves_like "a bson element" 74 | it_behaves_like "a serializable bson element" 75 | it_behaves_like "a deserializable bson element" 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/bson/nil_class_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe NilClass do 19 | 20 | describe "#to_bson/#from_bson" do 21 | 22 | let(:type) { 10.chr } 23 | let(:obj) { nil } 24 | let(:bson) { BSON::NO_VALUE } 25 | 26 | it_behaves_like "a bson element" 27 | it_behaves_like "a serializable bson element" 28 | it_behaves_like "a deserializable bson element" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/bson/object_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Object do 19 | 20 | describe "#to_bson_key" do 21 | 22 | let(:object) do 23 | 1..3 24 | end 25 | 26 | it "raises an exception" do 27 | expect { 28 | object.to_bson_key 29 | }.to raise_error(BSON::Error::InvalidKey) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/bson/open_struct_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2016-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe OpenStruct do 19 | 20 | describe "#to_bson" do 21 | 22 | let(:type) { 3.chr } 23 | 24 | it_behaves_like "a bson element" 25 | 26 | context "when the struct is a single level" do 27 | 28 | let(:obj) do 29 | described_class.new({"key" => "value" }) 30 | end 31 | 32 | let(:bson) do 33 | "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" + 34 | "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}" 35 | end 36 | 37 | it_behaves_like "a serializable bson element" 38 | end 39 | 40 | context "when the struct has dollar keys" do 41 | 42 | let(:obj) do 43 | described_class.new({ "$testing" => "value" }) 44 | end 45 | 46 | let(:bson) do 47 | "#{25.to_bson.to_s}#{String::BSON_TYPE}$testing#{BSON::NULL_BYTE}" + 48 | "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}" 49 | end 50 | 51 | it "serializes the struct" do 52 | expect(obj.to_bson.to_s).to eq(bson) 53 | end 54 | 55 | context "when the struct contains an array of documents containing invalid keys" do 56 | 57 | let(:obj) do 58 | described_class.new({ "array" => [{ "$testing" => "value" }] }) 59 | end 60 | 61 | let(:bson) do 62 | "#{45.to_bson.to_s}#{Array::BSON_TYPE}array#{BSON::NULL_BYTE}" + 63 | "#{[{ "$testing" => "value" }].to_bson.to_s}#{BSON::NULL_BYTE}" 64 | end 65 | 66 | it "serializes the struct" do 67 | expect(obj.to_bson.to_s).to eq(bson) 68 | end 69 | end 70 | end 71 | 72 | context "when the struct is embedded" do 73 | 74 | let(:obj) do 75 | described_class.new({ "field" => OpenStruct.new({ "key" => "value" })}) 76 | end 77 | 78 | let(:bson) do 79 | "#{32.to_bson.to_s}#{Hash::BSON_TYPE}field#{BSON::NULL_BYTE}" + 80 | "#{20.to_bson.to_s}#{String::BSON_TYPE}key#{BSON::NULL_BYTE}" + 81 | "#{6.to_bson.to_s}value#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}#{BSON::NULL_BYTE}" 82 | end 83 | 84 | it_behaves_like "a serializable bson element" 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/bson/registry_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Registry do 19 | 20 | describe ".get" do 21 | 22 | context "when the type has a correspoding class" do 23 | 24 | before do 25 | described_class.register(BSON::MinKey::BSON_TYPE, BSON::MinKey) 26 | end 27 | 28 | let(:klass) do 29 | described_class.get(BSON::MinKey::BSON_TYPE, "field") 30 | end 31 | 32 | it "returns the class" do 33 | expect(klass).to eq(BSON::MinKey) 34 | end 35 | end 36 | 37 | context "when the type has no corresponding class" do 38 | 39 | it "raises an error" do 40 | expect { 41 | described_class.get(25.chr, "field") 42 | }.to raise_error(BSON::Error::UnsupportedType) 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/bson/string_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | 3 | # Copyright (C) 2009-2020 MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require "spec_helper" 18 | 19 | describe String do 20 | 21 | describe "#to_bson/#from_bson" do 22 | 23 | let(:type) { 2.chr } 24 | let(:obj) { "test" } 25 | let(:bson) { "#{5.to_bson.to_s}test#{BSON::NULL_BYTE}" } 26 | 27 | it_behaves_like "a bson element" 28 | it_behaves_like "a serializable bson element" 29 | it_behaves_like "a deserializable bson element" 30 | end 31 | 32 | describe "#to_bson_object_id" do 33 | 34 | context "when the string has 12 characters" do 35 | 36 | let(:string) do 37 | "123456789012" 38 | end 39 | 40 | let(:converted) do 41 | string.to_bson_object_id 42 | end 43 | 44 | it "returns the array as a string" do 45 | expect(converted).to eq(string) 46 | end 47 | end 48 | 49 | context "when the array does not have 12 elements" do 50 | 51 | it "raises an exception" do 52 | expect { 53 | "test".to_bson_object_id 54 | }.to raise_error(BSON::Error::InvalidObjectId) 55 | end 56 | end 57 | end 58 | 59 | context "when the class is loaded" do 60 | 61 | let(:registered) do 62 | BSON::Registry.get(String::BSON_TYPE, 'field') 63 | end 64 | 65 | it "registers the type" do 66 | expect(registered).to eq(String) 67 | end 68 | end 69 | 70 | describe "#to_bson_key" do 71 | 72 | let(:string) { "test" } 73 | let(:encoded) { string.to_s } 74 | 75 | it "returns the encoded string" do 76 | expect(string.to_bson_key).to eq(encoded) 77 | end 78 | end 79 | 80 | describe "#to_hex_string" do 81 | 82 | let(:string) do 83 | "testing123" 84 | end 85 | 86 | it "converts the string to hex" do 87 | expect(string.to_hex_string).to eq("74657374696e67313233") 88 | end 89 | end 90 | 91 | describe "#to_bson_key" do 92 | 93 | let(:string) do 94 | "$testing.testing" 95 | end 96 | 97 | it "allows dots/dollars keys" do 98 | expect(string.to_bson_key).to eq(string) 99 | end 100 | end 101 | 102 | describe '#to_bson' do 103 | context 'when string is not valid utf-8' do 104 | let(:string) do 105 | "\xfe\x00\xff".force_encoding('BINARY') 106 | end 107 | 108 | let(:expected_message) do 109 | /from ASCII-8BIT to UTF-8/ 110 | end 111 | 112 | it 'raises EncodingError' do 113 | expect do 114 | string.to_bson 115 | end.to raise_error(EncodingError, expected_message) 116 | end 117 | end 118 | end 119 | 120 | describe '#as_extended_json' do 121 | let(:object) { 'Hello world!' } 122 | 123 | it_behaves_like '#as_extended_json returns self' 124 | it_behaves_like 'an Extended JSON serializable object' 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /spec/bson/symbol_raw_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Symbol::Raw do 19 | describe '#==' do 20 | let(:one) { described_class.new('foo') } 21 | let(:two) { described_class.new('foo') } 22 | let(:three) { described_class.new('bar') } 23 | 24 | it 'compares equal' do 25 | one.should == two 26 | end 27 | 28 | it 'compares not equal' do 29 | one.should_not == three 30 | end 31 | end 32 | 33 | describe '#eql?' do 34 | let(:one) { described_class.new('foo') } 35 | let(:two) { described_class.new('foo') } 36 | let(:three) { described_class.new('bar') } 37 | 38 | it 'compares equal' do 39 | one.should be_eql(two) 40 | end 41 | 42 | it 'compares not equal' do 43 | one.should_not be_eql(three) 44 | end 45 | end 46 | 47 | describe '#as_json' do 48 | let(:object) do 49 | described_class.new(:foobar) 50 | end 51 | 52 | it 'returns a string' do 53 | expect(object.as_json).to eq('foobar') 54 | end 55 | 56 | it_behaves_like 'a JSON serializable object' 57 | end 58 | 59 | describe '#as_extended_json' do 60 | let(:object) do 61 | described_class.new(:foobar) 62 | end 63 | 64 | it 'returns the binary data plus type' do 65 | expect(object.as_extended_json).to eq({ '$symbol' => 'foobar' }) 66 | end 67 | 68 | it_behaves_like 'an Extended JSON serializable object' 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/bson/time_with_zone_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2018-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | context 'when ActiveSupport support is enabled' do 19 | before do 20 | unless SpecConfig.instance.active_support? 21 | skip "ActiveSupport support is not enabled" 22 | end 23 | end 24 | 25 | describe 'ActiveSupport::TimeWithZone' do 26 | let(:cls) { ActiveSupport::TimeWithZone } 27 | 28 | it "shares BSON type with Time" do 29 | # ActiveSupport::TimeWithZone#new has no 0-argument version 30 | obj = Time.now.in_time_zone("UTC") 31 | expect(obj.bson_type).to eq(Time::BSON_TYPE) 32 | end 33 | 34 | shared_examples_for 'deserializes as expected' do 35 | it 'deserializes to UTC' do 36 | # Time zone information is lost during serialization - the time 37 | # is always serialized in UTC. 38 | rt_obj = Time.from_bson(obj.to_bson) 39 | expect(rt_obj.zone).to eq('UTC') 40 | end 41 | 42 | it 'deserializes to an equal object' do 43 | rt_obj = Time.from_bson(obj.to_bson) 44 | expect(rt_obj).to eq(obj) 45 | end 46 | end 47 | 48 | describe "#to_bson" do 49 | 50 | context "when the TimeWithZone is not in UTC" do 51 | 52 | let(:obj) { Time.utc(2012, 12, 12, 0, 0, 0).in_time_zone("Pacific Time (US & Canada)") } 53 | let(:bson) { [ (obj.utc.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 54 | 55 | it_behaves_like "a serializable bson element" 56 | it_behaves_like 'deserializes as expected' 57 | end 58 | 59 | context "when the TimeWithZone is in UTC" do 60 | 61 | let(:obj) { Time.utc(2012, 1, 1, 0, 0, 0).in_time_zone("UTC") } 62 | let(:bson) { [ (obj.utc.to_f * 1000).to_i ].pack(BSON::Int64::PACK) } 63 | 64 | it_behaves_like "a serializable bson element" 65 | it_behaves_like 'deserializes as expected' 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/bson/true_class_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe TrueClass do 19 | 20 | describe "#to_bson" do 21 | 22 | let(:obj) { true } 23 | let(:bson) { 1.chr } 24 | let(:type) { 8.chr } 25 | 26 | it_behaves_like "a bson element" 27 | it_behaves_like "a serializable bson element" 28 | end 29 | 30 | describe '#as_extended_json' do 31 | let(:object) { true } 32 | 33 | it_behaves_like '#as_extended_json returns self' 34 | it_behaves_like 'an Extended JSON serializable object' 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/bson/undefined_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON::Undefined do 19 | 20 | describe "#to_bson/#from_bson" do 21 | 22 | let(:type) { 6.chr } 23 | let(:obj) { described_class.new } 24 | let(:bson) { BSON::NO_VALUE } 25 | 26 | it_behaves_like "a bson element" 27 | it_behaves_like "a serializable bson element" 28 | it_behaves_like "a deserializable bson element" 29 | end 30 | 31 | describe "#as_json" do 32 | 33 | let(:object) do 34 | described_class.new 35 | end 36 | 37 | it "returns nil" do 38 | expect(object.as_json).to eq(nil) 39 | end 40 | 41 | it_behaves_like 'a JSON serializable object' 42 | end 43 | 44 | describe "#as_extended_json" do 45 | 46 | let(:object) do 47 | described_class.new 48 | end 49 | 50 | it "returns the binary data plus type" do 51 | expect(object.as_extended_json).to eq({ "$undefined" => true }) 52 | end 53 | 54 | it_behaves_like 'an Extended JSON serializable object' 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/bson/vector_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright (C) 2025-present MongoDB Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | require 'spec_helper' 18 | 19 | describe BSON::Vector do 20 | it 'behaves like an Array' do 21 | expect(described_class.new([ 1, 2, 3 ], :int8)).to be_a(Array) 22 | end 23 | 24 | describe '#initialize' do 25 | context 'when padding is not provided' do 26 | let(:vector) { described_class.new([ 1, 2, 3 ], :int8) } 27 | 28 | it 'sets the padding to 0' do 29 | expect(vector.padding).to eq(0) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/bson_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | require "spec_helper" 17 | 18 | describe BSON do 19 | 20 | describe ".ObjectId" do 21 | 22 | let(:string) { "4e4d66343b39b68407000001" } 23 | 24 | it "returns an BSON::ObjectId from given string" do 25 | expect(described_class::ObjectId(string)).to be_a BSON::ObjectId 26 | expect(described_class::ObjectId(string)).to eq BSON::ObjectId.from_string(string) 27 | end 28 | end 29 | 30 | describe "::BINARY" do 31 | 32 | it "returns BINARY" do 33 | expect(BSON::BINARY).to eq("BINARY") 34 | end 35 | end 36 | 37 | describe "::NO_VAUE" do 38 | 39 | it "returns an empty string" do 40 | expect(BSON::NO_VALUE).to be_empty 41 | end 42 | end 43 | 44 | describe "::NULL_BYTE" do 45 | 46 | it "returns the char 0x00" do 47 | expect(BSON::NULL_BYTE).to eq(0.chr) 48 | end 49 | end 50 | 51 | describe "::UTF8" do 52 | 53 | it "returns UTF-8" do 54 | expect(BSON::UTF8).to eq("UTF-8") 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/runners/binary_vector.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'runners/common_driver' 4 | 5 | module BSON 6 | module BinaryVector 7 | class Spec < CommonDriver::Spec 8 | def initialize(file) 9 | super 10 | @valid = @invalid = nil 11 | end 12 | 13 | def tests 14 | @spec['tests'].collect do |test| 15 | BSON::BinaryVector::Test.new(self, test) 16 | end 17 | end 18 | 19 | def valid_tests 20 | tests.select(&:valid?) 21 | end 22 | 23 | def invalid_tests 24 | tests.reject(&:valid?) 25 | end 26 | end 27 | 28 | class Test 29 | attr_reader :canonical_bson, :description, :dtype, :padding, :vector 30 | 31 | def initialize(spec, test) 32 | @spec = spec 33 | @description = test['description'] 34 | @valid = test['valid'] 35 | @vector = ExtJSON.parse_obj(test['vector']) 36 | @dtype_hex = test['dtype_hex'] 37 | @dtype_alias = test['dtype_alias'] 38 | @dtype = @dtype_alias.downcase.to_sym 39 | @padding = test['padding'] 40 | @canonical_bson = test['canonical_bson'] 41 | end 42 | 43 | def valid? 44 | @valid 45 | end 46 | 47 | def document_from_canonical_bson 48 | bson_bytes = decode_hex(@canonical_bson) 49 | buffer = BSON::ByteBuffer.new(bson_bytes) 50 | BSON::Document.from_bson(buffer) 51 | end 52 | 53 | def canonical_bson_from_document(use_vector_type: false, validate_vector_data: false) 54 | args = if use_vector_type 55 | [ BSON::Vector.new(@vector, @dtype, @padding) ] 56 | else 57 | [ @vector, @dtype, @padding ] 58 | end 59 | { 60 | @spec.test_key => BSON::Binary.from_vector( 61 | *args, 62 | validate_vector_data: validate_vector_data 63 | ), 64 | }.to_bson.to_s 65 | end 66 | 67 | def bson 68 | decode_hex(@canonical_bson) 69 | end 70 | 71 | private 72 | 73 | def decode_hex(obj) 74 | [ obj ].pack('H*') 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | # Copyright (C) 2009-2020 MongoDB Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | CURRENT_PATH = File.expand_path(File.dirname(__FILE__)) 17 | DRIVER_COMMON_BSON_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/decimal128/*.json").sort 18 | BSON_CORPUS_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/corpus/*.json").sort 19 | BSON_CORPUS_LEGACY_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/corpus_legacy/*.json").sort 20 | BINARY_VECTOR_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/binary_vector/*.json").sort 21 | 22 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "shared", "lib")) 23 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 24 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) 25 | 26 | require "ostruct" 27 | require "stringio" 28 | require "bson" 29 | require "json" 30 | require "rspec" 31 | require "yaml" 32 | 33 | require 'support/spec_config' 34 | 35 | if SpecConfig.instance.active_support? 36 | require "active_support/version" 37 | if ActiveSupport.version >= Gem::Version.new(7) 38 | # ActiveSupport wants us to require ALL of it all of the time. 39 | # See: https://github.com/rails/rails/issues/43851, 40 | # https://github.com/rails/rails/issues/43889, etc. 41 | require 'active_support' 42 | end 43 | require "active_support/time" 44 | require 'bson/active_support' 45 | end 46 | 47 | unless ENV['CI'] || BSON::Environment.jruby? 48 | begin 49 | require 'byebug' 50 | rescue Exception 51 | end 52 | end 53 | 54 | begin 55 | require 'mrss/lite_constraints' 56 | rescue LoadError => exc 57 | raise LoadError.new <<~MSG.strip 58 | The test suite requires shared tooling to be installed. 59 | Please refer to spec/README.md for instructions. 60 | #{exc.class}: #{exc} 61 | MSG 62 | end 63 | 64 | Dir["./spec/support/**/*.rb"].each { |file| require file } 65 | 66 | # Alternate IO class that returns a String from #readbyte. 67 | # See RUBY-898 for more information on why we need to test this. 68 | # Ruby core documentation says IO#readbyte returns a Fixnum, but 69 | # OpenSSL::SSL::SSLSocket#readbyte returns a String. 70 | class AlternateIO < StringIO 71 | 72 | # Read a byte from the stream. 73 | # 74 | # @returns [ String ] A String representation of the next byte. 75 | def readbyte 76 | super.chr 77 | end 78 | end 79 | 80 | RSpec.configure do |config| 81 | config.expect_with :rspec do |c| 82 | c.syntax = [:should, :expect] 83 | end 84 | 85 | # To ensure that calling GC.compact does not produce unexpected behavior, 86 | # randomly call GC.compact after a small percentage of tests in the suite. 87 | # This behavior is only enabled when the COMPACT environment variable is true. 88 | if SpecConfig.instance.compact? 89 | config.after do 90 | if rand < SpecConfig::COMPACTION_CHANCE 91 | GC.compact 92 | end 93 | end 94 | end 95 | 96 | config.extend Mrss::LiteConstraints 97 | end 98 | -------------------------------------------------------------------------------- /spec/spec_tests/binary_vector_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | require 'spec_helper' 3 | require 'runners/binary_vector' 4 | 5 | describe 'Binary vector tests' do 6 | specs = BINARY_VECTOR_TESTS.map { |file| BSON::BinaryVector::Spec.new(file) } 7 | skipped_tests = [ 8 | 'Overflow Vector INT8', 9 | 'Underflow Vector INT8', 10 | 'INT8 with float inputs', 11 | 'Overflow Vector PACKED_BIT', 12 | 'Underflow Vector PACKED_BIT', 13 | 'Vector with float values PACKED_BIT' 14 | ] 15 | [true, false].each do |use_vector_type| 16 | context "use vector type: #{use_vector_type}" do 17 | specs.each do |spec| 18 | context(spec.description) do 19 | spec.valid_tests.each do |test| 20 | context(test.description) do 21 | it 'encodes a document' do 22 | expect(test.canonical_bson_from_document(use_vector_type: use_vector_type)).to eq(test.bson) 23 | end 24 | 25 | it 'decodes BSON' do 26 | binary = test.document_from_canonical_bson[spec.test_key] 27 | expect(binary.type).to eq(:vector) 28 | vector = binary.as_vector 29 | expect(vector.dtype).to eq(test.dtype) 30 | expect(vector.padding).to eq(test.padding) 31 | if vector.dtype == :float32 32 | vector.each_with_index do |v, i| 33 | if v == Float::INFINITY || v == -Float::INFINITY 34 | expect(v).to eq(test.vector[i]) 35 | else 36 | expect(v).to be_within(0.00001).of(test.vector[i]) 37 | end 38 | end 39 | else 40 | expect(vector).to eq(test.vector) 41 | end 42 | end 43 | end 44 | end 45 | 46 | spec.invalid_tests.each do |test| 47 | context(test.description) do 48 | let(:subject) do 49 | if test.vector 50 | ->(vvd) { test.canonical_bson_from_document(use_vector_type: use_vector_type, validate_vector_data: vvd) } 51 | else 52 | ->() { test.document_from_canonical_bson } 53 | end 54 | end 55 | context 'with data validation' do 56 | it 'raises' do 57 | expect { 58 | subject.call(true) 59 | }.to raise_error do |err| 60 | expect([ArgumentError, BSON::Error]).to include(err.class) 61 | end 62 | end 63 | end 64 | 65 | context 'without data validation' do 66 | it 'raises' do 67 | skip 'Ruby Array.pack does not validate input' if skipped_tests.include?(test.description) 68 | 69 | expect { 70 | subject.call(false) 71 | }.to raise_error do |err| 72 | expect([ArgumentError, BSON::Error]).to include(err.class) 73 | end 74 | end 75 | end 76 | end 77 | end 78 | end 79 | end 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /spec/spec_tests/common_driver_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | require 'spec_helper' 3 | require 'runners/common_driver' 4 | 5 | describe 'Driver common bson tests' do 6 | 7 | specs = DRIVER_COMMON_BSON_TESTS.map { |file| BSON::CommonDriver::Spec.new(file) } 8 | 9 | specs.each do |spec| 10 | 11 | context(spec.description) do 12 | 13 | spec.valid_tests.each do |test| 14 | 15 | context(test.description << ' - ' << test.string) do 16 | 17 | it 'decodes the subject and displays as the correct string' do 18 | expect(test.object.to_s).to eq(test.expected_to_string) 19 | end 20 | 21 | it 'encodes the decoded object correctly (roundtrips)' do 22 | expect(test.reencoded_hex).to eq(test.subject.upcase) 23 | end 24 | 25 | it 'creates the correct object from extended json', if: test.from_ext_json? do 26 | expect(test.from_json_string).to eq(test.object) 27 | end 28 | 29 | it 'serializes to a string', if: test.to_ext_json? do 30 | expect(test.document_as_extended_json).to eq(test.ext_json) 31 | end 32 | 33 | it 'creates the correct extended json document from the decoded object', if: test.to_ext_json? do 34 | expect(test.document_as_extended_json).to eq(test.ext_json) 35 | end 36 | 37 | it 'parses the string value to the same value as the decoded document', if: test.from_string? do 38 | expect(BSON::Decimal128.new(test.string)).to eq(test.object) 39 | end 40 | 41 | it 'parses the #to_s (match_string) value to the same value as the decoded document', if: test.match_string do 42 | expect(BSON::Decimal128.new(test.match_string)).to eq(test.object) 43 | end 44 | 45 | it 'creates the correct object from a non canonical string and then prints to the correct string', if: test.match_string do 46 | expect(BSON::Decimal128.new(test.string).to_s).to eq(test.match_string) 47 | end 48 | 49 | it 'can be converted to a native type' do 50 | expect(test.native_type_conversion).to be_a(test.native_type) 51 | end 52 | end 53 | end 54 | 55 | spec.invalid_tests.each do |test| 56 | 57 | context(test.description << " - " << test.subject ) do 58 | 59 | let(:error) do 60 | ex = nil 61 | begin 62 | test.parse_invalid_string 63 | rescue => e 64 | ex = e 65 | end 66 | ex 67 | end 68 | 69 | let(:valid_errors) do 70 | [ 71 | BSON::Error::InvalidDecimal128String, 72 | BSON::Error::InvalidDecimal128Range, 73 | BSON::Error::UnrepresentablePrecision, 74 | ] 75 | end 76 | 77 | it 'raises an exception when parsing' do 78 | expect(error.class).to satisfy { |e| valid_errors.include?(e) } 79 | end 80 | end 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/spec_tests/corpus_legacy_spec.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | require 'spec_helper' 3 | require 'runners/corpus_legacy' 4 | 5 | describe 'Driver BSON Corpus Legacy spec tests' do 6 | 7 | BSON_CORPUS_LEGACY_TESTS.each do |path| 8 | basename = File.basename(path) 9 | # All of the tests in the failures subdir are failing apparently 10 | #basename = path.sub(/.*corpus-tests\//, '') 11 | 12 | spec = BSON::CorpusLegacy::Spec.new(path) 13 | 14 | context("(#{basename}): #{spec.description}") do 15 | 16 | spec.valid_tests.each do |test| 17 | 18 | context("VALID CASE: #{test.description}") do 19 | 20 | it 'roundtrips the given bson correctly' do 21 | expect(test.reencoded_bson).to eq(test.correct_bson) 22 | end 23 | 24 | context 'when the canonical bson is roundtripped', if: test.test_canonical_bson? do 25 | 26 | it 'encodes the canonical bson correctly' do 27 | expect(test.reencoded_canonical_bson).to eq(test.correct_bson) 28 | end 29 | end 30 | 31 | context 'when the document can be represented as extended json', if: test.test_extjson? do 32 | 33 | it 'decodes from the given bson, then encodes the document as extended json correctly' do 34 | expect(test.extjson_from_bson).to eq(test.correct_extjson) 35 | expect(test.extjson_from_bson[test.test_key]).to eq(test.correct_extjson[test.test_key]) 36 | end 37 | 38 | it 'decodes from extended json, then encodes the document as extended json correctly' do 39 | expect(test.extjson_from_encoded_extjson).to eq(test.correct_extjson) 40 | expect(test.extjson_from_encoded_extjson[test.test_key]).to eq(test.correct_extjson[test.test_key]) 41 | end 42 | 43 | context 'when the canonical bson can be represented as extended json', if: (test.test_canonical_bson? && test.test_extjson?) do 44 | 45 | it 'encodes the canonical bson correctly as extended json' do 46 | expect(test.extjson_from_canonical_bson).to eq(test.correct_extjson) 47 | expect(test.extjson_from_canonical_bson[test.test_key]).to eq(test.correct_extjson[test.test_key]) 48 | end 49 | end 50 | end 51 | end 52 | end 53 | 54 | spec.invalid_tests.each do |test| 55 | 56 | context("INVALID CASE: #{test.description}") do 57 | 58 | let(:error) do 59 | begin; test.reencoded_bson; false; rescue => e; e; end 60 | end 61 | 62 | it 'raises an error' do 63 | skip 'This test case does not raise and error but should' unless error 64 | expect do 65 | test.reencoded_bson 66 | end.to raise_error(error.class) 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/spec_tests/data/binary_vector/README.md: -------------------------------------------------------------------------------- 1 | # Testing Binary subtype 9: Vector 2 | 3 | The JSON files in this directory tree are platform-independent tests that drivers can use to prove their conformance to 4 | the specification. 5 | 6 | These tests focus on the roundtrip of the list of numbers as input/output, along with their data type and byte padding. 7 | 8 | Additional tests exist in `bson_corpus/tests/binary.json` but do not sufficiently test the end-to-end process of Vector 9 | to BSON. For this reason, drivers must create a bespoke test runner for the vector subtype. 10 | 11 | ## Format 12 | 13 | The test data corpus consists of a JSON file for each data type (dtype). Each file contains a number of test cases, 14 | under the top-level key "tests". Each test case pertains to a single vector. The keys provide the specification of the 15 | vector. Valid cases also include the Canonical BSON format of a document {test_key: binary}. The "test_key" is common, 16 | and specified at the top level. 17 | 18 | #### Top level keys 19 | 20 | Each JSON file contains three top-level keys. 21 | 22 | - `description`: human-readable description of what is in the file 23 | - `test_key`: name used for key when encoding/decoding a BSON document containing the single BSON Binary for the test 24 | case. Applies to *every* case. 25 | - `tests`: array of test case objects, each of which have the following keys. Valid cases will also contain additional 26 | binary and json encoding values. 27 | 28 | #### Keys of individual tests cases 29 | 30 | - `description`: string describing the test. 31 | - `valid`: boolean indicating if the vector, dtype, and padding should be considered a valid input. 32 | - `vector`: (required if valid is true) list of numbers 33 | - `dtype_hex`: string defining the data type in hex (e.g. "0x10", "0x27") 34 | - `dtype_alias`: (optional) string defining the data dtype, perhaps as Enum. 35 | - `padding`: (optional) integer for byte padding. Defaults to 0. 36 | - `canonical_bson`: (required if valid is true) an (uppercase) big-endian hex representation of a BSON byte string. 37 | 38 | ## Required tests 39 | 40 | #### To prove correct in a valid case (`valid: true`), one MUST 41 | 42 | - encode a document from the numeric values, dtype, and padding, along with the "test_key", and assert this matches the 43 | canonical_bson string. 44 | - decode the canonical_bson into its binary form, and then assert that the numeric values, dtype, and padding all match 45 | those provided in the JSON. 46 | 47 | Note: For floating point number types, exact numerical matches may not be possible. Drivers that natively support the 48 | floating-point type being tested (e.g., when testing float32 vector values in a driver that natively supports float32), 49 | MUST assert that the input float array is the same after encoding and decoding. 50 | 51 | #### To prove correct in an invalid case (`valid:false`), one MUST 52 | 53 | - if the vector field is present, raise an exception when attempting to encode a document from the numeric values, 54 | dtype, and padding. 55 | - if the canonical_bson field is present, raise an exception when attempting to deserialize it into the corresponding 56 | numeric values, as the field contains corrupted data. 57 | 58 | ## FAQ 59 | 60 | - What MongoDB Server version does this apply to? 61 | - Files in the "specifications" repository have no version scheme. They are not tied to a MongoDB server version. 62 | -------------------------------------------------------------------------------- /spec/spec_tests/data/binary_vector/float32.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Tests of Binary subtype 9, Vectors, with dtype FLOAT32", 3 | "test_key": "vector", 4 | "tests": [ 5 | { 6 | "description": "Simple Vector FLOAT32", 7 | "valid": true, 8 | "vector": [127.0, 7.0], 9 | "dtype_hex": "0x27", 10 | "dtype_alias": "FLOAT32", 11 | "padding": 0, 12 | "canonical_bson": "1C00000005766563746F72000A0000000927000000FE420000E04000" 13 | }, 14 | { 15 | "description": "Vector with decimals and negative value FLOAT32", 16 | "valid": true, 17 | "vector": [127.7, -7.7], 18 | "dtype_hex": "0x27", 19 | "dtype_alias": "FLOAT32", 20 | "padding": 0, 21 | "canonical_bson": "1C00000005766563746F72000A0000000927006666FF426666F6C000" 22 | }, 23 | { 24 | "description": "Empty Vector FLOAT32", 25 | "valid": true, 26 | "vector": [], 27 | "dtype_hex": "0x27", 28 | "dtype_alias": "FLOAT32", 29 | "padding": 0, 30 | "canonical_bson": "1400000005766563746F72000200000009270000" 31 | }, 32 | { 33 | "description": "Infinity Vector FLOAT32", 34 | "valid": true, 35 | "vector": [{"$numberDouble": "-Infinity"}, 0.0, {"$numberDouble": "Infinity"} ], 36 | "dtype_hex": "0x27", 37 | "dtype_alias": "FLOAT32", 38 | "padding": 0, 39 | "canonical_bson": "2000000005766563746F72000E000000092700000080FF000000000000807F00" 40 | }, 41 | { 42 | "description": "FLOAT32 with padding", 43 | "valid": false, 44 | "vector": [127.0, 7.0], 45 | "dtype_hex": "0x27", 46 | "dtype_alias": "FLOAT32", 47 | "padding": 3, 48 | "canonical_bson": "1C00000005766563746F72000A0000000927030000FE420000E04000" 49 | }, 50 | { 51 | "description": "Insufficient vector data with 3 bytes FLOAT32", 52 | "valid": false, 53 | "dtype_hex": "0x27", 54 | "dtype_alias": "FLOAT32", 55 | "canonical_bson": "1700000005766563746F7200050000000927002A2A2A00" 56 | }, 57 | { 58 | "description": "Insufficient vector data with 5 bytes FLOAT32", 59 | "valid": false, 60 | "dtype_hex": "0x27", 61 | "dtype_alias": "FLOAT32", 62 | "canonical_bson": "1900000005766563746F7200070000000927002A2A2A2A2A00" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /spec/spec_tests/data/binary_vector/int8.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Tests of Binary subtype 9, Vectors, with dtype INT8", 3 | "test_key": "vector", 4 | "tests": [ 5 | { 6 | "description": "Simple Vector INT8", 7 | "valid": true, 8 | "vector": [127, 7], 9 | "dtype_hex": "0x03", 10 | "dtype_alias": "INT8", 11 | "padding": 0, 12 | "canonical_bson": "1600000005766563746F7200040000000903007F0700" 13 | }, 14 | { 15 | "description": "Empty Vector INT8", 16 | "valid": true, 17 | "vector": [], 18 | "dtype_hex": "0x03", 19 | "dtype_alias": "INT8", 20 | "padding": 0, 21 | "canonical_bson": "1400000005766563746F72000200000009030000" 22 | }, 23 | { 24 | "description": "Overflow Vector INT8", 25 | "valid": false, 26 | "vector": [128], 27 | "dtype_hex": "0x03", 28 | "dtype_alias": "INT8", 29 | "padding": 0 30 | }, 31 | { 32 | "description": "Underflow Vector INT8", 33 | "valid": false, 34 | "vector": [-129], 35 | "dtype_hex": "0x03", 36 | "dtype_alias": "INT8", 37 | "padding": 0 38 | }, 39 | { 40 | "description": "INT8 with padding", 41 | "valid": false, 42 | "vector": [127, 7], 43 | "dtype_hex": "0x03", 44 | "dtype_alias": "INT8", 45 | "padding": 3, 46 | "canonical_bson": "1600000005766563746F7200040000000903037F0700" 47 | }, 48 | { 49 | "description": "INT8 with float inputs", 50 | "valid": false, 51 | "vector": [127.77, 7.77], 52 | "dtype_hex": "0x03", 53 | "dtype_alias": "INT8", 54 | "padding": 0 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /spec/spec_tests/data/binary_vector/packed_bit.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Tests of Binary subtype 9, Vectors, with dtype PACKED_BIT", 3 | "test_key": "vector", 4 | "tests": [ 5 | { 6 | "description": "Padding specified with no vector data PACKED_BIT", 7 | "valid": false, 8 | "vector": [], 9 | "dtype_hex": "0x10", 10 | "dtype_alias": "PACKED_BIT", 11 | "padding": 1, 12 | "canonical_bson": "1400000005766563746F72000200000009100100" 13 | }, 14 | { 15 | "description": "Simple Vector PACKED_BIT", 16 | "valid": true, 17 | "vector": [127, 7], 18 | "dtype_hex": "0x10", 19 | "dtype_alias": "PACKED_BIT", 20 | "padding": 0, 21 | "canonical_bson": "1600000005766563746F7200040000000910007F0700" 22 | }, 23 | { 24 | "description": "Empty Vector PACKED_BIT", 25 | "valid": true, 26 | "vector": [], 27 | "dtype_hex": "0x10", 28 | "dtype_alias": "PACKED_BIT", 29 | "padding": 0, 30 | "canonical_bson": "1400000005766563746F72000200000009100000" 31 | }, 32 | { 33 | "description": "PACKED_BIT with padding", 34 | "valid": true, 35 | "vector": [127, 7], 36 | "dtype_hex": "0x10", 37 | "dtype_alias": "PACKED_BIT", 38 | "padding": 3, 39 | "canonical_bson": "1600000005766563746F7200040000000910037F0700" 40 | }, 41 | { 42 | "description": "Overflow Vector PACKED_BIT", 43 | "valid": false, 44 | "vector": [256], 45 | "dtype_hex": "0x10", 46 | "dtype_alias": "PACKED_BIT", 47 | "padding": 0 48 | }, 49 | { 50 | "description": "Underflow Vector PACKED_BIT", 51 | "valid": false, 52 | "vector": [-1], 53 | "dtype_hex": "0x10", 54 | "dtype_alias": "PACKED_BIT", 55 | "padding": 0 56 | }, 57 | { 58 | "description": "Vector with float values PACKED_BIT", 59 | "valid": false, 60 | "vector": [127.5], 61 | "dtype_hex": "0x10", 62 | "dtype_alias": "PACKED_BIT", 63 | "padding": 0 64 | }, 65 | { 66 | "description": "Exceeding maximum padding PACKED_BIT", 67 | "valid": false, 68 | "vector": [1], 69 | "dtype_hex": "0x10", 70 | "dtype_alias": "PACKED_BIT", 71 | "padding": 8, 72 | "canonical_bson": "1500000005766563746F7200030000000910080100" 73 | }, 74 | { 75 | "description": "Negative padding PACKED_BIT", 76 | "valid": false, 77 | "vector": [1], 78 | "dtype_hex": "0x10", 79 | "dtype_alias": "PACKED_BIT", 80 | "padding": -1 81 | } 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/README.md: -------------------------------------------------------------------------------- 1 | There are the following deliberate changes made to the corpus tests in Ruby: 2 | 3 | 1. In double.js, Ruby appears to offer less precision than the spec tests 4 | demand: 5 | 6 | irb(main):001:0> -1.23456789012345677E+18 7 | => -1.2345678901234568e+18 8 | 9 | Because of this, -1.23456789012345677E+18 was changed to -1.2345678901234568e+18. 10 | The "e" was lowercased as well. Both the precision reduction and the lowercasing 11 | of "e" changes are also present in the Python driver, which appears to be 12 | affected by the same precision limitation. 13 | 14 | 2. In datetime.js, the millisecond component of iso8601 serialization of 15 | timestamps is always present, even if it is zero. 16 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/array.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Array", 3 | "bson_type": "0x04", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty", 8 | "canonical_bson": "0D000000046100050000000000", 9 | "canonical_extjson": "{\"a\" : []}" 10 | }, 11 | { 12 | "description": "Single Element Array", 13 | "canonical_bson": "140000000461000C0000001030000A0000000000", 14 | "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" 15 | }, 16 | { 17 | "description": "Single Element Array with index set incorrectly to empty string", 18 | "degenerate_bson": "130000000461000B00000010000A0000000000", 19 | "canonical_bson": "140000000461000C0000001030000A0000000000", 20 | "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" 21 | }, 22 | { 23 | "description": "Single Element Array with index set incorrectly to ab", 24 | "degenerate_bson": "150000000461000D000000106162000A0000000000", 25 | "canonical_bson": "140000000461000C0000001030000A0000000000", 26 | "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}]}" 27 | }, 28 | { 29 | "description": "Multi Element Array with duplicate indexes", 30 | "degenerate_bson": "1b000000046100130000001030000a000000103000140000000000", 31 | "canonical_bson": "1b000000046100130000001030000a000000103100140000000000", 32 | "canonical_extjson": "{\"a\" : [{\"$numberInt\": \"10\"}, {\"$numberInt\": \"20\"}]}" 33 | } 34 | ], 35 | "decodeErrors": [ 36 | { 37 | "description": "Array length too long: eats outer terminator", 38 | "bson": "140000000461000D0000001030000A0000000000" 39 | }, 40 | { 41 | "description": "Array length too short: leaks terminator", 42 | "bson": "140000000461000B0000001030000A0000000000" 43 | }, 44 | { 45 | "description": "Invalid Array: bad string length in field", 46 | "bson": "1A00000004666F6F00100000000230000500000062617A000000" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/boolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Boolean", 3 | "bson_type": "0x08", 4 | "test_key": "b", 5 | "valid": [ 6 | { 7 | "description": "True", 8 | "canonical_bson": "090000000862000100", 9 | "canonical_extjson": "{\"b\" : true}" 10 | }, 11 | { 12 | "description": "False", 13 | "canonical_bson": "090000000862000000", 14 | "canonical_extjson": "{\"b\" : false}" 15 | } 16 | ], 17 | "decodeErrors": [ 18 | { 19 | "description": "Invalid boolean value of 2", 20 | "bson": "090000000862000200" 21 | }, 22 | { 23 | "description": "Invalid boolean value of -1", 24 | "bson": "09000000086200FF00" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/code.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Javascript Code", 3 | "bson_type": "0x0D", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty string", 8 | "canonical_bson": "0D0000000D6100010000000000", 9 | "canonical_extjson": "{\"a\" : {\"$code\" : \"\"}}" 10 | }, 11 | { 12 | "description": "Single character", 13 | "canonical_bson": "0E0000000D610002000000620000", 14 | "canonical_extjson": "{\"a\" : {\"$code\" : \"b\"}}" 15 | }, 16 | { 17 | "description": "Multi-character", 18 | "canonical_bson": "190000000D61000D0000006162616261626162616261620000", 19 | "canonical_extjson": "{\"a\" : {\"$code\" : \"abababababab\"}}" 20 | }, 21 | { 22 | "description": "two-byte UTF-8 (\u00e9)", 23 | "canonical_bson": "190000000D61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 24 | "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}}" 25 | }, 26 | { 27 | "description": "three-byte UTF-8 (\u2606)", 28 | "canonical_bson": "190000000D61000D000000E29886E29886E29886E298860000", 29 | "canonical_extjson": "{\"a\" : {\"$code\" : \"\\u2606\\u2606\\u2606\\u2606\"}}" 30 | }, 31 | { 32 | "description": "Embedded nulls", 33 | "canonical_bson": "190000000D61000D0000006162006261620062616261620000", 34 | "canonical_extjson": "{\"a\" : {\"$code\" : \"ab\\u0000bab\\u0000babab\"}}" 35 | } 36 | ], 37 | "decodeErrors": [ 38 | { 39 | "description": "bad code string length: 0 (but no 0x00 either)", 40 | "bson": "0C0000000D61000000000000" 41 | }, 42 | { 43 | "description": "bad code string length: -1", 44 | "bson": "0C0000000D6100FFFFFFFF00" 45 | }, 46 | { 47 | "description": "bad code string length: eats terminator", 48 | "bson": "100000000D6100050000006200620000" 49 | }, 50 | { 51 | "description": "bad code string length: longer than rest of document", 52 | "bson": "120000000D00FFFFFF00666F6F6261720000" 53 | }, 54 | { 55 | "description": "code string is not null-terminated", 56 | "bson": "100000000D610004000000616263FF00" 57 | }, 58 | { 59 | "description": "empty code string, but extra null", 60 | "bson": "0E0000000D610001000000000000" 61 | }, 62 | { 63 | "description": "invalid UTF-8", 64 | "bson": "0E0000000D610002000000E90000" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/datetime.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DateTime", 3 | "bson_type": "0x09", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "epoch", 8 | "canonical_bson": "10000000096100000000000000000000", 9 | "relaxed_extjson": "{\"a\" : {\"$date\" : \"1970-01-01T00:00:00Z\"}}", 10 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"0\"}}}" 11 | }, 12 | { 13 | "description": "positive ms", 14 | "canonical_bson": "10000000096100C5D8D6CC3B01000000", 15 | "relaxed_extjson": "{\"a\" : {\"$date\" : \"2012-12-24T12:15:30.501Z\"}}", 16 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"1356351330501\"}}}" 17 | }, 18 | { 19 | "description": "negative", 20 | "canonical_bson": "10000000096100C33CE7B9BDFFFFFF00", 21 | "relaxed_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"-284643869501\"}}}", 22 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"-284643869501\"}}}" 23 | }, 24 | { 25 | "description" : "Y10K", 26 | "canonical_bson" : "1000000009610000DC1FD277E6000000", 27 | "canonical_extjson" : "{\"a\":{\"$date\":{\"$numberLong\":\"253402300800000\"}}}" 28 | }, 29 | { 30 | "description": "leading zero ms", 31 | "canonical_bson": "10000000096100D1D6D6CC3B01000000", 32 | "relaxed_extjson": "{\"a\" : {\"$date\" : \"2012-12-24T12:15:30.001Z\"}}", 33 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"1356351330001\"}}}" 34 | } 35 | ], 36 | "decodeErrors": [ 37 | { 38 | "description": "datetime field truncated", 39 | "bson": "0C0000000961001234567800" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/dbpointer.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DBPointer type (deprecated)", 3 | "bson_type": "0x0C", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "DBpointer", 9 | "canonical_bson": "1A0000000C610002000000620056E1FC72E0C917E9C471416100", 10 | "canonical_extjson": "{\"a\": {\"$dbPointer\": {\"$ref\": \"b\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}}", 11 | "converted_bson": "2a00000003610022000000022472656600020000006200072469640056e1fc72e0c917e9c47141610000", 12 | "converted_extjson": "{\"a\": {\"$ref\": \"b\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}" 13 | }, 14 | { 15 | "description": "DBpointer with opposite key order", 16 | "canonical_bson": "1A0000000C610002000000620056E1FC72E0C917E9C471416100", 17 | "canonical_extjson": "{\"a\": {\"$dbPointer\": {\"$ref\": \"b\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}}", 18 | "degenerate_extjson": "{\"a\": {\"$dbPointer\": {\"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}, \"$ref\": \"b\"}}}", 19 | "converted_bson": "2a00000003610022000000022472656600020000006200072469640056e1fc72e0c917e9c47141610000", 20 | "converted_extjson": "{\"a\": {\"$ref\": \"b\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}" 21 | }, 22 | { 23 | "description": "With two-byte UTF-8", 24 | "canonical_bson": "1B0000000C610003000000C3A90056E1FC72E0C917E9C471416100", 25 | "canonical_extjson": "{\"a\": {\"$dbPointer\": {\"$ref\": \"é\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}}", 26 | "converted_bson": "2B0000000361002300000002247265660003000000C3A900072469640056E1FC72E0C917E9C47141610000", 27 | "converted_extjson": "{\"a\": {\"$ref\": \"é\", \"$id\": {\"$oid\": \"56e1fc72e0c917e9c4714161\"}}}" 28 | } 29 | ], 30 | "decodeErrors": [ 31 | { 32 | "description": "String with negative length", 33 | "bson": "1A0000000C6100FFFFFFFF620056E1FC72E0C917E9C471416100" 34 | }, 35 | { 36 | "description": "String with zero length", 37 | "bson": "1A0000000C610000000000620056E1FC72E0C917E9C471416100" 38 | }, 39 | { 40 | "description": "String not null terminated", 41 | "bson": "1A0000000C610002000000626256E1FC72E0C917E9C471416100" 42 | }, 43 | { 44 | "description": "short OID (less than minimum length for field)", 45 | "bson": "160000000C61000300000061620056E1FC72E0C91700" 46 | }, 47 | { 48 | "description": "short OID (greater than minimum, but truncated)", 49 | "bson": "1A0000000C61000300000061620056E1FC72E0C917E9C4716100" 50 | }, 51 | { 52 | "description": "String with bad UTF-8", 53 | "bson": "1A0000000C610002000000E90056E1FC72E0C917E9C471416100" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Document type (sub-documents)", 3 | "bson_type": "0x03", 4 | "test_key": "x", 5 | "valid": [ 6 | { 7 | "description": "Empty subdoc", 8 | "canonical_bson": "0D000000037800050000000000", 9 | "canonical_extjson": "{\"x\" : {}}" 10 | }, 11 | { 12 | "description": "Empty-string key subdoc", 13 | "canonical_bson": "150000000378000D00000002000200000062000000", 14 | "canonical_extjson": "{\"x\" : {\"\" : \"b\"}}" 15 | }, 16 | { 17 | "description": "Single-character key subdoc", 18 | "canonical_bson": "160000000378000E0000000261000200000062000000", 19 | "canonical_extjson": "{\"x\" : {\"a\" : \"b\"}}" 20 | }, 21 | { 22 | "description": "Dollar-prefixed key in sub-document", 23 | "canonical_bson": "170000000378000F000000022461000200000062000000", 24 | "canonical_extjson": "{\"x\" : {\"$a\" : \"b\"}}" 25 | }, 26 | { 27 | "description": "Dollar as key in sub-document", 28 | "canonical_bson": "160000000378000E0000000224000200000061000000", 29 | "canonical_extjson": "{\"x\" : {\"$\" : \"a\"}}" 30 | }, 31 | { 32 | "description": "Dotted key in sub-document", 33 | "canonical_bson": "180000000378001000000002612E62000200000063000000", 34 | "canonical_extjson": "{\"x\" : {\"a.b\" : \"c\"}}" 35 | }, 36 | { 37 | "description": "Dot as key in sub-document", 38 | "canonical_bson": "160000000378000E000000022E000200000061000000", 39 | "canonical_extjson": "{\"x\" : {\".\" : \"a\"}}" 40 | } 41 | ], 42 | "decodeErrors": [ 43 | { 44 | "description": "Subdocument length too long: eats outer terminator", 45 | "bson": "1800000003666F6F000F0000001062617200FFFFFF7F0000" 46 | }, 47 | { 48 | "description": "Subdocument length too short: leaks terminator", 49 | "bson": "1500000003666F6F000A0000000862617200010000" 50 | }, 51 | { 52 | "description": "Invalid subdocument: bad string length in field", 53 | "bson": "1C00000003666F6F001200000002626172000500000062617A000000" 54 | }, 55 | { 56 | "description": "Null byte in sub-document key", 57 | "bson": "150000000378000D00000010610000010000000000" 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/int32.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Int32 type", 3 | "bson_type": "0x10", 4 | "test_key": "i", 5 | "valid": [ 6 | { 7 | "description": "MinValue", 8 | "canonical_bson": "0C0000001069000000008000", 9 | "canonical_extjson": "{\"i\" : {\"$numberInt\": \"-2147483648\"}}", 10 | "relaxed_extjson": "{\"i\" : -2147483648}" 11 | }, 12 | { 13 | "description": "MaxValue", 14 | "canonical_bson": "0C000000106900FFFFFF7F00", 15 | "canonical_extjson": "{\"i\" : {\"$numberInt\": \"2147483647\"}}", 16 | "relaxed_extjson": "{\"i\" : 2147483647}" 17 | }, 18 | { 19 | "description": "-1", 20 | "canonical_bson": "0C000000106900FFFFFFFF00", 21 | "canonical_extjson": "{\"i\" : {\"$numberInt\": \"-1\"}}", 22 | "relaxed_extjson": "{\"i\" : -1}" 23 | }, 24 | { 25 | "description": "0", 26 | "canonical_bson": "0C0000001069000000000000", 27 | "canonical_extjson": "{\"i\" : {\"$numberInt\": \"0\"}}", 28 | "relaxed_extjson": "{\"i\" : 0}" 29 | }, 30 | { 31 | "description": "1", 32 | "canonical_bson": "0C0000001069000100000000", 33 | "canonical_extjson": "{\"i\" : {\"$numberInt\": \"1\"}}", 34 | "relaxed_extjson": "{\"i\" : 1}" 35 | } 36 | ], 37 | "decodeErrors": [ 38 | { 39 | "description": "Bad int32 field length", 40 | "bson": "090000001061000500" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/int64.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Int64 type", 3 | "bson_type": "0x12", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "MinValue", 8 | "canonical_bson": "10000000126100000000000000008000", 9 | "canonical_extjson": "{\"a\" : {\"$numberLong\" : \"-9223372036854775808\"}}", 10 | "relaxed_extjson": "{\"a\" : -9223372036854775808}" 11 | }, 12 | { 13 | "description": "MaxValue", 14 | "canonical_bson": "10000000126100FFFFFFFFFFFFFF7F00", 15 | "canonical_extjson": "{\"a\" : {\"$numberLong\" : \"9223372036854775807\"}}", 16 | "relaxed_extjson": "{\"a\" : 9223372036854775807}" 17 | }, 18 | { 19 | "description": "-1", 20 | "canonical_bson": "10000000126100FFFFFFFFFFFFFFFF00", 21 | "canonical_extjson": "{\"a\" : {\"$numberLong\" : \"-1\"}}", 22 | "relaxed_extjson": "{\"a\" : -1}" 23 | }, 24 | { 25 | "description": "0", 26 | "canonical_bson": "10000000126100000000000000000000", 27 | "canonical_extjson": "{\"a\" : {\"$numberLong\" : \"0\"}}", 28 | "relaxed_extjson": "{\"a\" : 0}" 29 | }, 30 | { 31 | "description": "1", 32 | "canonical_bson": "10000000126100010000000000000000", 33 | "canonical_extjson": "{\"a\" : {\"$numberLong\" : \"1\"}}", 34 | "relaxed_extjson": "{\"a\" : 1}" 35 | } 36 | ], 37 | "decodeErrors": [ 38 | { 39 | "description": "int64 field truncated", 40 | "bson": "0C0000001261001234567800" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/maxkey.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Maxkey type", 3 | "bson_type": "0x7F", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Maxkey", 8 | "canonical_bson": "080000007F610000", 9 | "canonical_extjson": "{\"a\" : {\"$maxKey\" : 1}}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/minkey.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Minkey type", 3 | "bson_type": "0xFF", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Minkey", 8 | "canonical_bson": "08000000FF610000", 9 | "canonical_extjson": "{\"a\" : {\"$minKey\" : 1}}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/multi-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Multiple types within the same document", 3 | "bson_type": "0x00", 4 | "valid": [ 5 | { 6 | "description": "All BSON types", 7 | "canonical_bson": "F4010000075F69640057E193D7A9CC81B4027498B502537472696E670007000000737472696E670010496E743332002A00000012496E743634002A0000000000000001446F75626C6500000000000000F0BF0542696E617279001000000003A34C38F7C3ABEDC8A37814A992AB8DB60542696E61727955736572446566696E656400050000008001020304050D436F6465000E00000066756E6374696F6E2829207B7D000F436F64655769746853636F7065001B0000000E00000066756E6374696F6E2829207B7D00050000000003537562646F63756D656E74001200000002666F6F0004000000626172000004417272617900280000001030000100000010310002000000103200030000001033000400000010340005000000001154696D657374616D7000010000002A0000000B5265676578007061747465726E0000094461746574696D6545706F6368000000000000000000094461746574696D65506F73697469766500FFFFFF7F00000000094461746574696D654E656761746976650000000080FFFFFFFF085472756500010846616C73650000034442526566003D0000000224726566000B000000636F6C6C656374696F6E00072469640057FD71E96E32AB4225B723FB02246462000900000064617461626173650000FF4D696E6B6579007F4D61786B6579000A4E756C6C0000", 8 | "canonical_extjson": "{\"_id\": {\"$oid\": \"57e193d7a9cc81b4027498b5\"}, \"String\": \"string\", \"Int32\": {\"$numberInt\": \"42\"}, \"Int64\": {\"$numberLong\": \"42\"}, \"Double\": {\"$numberDouble\": \"-1.0\"}, \"Binary\": { \"$binary\" : {\"base64\": \"o0w498Or7cijeBSpkquNtg==\", \"subType\": \"03\"}}, \"BinaryUserDefined\": { \"$binary\" : {\"base64\": \"AQIDBAU=\", \"subType\": \"80\"}}, \"Code\": {\"$code\": \"function() {}\"}, \"CodeWithScope\": {\"$code\": \"function() {}\", \"$scope\": {}}, \"Subdocument\": {\"foo\": \"bar\"}, \"Array\": [{\"$numberInt\": \"1\"}, {\"$numberInt\": \"2\"}, {\"$numberInt\": \"3\"}, {\"$numberInt\": \"4\"}, {\"$numberInt\": \"5\"}], \"Timestamp\": {\"$timestamp\": {\"t\": 42, \"i\": 1}}, \"Regex\": {\"$regularExpression\": {\"pattern\": \"pattern\", \"options\": \"\"}}, \"DatetimeEpoch\": {\"$date\": {\"$numberLong\": \"0\"}}, \"DatetimePositive\": {\"$date\": {\"$numberLong\": \"2147483647\"}}, \"DatetimeNegative\": {\"$date\": {\"$numberLong\": \"-2147483648\"}}, \"True\": true, \"False\": false, \"DBRef\": {\"$ref\": \"collection\", \"$id\": {\"$oid\": \"57fd71e96e32ab4225b723fb\"}, \"$db\": \"database\"}, \"Minkey\": {\"$minKey\": 1}, \"Maxkey\": {\"$maxKey\": 1}, \"Null\": null}" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/null.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Null type", 3 | "bson_type": "0x0A", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Null", 8 | "canonical_bson": "080000000A610000", 9 | "canonical_extjson": "{\"a\" : null}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/oid.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "ObjectId", 3 | "bson_type": "0x07", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "All zeroes", 8 | "canonical_bson": "1400000007610000000000000000000000000000", 9 | "canonical_extjson": "{\"a\" : {\"$oid\" : \"000000000000000000000000\"}}" 10 | }, 11 | { 12 | "description": "All ones", 13 | "canonical_bson": "14000000076100FFFFFFFFFFFFFFFFFFFFFFFF00", 14 | "canonical_extjson": "{\"a\" : {\"$oid\" : \"ffffffffffffffffffffffff\"}}" 15 | }, 16 | { 17 | "description": "Random", 18 | "canonical_bson": "1400000007610056E1FC72E0C917E9C471416100", 19 | "canonical_extjson": "{\"a\" : {\"$oid\" : \"56e1fc72e0c917e9c4714161\"}}" 20 | } 21 | ], 22 | "decodeErrors": [ 23 | { 24 | "description": "OID truncated", 25 | "bson": "1200000007610056E1FC72E0C917E9C471" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/regex.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Regular Expression type", 3 | "bson_type": "0x0B", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "empty regex with no options", 8 | "canonical_bson": "0A0000000B6100000000", 9 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"\", \"options\" : \"\"}}}" 10 | }, 11 | { 12 | "description": "regex without options", 13 | "canonical_bson": "0D0000000B6100616263000000", 14 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"abc\", \"options\" : \"\"}}}" 15 | }, 16 | { 17 | "description": "regex with options", 18 | "canonical_bson": "0F0000000B610061626300696D0000", 19 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"abc\", \"options\" : \"im\"}}}" 20 | }, 21 | { 22 | "description": "regex with options (keys reversed)", 23 | "canonical_bson": "0F0000000B610061626300696D0000", 24 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"abc\", \"options\" : \"im\"}}}", 25 | "degenerate_extjson": "{\"a\" : {\"$regularExpression\" : {\"options\" : \"im\", \"pattern\": \"abc\"}}}" 26 | }, 27 | { 28 | "description": "regex with slash", 29 | "canonical_bson": "110000000B610061622F636400696D0000", 30 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"ab/cd\", \"options\" : \"im\"}}}" 31 | }, 32 | { 33 | "description": "flags not alphabetized", 34 | "degenerate_bson": "100000000B6100616263006D69780000", 35 | "canonical_bson": "100000000B610061626300696D780000", 36 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"abc\", \"options\" : \"imx\"}}}", 37 | "degenerate_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"abc\", \"options\" : \"mix\"}}}" 38 | }, 39 | { 40 | "description" : "Required escapes", 41 | "canonical_bson" : "100000000B610061625C226162000000", 42 | "canonical_extjson": "{\"a\" : {\"$regularExpression\" : { \"pattern\": \"ab\\\\\\\"ab\", \"options\" : \"\"}}}" 43 | }, 44 | { 45 | "description" : "Regular expression as value of $regex query operator", 46 | "canonical_bson" : "180000000B247265676578007061747465726E0069780000", 47 | "canonical_extjson": "{\"$regex\" : {\"$regularExpression\" : { \"pattern\": \"pattern\", \"options\" : \"ix\"}}}" 48 | }, 49 | { 50 | "description" : "Regular expression as value of $regex query operator with $options", 51 | "canonical_bson" : "270000000B247265676578007061747465726E000002246F7074696F6E73000300000069780000", 52 | "canonical_extjson": "{\"$regex\" : {\"$regularExpression\" : { \"pattern\": \"pattern\", \"options\" : \"\"}}, \"$options\" : \"ix\"}" 53 | } 54 | ], 55 | "decodeErrors": [ 56 | { 57 | "description": "Null byte in pattern string", 58 | "bson": "0F0000000B610061006300696D0000" 59 | }, 60 | { 61 | "description": "Null byte in flags string", 62 | "bson": "100000000B61006162630069006D0000" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "String", 3 | "bson_type": "0x02", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty string", 8 | "canonical_bson": "0D000000026100010000000000", 9 | "canonical_extjson": "{\"a\" : \"\"}" 10 | }, 11 | { 12 | "description": "Single character", 13 | "canonical_bson": "0E00000002610002000000620000", 14 | "canonical_extjson": "{\"a\" : \"b\"}" 15 | }, 16 | { 17 | "description": "Multi-character", 18 | "canonical_bson": "190000000261000D0000006162616261626162616261620000", 19 | "canonical_extjson": "{\"a\" : \"abababababab\"}" 20 | }, 21 | { 22 | "description": "two-byte UTF-8 (\u00e9)", 23 | "canonical_bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 24 | "canonical_extjson": "{\"a\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}" 25 | }, 26 | { 27 | "description": "three-byte UTF-8 (\u2606)", 28 | "canonical_bson": "190000000261000D000000E29886E29886E29886E298860000", 29 | "canonical_extjson": "{\"a\" : \"\\u2606\\u2606\\u2606\\u2606\"}" 30 | }, 31 | { 32 | "description": "Embedded nulls", 33 | "canonical_bson": "190000000261000D0000006162006261620062616261620000", 34 | "canonical_extjson": "{\"a\" : \"ab\\u0000bab\\u0000babab\"}" 35 | }, 36 | { 37 | "description": "Required escapes", 38 | "canonical_bson" : "320000000261002600000061625C220102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F61620000", 39 | "canonical_extjson" : "{\"a\":\"ab\\\\\\\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001fab\"}" 40 | } 41 | ], 42 | "decodeErrors": [ 43 | { 44 | "description": "bad string length: 0 (but no 0x00 either)", 45 | "bson": "0C0000000261000000000000" 46 | }, 47 | { 48 | "description": "bad string length: -1", 49 | "bson": "0C000000026100FFFFFFFF00" 50 | }, 51 | { 52 | "description": "bad string length: eats terminator", 53 | "bson": "10000000026100050000006200620000" 54 | }, 55 | { 56 | "description": "bad string length: longer than rest of document", 57 | "bson": "120000000200FFFFFF00666F6F6261720000" 58 | }, 59 | { 60 | "description": "string is not null-terminated", 61 | "bson": "1000000002610004000000616263FF00" 62 | }, 63 | { 64 | "description": "empty string, but extra null", 65 | "bson": "0E00000002610001000000000000" 66 | }, 67 | { 68 | "description": "invalid UTF-8", 69 | "bson": "0E00000002610002000000E90000" 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/symbol.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Symbol", 3 | "bson_type": "0x0E", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "Empty string", 9 | "canonical_bson": "0D0000000E6100010000000000", 10 | "canonical_extjson": "{\"a\": {\"$symbol\": \"\"}}", 11 | "converted_bson": "0D000000026100010000000000", 12 | "converted_extjson": "{\"a\": \"\"}" 13 | }, 14 | { 15 | "description": "Single character", 16 | "canonical_bson": "0E0000000E610002000000620000", 17 | "canonical_extjson": "{\"a\": {\"$symbol\": \"b\"}}", 18 | "converted_bson": "0E00000002610002000000620000", 19 | "converted_extjson": "{\"a\": \"b\"}" 20 | }, 21 | { 22 | "description": "Multi-character", 23 | "canonical_bson": "190000000E61000D0000006162616261626162616261620000", 24 | "canonical_extjson": "{\"a\": {\"$symbol\": \"abababababab\"}}", 25 | "converted_bson": "190000000261000D0000006162616261626162616261620000", 26 | "converted_extjson": "{\"a\": \"abababababab\"}" 27 | }, 28 | { 29 | "description": "two-byte UTF-8 (\u00e9)", 30 | "canonical_bson": "190000000E61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 31 | "canonical_extjson": "{\"a\": {\"$symbol\": \"éééééé\"}}", 32 | "converted_bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 33 | "converted_extjson": "{\"a\": \"éééééé\"}" 34 | }, 35 | { 36 | "description": "three-byte UTF-8 (\u2606)", 37 | "canonical_bson": "190000000E61000D000000E29886E29886E29886E298860000", 38 | "canonical_extjson": "{\"a\": {\"$symbol\": \"☆☆☆☆\"}}", 39 | "converted_bson": "190000000261000D000000E29886E29886E29886E298860000", 40 | "converted_extjson": "{\"a\": \"☆☆☆☆\"}" 41 | }, 42 | { 43 | "description": "Embedded nulls", 44 | "canonical_bson": "190000000E61000D0000006162006261620062616261620000", 45 | "canonical_extjson": "{\"a\": {\"$symbol\": \"ab\\u0000bab\\u0000babab\"}}", 46 | "converted_bson": "190000000261000D0000006162006261620062616261620000", 47 | "converted_extjson": "{\"a\": \"ab\\u0000bab\\u0000babab\"}" 48 | } 49 | ], 50 | "decodeErrors": [ 51 | { 52 | "description": "bad symbol length: 0 (but no 0x00 either)", 53 | "bson": "0C0000000E61000000000000" 54 | }, 55 | { 56 | "description": "bad symbol length: -1", 57 | "bson": "0C0000000E6100FFFFFFFF00" 58 | }, 59 | { 60 | "description": "bad symbol length: eats terminator", 61 | "bson": "100000000E6100050000006200620000" 62 | }, 63 | { 64 | "description": "bad symbol length: longer than rest of document", 65 | "bson": "120000000E00FFFFFF00666F6F6261720000" 66 | }, 67 | { 68 | "description": "symbol is not null-terminated", 69 | "bson": "100000000E610004000000616263FF00" 70 | }, 71 | { 72 | "description": "empty symbol, but extra null", 73 | "bson": "0E0000000E610001000000000000" 74 | }, 75 | { 76 | "description": "invalid UTF-8", 77 | "bson": "0E0000000E610002000000E90000" 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/timestamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Timestamp type", 3 | "bson_type": "0x11", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Timestamp: (123456789, 42)", 8 | "canonical_bson": "100000001161002A00000015CD5B0700", 9 | "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 123456789, \"i\" : 42} } }" 10 | }, 11 | { 12 | "description": "Timestamp: (123456789, 42) (keys reversed)", 13 | "canonical_bson": "100000001161002A00000015CD5B0700", 14 | "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 123456789, \"i\" : 42} } }", 15 | "degenerate_extjson": "{\"a\" : {\"$timestamp\" : {\"i\" : 42, \"t\" : 123456789} } }" 16 | }, 17 | { 18 | "description": "Timestamp with high-order bit set on both seconds and increment", 19 | "canonical_bson": "10000000116100FFFFFFFFFFFFFFFF00", 20 | "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 4294967295, \"i\" : 4294967295} } }" 21 | }, 22 | { 23 | "description": "Timestamp with high-order bit set on both seconds and increment (not UINT32_MAX)", 24 | "canonical_bson": "1000000011610000286BEE00286BEE00", 25 | "canonical_extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 4000000000, \"i\" : 4000000000} } }" 26 | } 27 | ], 28 | "decodeErrors": [ 29 | { 30 | "description": "Truncated timestamp field", 31 | "bson": "0f0000001161002A00000015CD5B00" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus/undefined.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Undefined type (deprecated)", 3 | "bson_type": "0x06", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "Undefined", 9 | "canonical_bson": "0800000006610000", 10 | "canonical_extjson": "{\"a\" : {\"$undefined\" : true}}", 11 | "converted_bson": "080000000A610000", 12 | "converted_extjson": "{\"a\" : null}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/array.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Array", 3 | "bson_type": "0x04", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty", 8 | "bson": "0D000000046100050000000000", 9 | "extjson": "{\"a\" : []}" 10 | }, 11 | { 12 | "description": "Single Element Array", 13 | "bson": "140000000461000C0000001030000A0000000000", 14 | "extjson": "{\"a\" : [10]}" 15 | }, 16 | { 17 | "description": "Single Element Array with index set incorrectly to empty string", 18 | "bson": "130000000461000B00000010000A0000000000", 19 | "canonical_bson": "140000000461000C0000001030000A0000000000", 20 | "extjson": "{\"a\" : [10]}" 21 | }, 22 | { 23 | "description": "Single Element Array with index set incorrectly to ab", 24 | "bson": "150000000461000D000000106162000A0000000000", 25 | "canonical_bson": "140000000461000C0000001030000A0000000000", 26 | "extjson": "{\"a\" : [10]}" 27 | }, 28 | { 29 | "description": "Multi Element Array with duplicate indexes", 30 | "bson": "1b000000046100130000001030000a000000103000140000000000", 31 | "canonical_bson": "1b000000046100130000001030000a000000103100140000000000", 32 | "extjson": "{\"a\" : [10, 20]}" 33 | } 34 | ], 35 | "decodeErrors": [ 36 | { 37 | "description": "Array length too long: eats outer terminator", 38 | "bson": "140000000461000D0000001030000A0000000000" 39 | }, 40 | { 41 | "description": "Array length too short: leaks terminator", 42 | "bson": "140000000461000B0000001030000A0000000000" 43 | }, 44 | { 45 | "description": "Invalid Array: bad string length in field", 46 | "bson": "1A00000004666F6F00100000000230000500000062617A000000" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/binary.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Binary type", 3 | "bson_type": "0x05", 4 | "test_key": "x", 5 | "valid": [ 6 | { 7 | "description": "subtype 0x00 (Zero-length)", 8 | "bson": "0D000000057800000000000000", 9 | "extjson": "{\"x\" : {\"$binary\" : \"\", \"$type\" : \"00\"}}" 10 | }, 11 | { 12 | "description": "subtype 0x00", 13 | "bson": "0F0000000578000200000000FFFF00", 14 | "extjson": "{\"x\" : {\"$binary\" : \"//8=\", \"$type\" : \"00\"}}" 15 | }, 16 | { 17 | "description": "subtype 0x01", 18 | "bson": "0F0000000578000200000001FFFF00", 19 | "extjson": "{\"x\" : {\"$binary\" : \"//8=\", \"$type\" : \"01\"}}" 20 | }, 21 | { 22 | "description": "subtype 0x02", 23 | "bson": "13000000057800060000000202000000ffff00", 24 | "extjson": "{\"x\" : {\"$binary\" : \"//8=\", \"$type\" : \"02\"}}" 25 | }, 26 | { 27 | "description": "subtype 0x03", 28 | "bson": "1D000000057800100000000373FFD26444B34C6990E8E7D1DFC035D400", 29 | "extjson": "{\"x\" : {\"$binary\" : \"c//SZESzTGmQ6OfR38A11A==\", \"$type\" : \"03\"}}" 30 | }, 31 | { 32 | "description": "subtype 0x04", 33 | "bson": "1D000000057800100000000473FFD26444B34C6990E8E7D1DFC035D400", 34 | "extjson": "{\"x\" : {\"$binary\" : \"c//SZESzTGmQ6OfR38A11A==\", \"$type\" : \"04\"}}" 35 | }, 36 | { 37 | "description": "subtype 0x05", 38 | "bson": "1D000000057800100000000573FFD26444B34C6990E8E7D1DFC035D400", 39 | "extjson": "{\"x\" : {\"$binary\" : \"c//SZESzTGmQ6OfR38A11A==\", \"$type\" : \"05\"}}" 40 | }, 41 | { 42 | "description": "subtype 0x80", 43 | "bson": "0F0000000578000200000080FFFF00", 44 | "extjson": "{\"x\" : {\"$binary\" : \"//8=\", \"$type\" : \"80\"}}" 45 | } 46 | ], 47 | "decodeErrors": [ 48 | { 49 | "description": "Length longer than document", 50 | "bson": "1D000000057800FF0000000573FFD26444B34C6990E8E7D1DFC035D400" 51 | }, 52 | { 53 | "description": "Negative length", 54 | "bson": "0D000000057800FFFFFFFF0000" 55 | }, 56 | { 57 | "description": "subtype 0x02 length too long ", 58 | "bson": "13000000057800060000000203000000FFFF00" 59 | }, 60 | { 61 | "description": "subtype 0x02 length too short", 62 | "bson": "13000000057800060000000201000000FFFF00" 63 | }, 64 | { 65 | "description": "subtype 0x02 length negative one", 66 | "bson": "130000000578000600000002FFFFFFFFFFFF00" 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/boolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Boolean", 3 | "bson_type": "0x08", 4 | "test_key": "b", 5 | "valid": [ 6 | { 7 | "description": "True", 8 | "bson": "090000000862000100", 9 | "extjson": "{\"b\" : true}" 10 | }, 11 | { 12 | "description": "False", 13 | "bson": "090000000862000000", 14 | "extjson": "{\"b\" : false}" 15 | } 16 | ], 17 | "decodeErrors": [ 18 | { 19 | "description": "Invalid boolean value of 2", 20 | "bson": "090000000862000200" 21 | }, 22 | { 23 | "description": "Invalid boolean value of -1", 24 | "bson": "09000000086200FF00" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/code.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Code", 3 | "bson_type": "0x0D", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty string", 8 | "bson": "0D0000000D6100010000000000", 9 | "extjson": "{\"a\" : {\"$code\" : \"\"}}" 10 | }, 11 | { 12 | "description": "Single character", 13 | "bson": "0E0000000D610002000000620000", 14 | "extjson": "{\"a\" : {\"$code\" : \"b\"}}" 15 | }, 16 | { 17 | "description": "Multi-character", 18 | "bson": "190000000D61000D0000006162616261626162616261620000", 19 | "extjson": "{\"a\" : {\"$code\" : \"abababababab\"}}" 20 | }, 21 | { 22 | "description": "two-byte UTF-8 (\u00e9)", 23 | "bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 24 | "extjson": "{\"a\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}" 25 | }, 26 | { 27 | "description": "three-byte UTF-8 (\u2606)", 28 | "bson": "190000000261000D000000E29886E29886E29886E298860000", 29 | "extjson": "{\"a\" : \"\\u2606\\u2606\\u2606\\u2606\"}" 30 | }, 31 | { 32 | "description": "Embedded nulls", 33 | "bson": "190000000261000D0000006162006261620062616261620000", 34 | "extjson": "{\"a\" : \"ab\\u0000bab\\u0000babab\"}" 35 | } 36 | ], 37 | "decodeErrors": [ 38 | { 39 | "description": "bad code string length: 0 (but no 0x00 either)", 40 | "bson": "0C0000000261000000000000" 41 | }, 42 | { 43 | "description": "bad code string length: -1", 44 | "bson": "0C000000026100FFFFFFFF00" 45 | }, 46 | { 47 | "description": "bad code string length: eats terminator", 48 | "bson": "10000000026100050000006200620000" 49 | }, 50 | { 51 | "description": "bad code string length: longer than rest of document", 52 | "bson": "120000000200FFFFFF00666F6F6261720000" 53 | }, 54 | { 55 | "description": "code string is not null-terminated", 56 | "bson": "1000000002610004000000616263FF00" 57 | }, 58 | { 59 | "description": "empty code string, but extra null", 60 | "bson": "0E00000002610001000000000000" 61 | }, 62 | { 63 | "description": "invalid UTF-8", 64 | "bson": "0E00000002610002000000E90000" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/document.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Document", 3 | "bson_type": "0x03", 4 | "test_key": "x", 5 | "valid": [ 6 | { 7 | "description": "Empty subdoc", 8 | "bson": "0D000000037800050000000000", 9 | "extjson": "{\"x\" : {}}" 10 | }, 11 | { 12 | "description": "Empty-string key subdoc", 13 | "bson": "150000000378000D00000002000200000062000000", 14 | "extjson": "{\"x\" : {\"\" : \"b\"}}" 15 | }, 16 | { 17 | "description": "Single-character key subdoc", 18 | "bson": "160000000378000E0000000261000200000062000000", 19 | "extjson": "{\"x\" : {\"a\" : \"b\"}}" 20 | } 21 | ], 22 | "decodeErrors": [ 23 | { 24 | "description": "Subdocument length too long: eats outer terminator", 25 | "bson": "1800000003666F6F000F0000001062617200FFFFFF7F0000" 26 | }, 27 | { 28 | "description": "Subdocument length too short: leaks terminator", 29 | "bson": "1500000003666F6F000A0000000862617200010000" 30 | }, 31 | { 32 | "description": "Invalid subdocument: bad string length in field", 33 | "bson": "1C00000003666F6F001200000002626172000500000062617A000000" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/double.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Float", 3 | "bson_type": "0x01", 4 | "test_key": "d", 5 | "valid": [ 6 | { 7 | "description": "+1.0", 8 | "bson": "10000000016400000000000000F03F00", 9 | "extjson": "{\"d\" : 1.0}" 10 | }, 11 | { 12 | "description": "-1.0", 13 | "bson": "10000000016400000000000000F0BF00", 14 | "extjson": "{\"d\" : -1.0}" 15 | }, 16 | { 17 | "description": "+1.0001220703125", 18 | "bson": "10000000016400000000008000F03F00", 19 | "extjson": "{\"d\" : 1.0001220703125}" 20 | }, 21 | { 22 | "description": "-1.0001220703125", 23 | "bson": "10000000016400000000008000F0BF00", 24 | "extjson": "{\"d\" : -1.0001220703125}" 25 | }, 26 | { 27 | "description": "+2.0001220703125e10", 28 | "bson": "1000000001640000807ca1a9a0124200", 29 | "extjson": "{\"d\" : 2.0001220703125e10}" 30 | }, 31 | { 32 | "description": "-2.0001220703125e10", 33 | "bson": "1000000001640000807ca1a9a012c200", 34 | "extjson": "{\"d\" : -2.0001220703125e10}" 35 | }, 36 | { 37 | "description": "0.0", 38 | "bson": "10000000016400000000000000000000", 39 | "extjson": "{\"d\" : 0.0}" 40 | }, 41 | { 42 | "description": "-0.0", 43 | "bson": "10000000016400000000000000008000", 44 | "extjson": "{\"d\" : -0.0}" 45 | }, 46 | { 47 | "description": "NaN", 48 | "bson": "10000000016400000000000000F87F00" 49 | }, 50 | { 51 | "description": "NaN with payload", 52 | "bson": "10000000016400120000000000F87F00" 53 | }, 54 | { 55 | "description": "Inf", 56 | "bson": "10000000016400000000000000F07F00" 57 | }, 58 | { 59 | "description": "-Inf", 60 | "bson": "10000000016400000000000000F0FF00" 61 | } 62 | ], 63 | "decodeErrors": [ 64 | { 65 | "description": "double truncated", 66 | "bson": "0B0000000164000000F03F00" 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/failures/datetime.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DateTime", 3 | "bson_type": "0x09", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "epoch", 8 | "bson": "10000000096100000000000000000000", 9 | "extjson": "{\"a\" : {\"$date\" : \"1970-01-01T00:00:00.000Z\"}}", 10 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"0\"}}}" 11 | }, 12 | { 13 | "description": "positive ms", 14 | "bson": "10000000096100C4D8D6CC3B01000000", 15 | "extjson": "{\"a\" : {\"$date\" : \"2012-12-24T12:15:30.500Z\"}}", 16 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"1356351330500\"}}}" 17 | }, 18 | { 19 | "description": "negative", 20 | "bson": "10000000096100C43CE7B9BDFFFFFF00", 21 | "extjson": "{\"a\" : {\"$date\" : \"1960-12-24T12:15:30.500Z\"}}", 22 | "canonical_extjson": "{\"a\" : {\"$date\" : {\"$numberLong\" : \"-284643869500\"}}}" 23 | } 24 | ], 25 | "decodeErrors": [ 26 | { 27 | "description": "datetime field truncated", 28 | "bson": "0C0000000961001234567800" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/failures/dbpointer.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "DBPointer type (deprecated)", 3 | "bson_type": "0x0C", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "DBpointer", 9 | "bson": "1A0000000C610002000000620056E1FC72E0C917E9C471416100" 10 | }, 11 | { 12 | "description": "With two-byte UTF-8", 13 | "bson": "1B0000000C610003000000C3A90056E1FC72E0C917E9C471416100" 14 | } 15 | ], 16 | "decodeErrors": [ 17 | { 18 | "description": "String with negative length", 19 | "bson": "1A0000000C6100FFFFFFFF620056E1FC72E0C917E9C471416100" 20 | }, 21 | { 22 | "description": "String with zero length", 23 | "bson": "1A0000000C610000000000620056E1FC72E0C917E9C471416100" 24 | }, 25 | { 26 | "description": "String not null terminated", 27 | "bson": "1A0000000C610002000000626256E1FC72E0C917E9C471416100" 28 | }, 29 | { 30 | "description": "short OID (less than minimum length for field)", 31 | "bson": "160000000C61000300000061620056E1FC72E0C91700" 32 | }, 33 | { 34 | "description": "short OID (greater than minimum, but truncated)", 35 | "bson": "1A0000000C61000300000061620056E1FC72E0C917E9C4716100" 36 | }, 37 | { 38 | "description": "String with bad UTF-8", 39 | "bson": "1A0000000C610002000000E90056E1FC72E0C917E9C471416100" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/failures/int64.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Int64 type", 3 | "bson_type": "0x12", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "MinValue", 8 | "bson": "10000000126100000000000000008000", 9 | "extjson": "{\"a\" : {\"$numberLong\" : \"-9223372036854775808\"}}" 10 | }, 11 | { 12 | "description": "MaxValue", 13 | "bson": "10000000126100FFFFFFFFFFFFFF7F00", 14 | "extjson": "{\"a\" : {\"$numberLong\" : \"9223372036854775807\"}}" 15 | }, 16 | { 17 | "description": "-1", 18 | "bson": "10000000126100FFFFFFFFFFFFFFFF00", 19 | "extjson": "{\"a\" : {\"$numberLong\" : \"-1\"}}" 20 | }, 21 | { 22 | "description": "0", 23 | "bson": "10000000126100000000000000000000", 24 | "extjson": "{\"a\" : {\"$numberLong\" : \"0\"}}" 25 | }, 26 | { 27 | "description": "1", 28 | "bson": "10000000126100010000000000000000", 29 | "extjson": "{\"a\" : {\"$numberLong\" : \"1\"}}" 30 | } 31 | ], 32 | "decodeErrors": [ 33 | { 34 | "description": "int64 field truncated", 35 | "bson": "0C0000001261001234567800" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/failures/symbol.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Symbol", 3 | "bson_type": "0x0E", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "Empty string", 9 | "bson": "0D0000000E6100010000000000" 10 | }, 11 | { 12 | "description": "Single character", 13 | "bson": "0E0000000E610002000000620000" 14 | }, 15 | { 16 | "description": "Multi-character", 17 | "bson": "190000000E61000D0000006162616261626162616261620000" 18 | }, 19 | { 20 | "description": "two-byte UTF-8 (\u00e9)", 21 | "bson": "190000000E61000D000000C3A9C3A9C3A9C3A9C3A9C3A90000" 22 | }, 23 | { 24 | "description": "three-byte UTF-8 (\u2606)", 25 | "bson": "190000000E61000D000000E29886E29886E29886E298860000" 26 | }, 27 | { 28 | "description": "Embedded nulls", 29 | "bson": "190000000E61000D0000006162006261620062616261620000" 30 | } 31 | ], 32 | "decodeErrors": [ 33 | { 34 | "description": "bad symbol length: 0 (but no 0x00 either)", 35 | "bson": "0C0000000261000000000000" 36 | }, 37 | { 38 | "description": "bad symbol length: -1", 39 | "bson": "0C000000026100FFFFFFFF00" 40 | }, 41 | { 42 | "description": "bad symbol length: eats terminator", 43 | "bson": "10000000026100050000006200620000" 44 | }, 45 | { 46 | "description": "bad symbol length: longer than rest of document", 47 | "bson": "120000000200FFFFFF00666F6F6261720000" 48 | }, 49 | { 50 | "description": "symbol is not null-terminated", 51 | "bson": "1000000002610004000000616263FF00" 52 | }, 53 | { 54 | "description": "empty symbol, but extra null", 55 | "bson": "0E00000002610001000000000000" 56 | }, 57 | { 58 | "description": "invalid UTF-8", 59 | "bson": "0E00000002610002000000E90000" 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/int32.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Integer", 3 | "bson_type": "0x10", 4 | "test_key": "i", 5 | "valid": [ 6 | { 7 | "description": "MinValue", 8 | "bson": "0C0000001069000000008000", 9 | "extjson": "{\"i\" : -2147483648}" 10 | }, 11 | { 12 | "description": "MaxValue", 13 | "bson": "0C000000106900FFFFFF7F00", 14 | "extjson": "{\"i\" : 2147483647}" 15 | }, 16 | { 17 | "description": "-1", 18 | "bson": "0C000000106900FFFFFFFF00", 19 | "extjson": "{\"i\" : -1}" 20 | }, 21 | { 22 | "description": "0", 23 | "bson": "0C0000001069000000000000", 24 | "extjson": "{\"i\" : 0}" 25 | }, 26 | { 27 | "description": "1", 28 | "bson": "0C0000001069000100000000", 29 | "extjson": "{\"i\" : 1}" 30 | } 31 | ], 32 | "decodeErrors": [ 33 | { 34 | "description": "Bad int32 field length", 35 | "bson": "090000001061000500" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/maxkey.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "MaxKey", 3 | "bson_type": "0xFF", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Maxkey", 8 | "bson": "080000007F610000", 9 | "extjson": "{\"a\" : {\"$maxKey\" : 1}}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/minkey.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "MinKey", 3 | "bson_type": "0xFF", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Minkey", 8 | "bson": "08000000FF610000", 9 | "extjson": "{\"a\" : {\"$minKey\" : 1}}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/null.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "NilClass", 3 | "bson_type": "0x0A", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Null", 8 | "bson": "080000000A610000", 9 | "extjson": "{\"a\" : null}" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/oid.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "ObjectId", 3 | "bson_type": "0x07", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "All zeroes", 8 | "bson": "1400000007610000000000000000000000000000", 9 | "extjson": "{\"a\" : {\"$oid\" : \"000000000000000000000000\"}}" 10 | }, 11 | { 12 | "description": "All ones", 13 | "bson": "14000000076100FFFFFFFFFFFFFFFFFFFFFFFF00", 14 | "extjson": "{\"a\" : {\"$oid\" : \"ffffffffffffffffffffffff\"}}" 15 | }, 16 | { 17 | "description": "Random", 18 | "bson": "1400000007610056E1FC72E0C917E9C471416100", 19 | "extjson": "{\"a\" : {\"$oid\" : \"56e1fc72e0c917e9c4714161\"}}" 20 | } 21 | ], 22 | "decodeErrors": [ 23 | { 24 | "description": "OID truncated", 25 | "bson": "1200000007610056E1FC72E0C917E9C471" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/regex.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Regexp", 3 | "bson_type": "0x0B", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "empty regex with no options", 8 | "bson": "0A0000000B6100000000", 9 | "extjson": "{\"a\" : {\"$regex\" : \"\", \"$options\" : \"\"}}" 10 | }, 11 | { 12 | "description": "regex without options", 13 | "bson": "0D0000000B6100616263000000", 14 | "extjson": "{\"a\" : {\"$regex\" : \"abc\", \"$options\" : \"\"}}" 15 | }, 16 | { 17 | "description": "regex with options", 18 | "bson": "0F0000000B610061626300696D0000", 19 | "extjson": "{\"a\" : {\"$regex\" : \"abc\", \"$options\" : \"im\"}}" 20 | }, 21 | { 22 | "description": "regex with slash", 23 | "bson": "110000000B610061622F636400696D0000", 24 | "extjson": "{\"a\" : {\"$regex\" : \"ab/cd\", \"$options\" : \"im\"}}" 25 | } 26 | ], 27 | "decodeErrors": [ 28 | { 29 | "description": "embedded null in pattern", 30 | "bson": "0F0000000B610061006300696D0000" 31 | }, 32 | { 33 | "description": "embedded null in flags", 34 | "bson": "100000000B61006162630069006D0000" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "String", 3 | "bson_type": "0x02", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Empty string", 8 | "bson": "0D000000026100010000000000", 9 | "extjson": "{\"a\" : \"\"}" 10 | }, 11 | { 12 | "description": "Single character", 13 | "bson": "0E00000002610002000000620000", 14 | "extjson": "{\"a\" : \"b\"}" 15 | }, 16 | { 17 | "description": "Multi-character", 18 | "bson": "190000000261000D0000006162616261626162616261620000", 19 | "extjson": "{\"a\" : \"abababababab\"}" 20 | }, 21 | { 22 | "description": "two-byte UTF-8 (\u00e9)", 23 | "bson": "190000000261000D000000C3A9C3A9C3A9C3A9C3A9C3A90000", 24 | "extjson": "{\"a\" : \"\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\\u00e9\"}" 25 | }, 26 | { 27 | "description": "three-byte UTF-8 (\u2606)", 28 | "bson": "190000000261000D000000E29886E29886E29886E298860000", 29 | "extjson": "{\"a\" : \"\\u2606\\u2606\\u2606\\u2606\"}" 30 | }, 31 | { 32 | "description": "Embedded nulls", 33 | "bson": "190000000261000D0000006162006261620062616261620000", 34 | "extjson": "{\"a\" : \"ab\\u0000bab\\u0000babab\"}" 35 | } 36 | ], 37 | "decodeErrors": [ 38 | { 39 | "description": "bad string length: 0 (but no 0x00 either)", 40 | "bson": "0C0000000261000000000000" 41 | }, 42 | { 43 | "description": "bad string length: -1", 44 | "bson": "0C000000026100FFFFFFFF00" 45 | }, 46 | { 47 | "description": "bad string length: eats terminator", 48 | "bson": "10000000026100050000006200620000" 49 | }, 50 | { 51 | "description": "bad string length: longer than rest of document", 52 | "bson": "120000000200FFFFFF00666F6F6261720000" 53 | }, 54 | { 55 | "description": "string is not null-terminated", 56 | "bson": "1000000002610004000000616263FF00" 57 | }, 58 | { 59 | "description": "empty string, but extra null", 60 | "bson": "0E00000002610001000000000000" 61 | }, 62 | { 63 | "description": "invalid UTF-8", 64 | "bson": "0E00000002610002000000E90000" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/timestamp.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Timestamp", 3 | "bson_type": "0x11", 4 | "test_key": "a", 5 | "valid": [ 6 | { 7 | "description": "Timestamp: (123456789, 42)", 8 | "bson": "100000001161002A00000015CD5B0700", 9 | "extjson": "{\"a\" : {\"$timestamp\" : {\"t\" : 123456789, \"i\" : 42}}}" 10 | } 11 | ], 12 | "decodeErrors": [ 13 | { 14 | "description": "Truncated timestamp field", 15 | "bson": "0f0000001161002A00000015CD5B00" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/top.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Top-level document validity", 3 | "bson_type": "0x00", 4 | "decodeErrors": [ 5 | { 6 | "description": "An object size that's too small to even include the object size, but is a well-formed, empty object", 7 | "bson": "0100000000" 8 | }, 9 | { 10 | "description": "An object size that's only enough for the object size, but is a well-formed, empty object", 11 | "bson": "0400000000" 12 | }, 13 | { 14 | "description": "One object, with length shorter than size (missing EOO)", 15 | "bson": "05000000" 16 | }, 17 | { 18 | "description": "One object, sized correctly, with a spot for an EOO, but the EOO is 0x01", 19 | "bson": "0500000001" 20 | }, 21 | { 22 | "description": "One object, sized correctly, with a spot for an EOO, but the EOO is 0xff", 23 | "bson": "05000000FF" 24 | }, 25 | { 26 | "description": "One object, sized correctly, with a spot for an EOO, but the EOO is 0x70", 27 | "bson": "0500000070" 28 | }, 29 | { 30 | "description": "Byte count is zero (with non-zero input length)", 31 | "bson": "00000000000000000000" 32 | }, 33 | { 34 | "description": "Stated length exceeds byte count, with truncated document", 35 | "bson": "1200000002666F6F0004000000626172" 36 | }, 37 | { 38 | "description": "Stated length less than byte count, with garbage after envelope", 39 | "bson": "1200000002666F6F00040000006261720000DEADBEEF" 40 | }, 41 | { 42 | "description": "Stated length exceeds byte count, with valid envelope", 43 | "bson": "1300000002666F6F00040000006261720000" 44 | }, 45 | { 46 | "description": "Stated length less than byte count, with valid envelope", 47 | "bson": "1100000002666F6F00040000006261720000" 48 | }, 49 | { 50 | "description": "Invalid BSON type low range", 51 | "bson": "07000000000000" 52 | }, 53 | { 54 | "description": "Invalid BSON type high range", 55 | "bson": "07000000800000" 56 | }, 57 | { 58 | "description": "Document truncated mid-key", 59 | "bson": "1200000002666F" 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /spec/spec_tests/data/corpus_legacy/undefined.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Undefined type (deprecated)", 3 | "bson_type": "0x06", 4 | "deprecated": true, 5 | "test_key": "a", 6 | "valid": [ 7 | { 8 | "description": "Undefined", 9 | "bson": "0800000006610000", 10 | "extjson": "{\"a\" : {\"$undefined\" : true}}" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /spec/support/spec_config.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | require 'singleton' 3 | 4 | class SpecConfig 5 | include Singleton 6 | 7 | COMPACTION_CHANCE = 0.001 8 | 9 | def active_support? 10 | %w(1 true yes).include?(ENV['WITH_ACTIVE_SUPPORT']) || 11 | ENV['WITH_ACTIVE_SUPPORT'] =~ /[0-9]/ && ENV['WITH_ACTIVE_SUPPORT'] != '0' 12 | end 13 | 14 | def compact? 15 | %w(1 true yes).include?(ENV['COMPACT']) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/utils.rb: -------------------------------------------------------------------------------- 1 | # rubocop:todo all 2 | module Utils 3 | extend self 4 | 5 | # JRuby chokes when strings like "\xfe\x00\xff", which are not valid UTF-8, 6 | # appear in the source. Use this method to build such strings. 7 | # char_array is an array of byte values to use for the string. 8 | def make_byte_string(char_array, encoding = 'BINARY') 9 | char_array.map do |char| 10 | char.chr.force_encoding('BINARY') 11 | end.join.force_encoding(encoding) 12 | end 13 | 14 | # Forks the current process and executes the given block in the child. 15 | # The value returned by the block is then returned in the parent process 16 | # by this method. 17 | # 18 | # @return [ Object ] the value returned by the block 19 | def perform_in_child(&block) 20 | reader, writer = IO.pipe 21 | 22 | if fork 23 | parent_worker(reader, writer) 24 | else 25 | child_worker(reader, writer, &block) 26 | end 27 | end 28 | 29 | private 30 | 31 | # A utility method for #perform_in_child, to handle tasks for the parent 32 | # side of the fork. 33 | # 34 | # @param [ IO ] reader The reader IO for the pipe 35 | # @param [ IO ] writer The writer IO for the pipe 36 | # 37 | # @return [ Object ] the value returned by the child process 38 | def parent_worker(reader, writer) 39 | writer.close 40 | blob = reader.read 41 | reader.close 42 | Process.wait 43 | Marshal.load(blob) 44 | end 45 | 46 | # A utility method for #perform_in_child, to handle tasks for the child 47 | # side of the fork. 48 | # 49 | # @param [ IO ] reader The reader IO for the pipe 50 | # @param [ IO ] writer The writer IO for the pipe 51 | def child_worker(reader, writer, &block) 52 | reader.close 53 | result = block.call 54 | writer.write Marshal.dump(result) 55 | writer.close 56 | exit! 0 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /src/main/org/bson_ruby/NativeService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2020 MongoDB Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.bson_ruby; 18 | 19 | import java.io.IOException; 20 | 21 | import org.jruby.Ruby; 22 | import org.jruby.RubyClass; 23 | import org.jruby.RubyModule; 24 | import org.jruby.runtime.load.BasicLibraryService; 25 | import org.jruby.runtime.ObjectAllocator; 26 | import org.jruby.runtime.builtin.IRubyObject; 27 | 28 | /** 29 | * The native implementation of various extensions. 30 | * 31 | * @since 2.0.0 32 | */ 33 | public class NativeService implements BasicLibraryService { 34 | 35 | /** 36 | * Constant for the BSON module name. 37 | * 38 | * @since 2.0.0 39 | */ 40 | private final String BSON = "BSON".intern(); 41 | 42 | /** 43 | * Constant for the BSON module name. 44 | * 45 | * @since 2.0.0 46 | */ 47 | private final String BYTE_BUF = "ByteBuff".intern(); 48 | 49 | /** 50 | * Loads the native extension into the JRuby runtime. 51 | * 52 | * @param runtime The Ruby runtime. 53 | * 54 | * @return Always returns true if no exception. 55 | * 56 | * @since 2.0.0 57 | */ 58 | public boolean basicLoad(final Ruby runtime) throws IOException { 59 | RubyModule bson = runtime.fastGetModule(BSON); 60 | GeneratorExtension.extend(bson); 61 | 62 | RubyClass byteBuffer = bson.defineClassUnder("ByteBuffer", runtime.getObject(), new ObjectAllocator() { 63 | public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) { 64 | return new ByteBuf(runtime, rubyClass); 65 | } 66 | }); 67 | 68 | byteBuffer.defineAnnotatedMethods(ByteBuf.class); 69 | return true; 70 | } 71 | } 72 | --------------------------------------------------------------------------------