bson 4.2.2 → 4.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +25 -7
  5. data/Rakefile +16 -9
  6. data/ext/bson/{native-endian.h → bson-endian.h} +5 -99
  7. data/ext/bson/bson-native.h +125 -0
  8. data/ext/bson/bytebuf.c +133 -0
  9. data/ext/bson/endian.c +117 -0
  10. data/ext/bson/init.c +355 -0
  11. data/ext/bson/libbson-utf8.c +230 -0
  12. data/ext/bson/read.c +411 -0
  13. data/ext/bson/util.c +95 -0
  14. data/ext/bson/write.c +680 -0
  15. data/lib/bson.rb +6 -3
  16. data/lib/bson/active_support.rb +17 -0
  17. data/lib/bson/array.rb +57 -17
  18. data/lib/bson/binary.rb +185 -13
  19. data/lib/bson/boolean.rb +12 -3
  20. data/lib/bson/code.rb +16 -2
  21. data/lib/bson/code_with_scope.rb +32 -5
  22. data/lib/bson/config.rb +1 -1
  23. data/lib/bson/date.rb +12 -2
  24. data/lib/bson/date_time.rb +2 -2
  25. data/lib/bson/db_pointer.rb +110 -0
  26. data/lib/bson/decimal128.rb +17 -3
  27. data/lib/bson/decimal128/builder.rb +1 -1
  28. data/lib/bson/document.rb +152 -5
  29. data/lib/bson/environment.rb +2 -1
  30. data/lib/bson/error.rb +27 -0
  31. data/lib/bson/ext_json.rb +383 -0
  32. data/lib/bson/false_class.rb +1 -1
  33. data/lib/bson/float.rb +48 -2
  34. data/lib/bson/hash.rb +68 -17
  35. data/lib/bson/int32.rb +52 -13
  36. data/lib/bson/int64.rb +59 -15
  37. data/lib/bson/integer.rb +36 -2
  38. data/lib/bson/json.rb +1 -1
  39. data/lib/bson/max_key.rb +13 -1
  40. data/lib/bson/min_key.rb +13 -1
  41. data/lib/bson/nil_class.rb +4 -2
  42. data/lib/bson/object.rb +28 -1
  43. data/lib/bson/object_id.rb +16 -2
  44. data/lib/bson/open_struct.rb +1 -1
  45. data/lib/bson/regexp.rb +27 -4
  46. data/lib/bson/registry.rb +3 -3
  47. data/lib/bson/specialized.rb +4 -2
  48. data/lib/bson/string.rb +5 -3
  49. data/lib/bson/symbol.rb +99 -7
  50. data/lib/bson/time.rb +63 -4
  51. data/lib/bson/time_with_zone.rb +54 -0
  52. data/lib/bson/timestamp.rb +44 -6
  53. data/lib/bson/true_class.rb +1 -1
  54. data/lib/bson/undefined.rb +12 -1
  55. data/lib/bson/version.rb +2 -2
  56. data/spec/bson/array_spec.rb +18 -1
  57. data/spec/bson/binary_spec.rb +100 -3
  58. data/spec/bson/binary_uuid_spec.rb +189 -0
  59. data/spec/bson/boolean_spec.rb +1 -1
  60. data/spec/bson/byte_buffer_read_spec.rb +197 -0
  61. data/spec/bson/byte_buffer_spec.rb +121 -381
  62. data/spec/bson/byte_buffer_write_spec.rb +854 -0
  63. data/spec/bson/code_spec.rb +1 -1
  64. data/spec/bson/code_with_scope_spec.rb +1 -1
  65. data/spec/bson/date_spec.rb +1 -1
  66. data/spec/bson/date_time_spec.rb +54 -1
  67. data/spec/bson/decimal128_spec.rb +35 -35
  68. data/spec/bson/document_as_spec.rb +46 -0
  69. data/spec/bson/document_spec.rb +197 -30
  70. data/spec/bson/ext_json_parse_spec.rb +308 -0
  71. data/spec/bson/false_class_spec.rb +1 -1
  72. data/spec/bson/float_spec.rb +37 -1
  73. data/spec/bson/hash_as_spec.rb +57 -0
  74. data/spec/bson/hash_spec.rb +209 -1
  75. data/spec/bson/int32_spec.rb +180 -6
  76. data/spec/bson/int64_spec.rb +199 -6
  77. data/spec/bson/integer_spec.rb +29 -3
  78. data/spec/bson/json_spec.rb +1 -1
  79. data/spec/bson/max_key_spec.rb +1 -1
  80. data/spec/bson/min_key_spec.rb +1 -1
  81. data/spec/bson/nil_class_spec.rb +1 -1
  82. data/spec/bson/object_id_spec.rb +1 -1
  83. data/spec/bson/object_spec.rb +1 -1
  84. data/spec/bson/open_struct_spec.rb +1 -1
  85. data/spec/bson/raw_spec.rb +34 -2
  86. data/spec/bson/regexp_spec.rb +1 -1
  87. data/spec/bson/registry_spec.rb +1 -1
  88. data/spec/bson/string_spec.rb +19 -1
  89. data/spec/bson/symbol_raw_spec.rb +45 -0
  90. data/spec/bson/symbol_spec.rb +63 -3
  91. data/spec/bson/time_spec.rb +205 -2
  92. data/spec/bson/time_with_zone_spec.rb +68 -0
  93. data/spec/bson/timestamp_spec.rb +56 -1
  94. data/spec/bson/true_class_spec.rb +1 -1
  95. data/spec/bson/undefined_spec.rb +1 -1
  96. data/spec/bson_spec.rb +1 -1
  97. data/spec/{support → runners}/common_driver.rb +1 -1
  98. data/spec/runners/corpus.rb +185 -0
  99. data/spec/{support/corpus.rb → runners/corpus_legacy.rb} +41 -59
  100. data/spec/spec_helper.rb +40 -3
  101. data/spec/{bson/driver_bson_spec.rb → spec_tests/common_driver_spec.rb} +1 -0
  102. data/spec/{bson/corpus_spec.rb → spec_tests/corpus_legacy_spec.rb} +10 -7
  103. data/spec/spec_tests/corpus_spec.rb +124 -0
  104. data/spec/spec_tests/data/corpus/README.md +15 -0
  105. data/spec/spec_tests/data/corpus/array.json +49 -0
  106. data/spec/spec_tests/data/corpus/binary.json +113 -0
  107. data/spec/spec_tests/data/corpus/boolean.json +27 -0
  108. data/spec/spec_tests/data/corpus/code.json +67 -0
  109. data/spec/spec_tests/data/corpus/code_w_scope.json +78 -0
  110. data/spec/spec_tests/data/corpus/datetime.json +42 -0
  111. data/spec/spec_tests/data/corpus/dbpointer.json +56 -0
  112. data/spec/spec_tests/data/corpus/dbref.json +31 -0
  113. data/spec/spec_tests/data/corpus/decimal128-1.json +317 -0
  114. data/spec/spec_tests/data/corpus/decimal128-2.json +793 -0
  115. data/spec/spec_tests/data/corpus/decimal128-3.json +1771 -0
  116. data/spec/spec_tests/data/corpus/decimal128-4.json +117 -0
  117. data/spec/spec_tests/data/corpus/decimal128-5.json +402 -0
  118. data/spec/spec_tests/data/corpus/decimal128-6.json +119 -0
  119. data/spec/spec_tests/data/corpus/decimal128-7.json +323 -0
  120. data/spec/spec_tests/data/corpus/document.json +36 -0
  121. data/spec/spec_tests/data/corpus/double.json +87 -0
  122. data/spec/spec_tests/data/corpus/int32.json +43 -0
  123. data/spec/spec_tests/data/corpus/int64.json +43 -0
  124. data/spec/spec_tests/data/corpus/maxkey.json +12 -0
  125. data/spec/spec_tests/data/corpus/minkey.json +12 -0
  126. data/spec/spec_tests/data/corpus/multi-type-deprecated.json +15 -0
  127. data/spec/spec_tests/data/corpus/multi-type.json +11 -0
  128. data/spec/spec_tests/data/corpus/null.json +12 -0
  129. data/spec/spec_tests/data/corpus/oid.json +28 -0
  130. data/spec/spec_tests/data/corpus/regex.json +65 -0
  131. data/spec/spec_tests/data/corpus/string.json +72 -0
  132. data/spec/spec_tests/data/corpus/symbol.json +80 -0
  133. data/spec/spec_tests/data/corpus/timestamp.json +34 -0
  134. data/spec/spec_tests/data/corpus/top.json +236 -0
  135. data/spec/spec_tests/data/corpus/undefined.json +15 -0
  136. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/array.json +8 -2
  137. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/binary.json +0 -0
  138. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/boolean.json +0 -0
  139. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code.json +1 -1
  140. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/code_w_scope.json +1 -1
  141. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/document.json +1 -1
  142. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/double.json +1 -1
  143. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/datetime.json +0 -0
  144. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/dbpointer.json +0 -0
  145. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/int64.json +0 -0
  146. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/failures/symbol.json +0 -0
  147. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/int32.json +1 -1
  148. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/maxkey.json +1 -1
  149. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/minkey.json +1 -1
  150. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/null.json +1 -1
  151. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/oid.json +0 -0
  152. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/regex.json +1 -1
  153. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/string.json +0 -0
  154. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/timestamp.json +1 -1
  155. data/spec/{support/corpus-tests → spec_tests/data/corpus_legacy}/top.json +0 -0
  156. data/spec/{support/corpus-tests/failures → spec_tests/data/corpus_legacy}/undefined.json +0 -0
  157. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-1.json +0 -0
  158. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-2.json +0 -0
  159. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-3.json +0 -0
  160. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-4.json +0 -0
  161. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-5.json +0 -0
  162. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-6.json +0 -0
  163. data/spec/{support/driver-spec-tests → spec_tests/data}/decimal128/decimal128-7.json +0 -0
  164. data/spec/support/shared_examples.rb +3 -5
  165. data/spec/support/spec_config.rb +16 -0
  166. data/spec/support/utils.rb +10 -0
  167. metadata +227 -124
  168. metadata.gz.sig +0 -0
  169. data/ext/bson/bson_native.c +0 -762
data/lib/bson.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@ module BSON
57
57
  end
58
58
 
59
59
  require "bson/config"
60
+ require "bson/error"
60
61
  require "bson/registry"
61
62
  require "bson/specialized"
62
63
  require "bson/json"
@@ -70,8 +71,10 @@ require "bson/code"
70
71
  require "bson/code_with_scope"
71
72
  require "bson/date"
72
73
  require "bson/date_time"
74
+ require "bson/db_pointer"
73
75
  require "bson/decimal128"
74
76
  require "bson/document"
77
+ require "bson/ext_json"
75
78
  require "bson/false_class"
76
79
  require "bson/float"
77
80
  require "bson/hash"
@@ -101,7 +104,7 @@ begin
101
104
  else
102
105
  require "bson_native"
103
106
  end
104
- rescue LoadError
105
- $stderr.puts("Failed to load the necessary extensions.")
107
+ rescue LoadError => e
108
+ $stderr.puts("Failed to load the necessary extensions: #{e.class}: #{e}")
106
109
  raise
107
110
  end
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2018-2020 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Require this file if using BSON with ActiveSupport.
16
+
17
+ require "bson/time_with_zone"
data/lib/bson/array.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -41,15 +41,22 @@ module BSON
41
41
  #
42
42
  # @since 2.0.0
43
43
  def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
44
- position = buffer.length
45
- buffer.put_int32(0)
46
- each_with_index do |value, index|
47
- buffer.put_byte(value.bson_type)
48
- buffer.put_cstring(index.to_s)
49
- value.to_bson(buffer, validating_keys)
44
+ if buffer.respond_to?(:put_array)
45
+ buffer.put_array(self, validating_keys)
46
+ else
47
+ position = buffer.length
48
+ buffer.put_int32(0)
49
+ each_with_index do |value, index|
50
+ unless value.respond_to?(:bson_type)
51
+ raise Error::UnserializableClass, "Array element at position #{index} does not define its BSON serialized type: #{value}"
52
+ end
53
+ buffer.put_byte(value.bson_type)
54
+ buffer.put_cstring(index.to_s)
55
+ value.to_bson(buffer, validating_keys)
56
+ end
57
+ buffer.put_byte(NULL_BYTE)
58
+ buffer.replace_int32(position, buffer.length - position)
50
59
  end
51
- buffer.put_byte(NULL_BYTE)
52
- buffer.replace_int32(position, buffer.length - position)
53
60
  end
54
61
 
55
62
  # Convert the array to an object id. This will only work for arrays of size
@@ -60,7 +67,7 @@ module BSON
60
67
  #
61
68
  # @note This is used for repairing legacy bson data.
62
69
  #
63
- # @raise [ InvalidObjectId ] If the array is not 12 elements.
70
+ # @raise [ BSON::ObjectId::Invalid ] If the array is not 12 elements.
64
71
  #
65
72
  # @return [ String ] The raw object id bytes.
66
73
  #
@@ -81,25 +88,58 @@ module BSON
81
88
  map { |value| value.to_bson_normalized_value }
82
89
  end
83
90
 
91
+ # Converts this object to a representation directly serializable to
92
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
93
+ #
94
+ # This method recursively invokes +as_extended_json+ with the provided
95
+ # options on each array element.
96
+ #
97
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
98
+ # (default is canonical extended JSON)
99
+ #
100
+ # @return [ Array ] This array converted to extended json representation.
101
+ def as_extended_json(**options)
102
+ map do |item|
103
+ item.as_extended_json(**options)
104
+ end
105
+ end
106
+
84
107
  module ClassMethods
85
108
 
86
109
  # Deserialize the array from BSON.
87
110
  #
88
111
  # @param [ ByteBuffer ] buffer The byte buffer.
89
112
  #
113
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
114
+ #
90
115
  # @return [ Array ] The decoded array.
91
116
  #
92
117
  # @see http://bsonspec.org/#/specification
93
118
  #
94
119
  # @since 2.0.0
95
- def from_bson(buffer)
96
- array = new
97
- buffer.get_int32 # throw away the length
98
- while (type = buffer.get_byte) != NULL_BYTE
99
- buffer.get_cstring
100
- array << BSON::Registry.get(type).from_bson(buffer)
120
+ def from_bson(buffer, **options)
121
+ if buffer.respond_to?(:get_array)
122
+ buffer.get_array(**options)
123
+ else
124
+ array = new
125
+ start_position = buffer.read_position
126
+ expected_byte_size = buffer.get_int32
127
+ while (type = buffer.get_byte) != NULL_BYTE
128
+ buffer.get_cstring
129
+ cls = BSON::Registry.get(type)
130
+ value = if options.empty?
131
+ cls.from_bson(buffer)
132
+ else
133
+ cls.from_bson(buffer, **options)
134
+ end
135
+ array << value
136
+ end
137
+ actual_byte_size = buffer.read_position - start_position
138
+ if actual_byte_size != expected_byte_size
139
+ raise Error::BSONDecodeError, "Expected array to take #{expected_byte_size} bytes but it took #{actual_byte_size} bytes"
140
+ end
141
+ array
101
142
  end
102
- array
103
143
  end
104
144
  end
105
145
 
data/lib/bson/binary.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -31,6 +31,12 @@ module BSON
31
31
 
32
32
  # The mappings of subtypes to their single byte identifiers.
33
33
  #
34
+ # @note subtype 6 (ciphertext) is used for the Client-Side Encryption
35
+ # feature. Data represented by this subtype is often encrypted, but
36
+ # may also be plaintext. All instances of this subtype necessary for
37
+ # Client-Side Encryption will be created internally by the Ruby driver.
38
+ # An application should not create new BSON::Binary objects of this subtype.
39
+ #
34
40
  # @since 2.0.0
35
41
  SUBTYPES = {
36
42
  :generic => 0.chr,
@@ -39,6 +45,7 @@ module BSON
39
45
  :uuid_old => 3.chr,
40
46
  :uuid => 4.chr,
41
47
  :md5 => 5.chr,
48
+ :ciphertext => 6.chr,
42
49
  :user => 128.chr
43
50
  }.freeze
44
51
 
@@ -47,13 +54,17 @@ module BSON
47
54
  # @since 2.0.0
48
55
  TYPES = SUBTYPES.invert.freeze
49
56
 
50
- # @!attribute data
51
- # @return [ Object ] The raw binary data.
52
- # @since 2.0.0
53
- # @!attribute type
54
- # @return [ Symbol ] The binary type.
55
- # @since 2.0.0
56
- attr_reader :data, :type
57
+ # @return [ String ] The raw binary data.
58
+ #
59
+ # The string is always stored in BINARY encoding.
60
+ #
61
+ # @since 2.0.0
62
+ attr_reader :data
63
+
64
+ # @return [ Symbol ] The binary type.
65
+ #
66
+ # @since 2.0.0
67
+ attr_reader :type
57
68
 
58
69
  # Determine if this binary object is equal to another object.
59
70
  #
@@ -90,21 +101,61 @@ module BSON
90
101
  # @return [ Hash ] The binary as a JSON hash.
91
102
  #
92
103
  # @since 2.0.0
104
+ # @deprecated Use as_extended_json instead.
93
105
  def as_json(*args)
94
- { "$binary" => Base64.encode64(data), "$type" => type }
106
+ as_extended_json
107
+ end
108
+
109
+ # Converts this object to a representation directly serializable to
110
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
111
+ #
112
+ # @option opts [ nil | :relaxed | :legacy ] :mode Serialization mode
113
+ # (default is canonical extended JSON)
114
+ #
115
+ # @return [ Hash ] The extended json representation.
116
+ def as_extended_json(**options)
117
+ subtype = SUBTYPES[type].each_byte.map { |c| c.to_s(16) }.join
118
+ if subtype.length == 1
119
+ subtype = "0#{subtype}"
120
+ end
121
+
122
+ value = Base64.encode64(data).strip
123
+
124
+ if options[:mode] == :legacy
125
+ { "$binary" => value, "$type" => subtype }
126
+ else
127
+ { "$binary" => {'base64' => value, "subType" => subtype }}
128
+ end
95
129
  end
96
130
 
97
131
  # Instantiate the new binary object.
98
132
  #
133
+ # This method accepts a string in any encoding; however, if a string is
134
+ # of a non-BINARY encoding, the encoding is set to BINARY. This does not
135
+ # change the bytes of the string but it means that applications referencing
136
+ # the data of a Binary instance cannot assume it is in a non-binary
137
+ # encoding, even if the string given to the constructor was in such an
138
+ # encoding.
139
+ #
99
140
  # @example Instantiate a binary.
100
141
  # BSON::Binary.new(data, :md5)
101
142
  #
102
- # @param [ Object ] data The raw binary data.
143
+ # @param [ String ] data The raw binary data.
103
144
  # @param [ Symbol ] type The binary type.
104
145
  #
105
146
  # @since 2.0.0
106
147
  def initialize(data = "", type = :generic)
107
148
  validate_type!(type)
149
+
150
+ # The Binary class used to force encoding to BINARY when serializing to
151
+ # BSON. Instead of doing that during serialization, perform this
152
+ # operation during Binary construction to make it clear that once
153
+ # the string is given to the Binary, the data is treated as a binary
154
+ # string and not a text string in any encoding.
155
+ unless data.encoding == Encoding.find('BINARY')
156
+ data = data.dup.force_encoding('BINARY')
157
+ end
158
+
108
159
  @data = data
109
160
  @type = type
110
161
  end
@@ -121,6 +172,70 @@ module BSON
121
172
  "<BSON::Binary:0x#{object_id} type=#{type} data=0x#{data[0, 8].unpack('H*').first}...>"
122
173
  end
123
174
 
175
+ # Returns a string representation of the UUID stored in this Binary.
176
+ #
177
+ # If the Binary is of subtype 4 (:uuid), this method returns the UUID
178
+ # in RFC 4122 format. If the representation parameter is provided, it
179
+ # must be the value :standard as a symbol or a string.
180
+ #
181
+ # If the Binary is of subtype 3 (:uuid_old), this method requires that
182
+ # the representation parameter is provided and is one of :csharp_legacy,
183
+ # :java_legacy or :python_legacy or the equivalent strings. In this case
184
+ # the method assumes the Binary stores the UUID in the specified format,
185
+ # transforms the stored bytes to the standard RFC 4122 representation
186
+ # and returns the UUID in RFC 4122 format.
187
+ #
188
+ # If the Binary is of another subtype, this method raises TypeError.
189
+ #
190
+ # @param [ Symbol ] representation How to interpret the UUID.
191
+ #
192
+ # @return [ String ] The string representation of the UUID.
193
+ #
194
+ # @raise [ TypeError ] If the subtype of Binary is not :uuid nor :uuid_old.
195
+ # @raise [ ArgumentError ] If the representation other than :standard
196
+ # is requested for Binary subtype 4 (:uuid), if :standard representation
197
+ # is requested for Binary subtype 3 (:uuid_old), or if an invalid
198
+ # representation is requested.
199
+ #
200
+ # @api experimental
201
+ def to_uuid(representation = nil)
202
+ if representation.is_a?(String)
203
+ raise ArgumentError, "Representation must be given as a symbol: #{representation}"
204
+ end
205
+ case type
206
+ when :uuid
207
+ if representation && representation != :standard
208
+ raise ArgumentError, "Binary of type :uuid can only be stringified to :standard representation, requested: #{representation.inspect}"
209
+ end
210
+
211
+ data.split('').map { |n| '%02x' % n.ord }.join.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
212
+ when :uuid_old
213
+ if representation.nil?
214
+ raise ArgumentError, 'Representation must be specified for BSON::Binary objects of type :uuid_old'
215
+ end
216
+
217
+ hex = data.split('').map { |n| '%02x' % n.ord }.join
218
+
219
+ case representation
220
+ when :standard
221
+ raise ArgumentError, 'BSON::Binary objects of type :uuid_old cannot be stringified to :standard representation'
222
+ when :csharp_legacy
223
+ hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(.{16})\z/, '\4\3\2\1\6\5\8\7\9')
224
+ when :java_legacy
225
+ hex.sub(/\A(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)(..)\z/) do |m|
226
+ "#{$8}#{$7}#{$6}#{$5}#{$4}#{$3}#{$2}#{$1}" +
227
+ "#{$16}#{$15}#{$14}#{$13}#{$12}#{$11}#{$10}#{$9}"
228
+ end
229
+ when :python_legacy
230
+ hex
231
+ else
232
+ raise ArgumentError, "Invalid representation: #{representation}"
233
+ end.sub(/\A(.{8})(.{4})(.{4})(.{4})(.{12})\z/, '\1-\2-\3-\4-\5')
234
+ else
235
+ raise TypeError, "The type of Binary must be :uuid or :uuid_old, this object is: #{type.inspect}"
236
+ end
237
+ end
238
+
124
239
  # Encode the binary type
125
240
  #
126
241
  # @example Encode the binary.
@@ -136,7 +251,7 @@ module BSON
136
251
  buffer.put_int32(0)
137
252
  buffer.put_byte(SUBTYPES[type])
138
253
  buffer.put_int32(data.bytesize) if type == :old
139
- buffer.put_bytes(data.force_encoding(BINARY))
254
+ buffer.put_bytes(data)
140
255
  buffer.replace_int32(position, buffer.length - position - 5)
141
256
  end
142
257
 
@@ -144,19 +259,76 @@ module BSON
144
259
  #
145
260
  # @param [ ByteBuffer ] buffer The byte buffer.
146
261
  #
262
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
263
+ #
147
264
  # @return [ Binary ] The decoded binary data.
148
265
  #
149
266
  # @see http://bsonspec.org/#/specification
150
267
  #
151
268
  # @since 2.0.0
152
- def self.from_bson(buffer)
269
+ def self.from_bson(buffer, **options)
153
270
  length = buffer.get_int32
154
- type = TYPES[buffer.get_byte]
271
+ type_byte = buffer.get_byte
272
+ type = TYPES[type_byte]
273
+ if type.nil?
274
+ raise Error::UnsupportedBinarySubtype,
275
+ "BSON data contains unsupported binary subtype #{'0x%02x' % type_byte.ord}"
276
+ end
277
+
155
278
  length = buffer.get_int32 if type == :old
156
279
  data = buffer.get_bytes(length)
157
280
  new(data, type)
158
281
  end
159
282
 
283
+ # Creates a BSON::Binary from a string representation of a UUID.
284
+ #
285
+ # The UUID may be given in either 00112233-4455-6677-8899-aabbccddeeff or
286
+ # 00112233445566778899AABBCCDDEEFF format - specifically, any dashes in
287
+ # the UUID are removed and both upper and lower case letters are acceptable.
288
+ #
289
+ # The input UUID string is always interpreted to be in the RFC 4122 format.
290
+ #
291
+ # If representation is not provided, this method creates a BSON::Binary
292
+ # of subtype 4 (:uuid). If representation is provided, it must be one of
293
+ # :standard, :csharp_legacy, :java_legacy or :python_legacy. If
294
+ # representation is :standard, this method creates a subtype 4 (:uuid)
295
+ # binary which is the same behavior as if representation was not provided.
296
+ # For other representations, this method creates a Binary of subtype 3
297
+ # (:uuid_old) with the UUID converted to the appropriate legacy MongoDB
298
+ # UUID storage format.
299
+ #
300
+ # @param [ String ] uuid The string representation of the UUID.
301
+ # @param [ Symbol ] representation How to interpret the UUID.
302
+ #
303
+ # @return [ Binary ] The binary.
304
+ #
305
+ # @raise [ ArgumentError ] If invalid representation is requested.
306
+ #
307
+ # @api experimental
308
+ def self.from_uuid(uuid, representation = nil)
309
+ if representation.is_a?(String)
310
+ raise ArgumentError, "Representation must be given as a symbol: #{representation}"
311
+ end
312
+ uuid_binary = uuid.gsub('-', '').scan(/../).map(&:hex).map(&:chr).join
313
+ case representation && representation
314
+ when nil, :standard
315
+ new(uuid_binary, :uuid)
316
+ when :csharp_legacy
317
+ uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.{8})\z/, '\4\3\2\1\6\5\8\7\9')
318
+ new(uuid_binary, :uuid_old)
319
+ when :java_legacy
320
+ uuid_binary.sub!(/\A(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\z/) do |m|
321
+ "#{$8}#{$7}#{$6}#{$5}#{$4}#{$3}#{$2}#{$1}" +
322
+ "#{$16}#{$15}#{$14}#{$13}#{$12}#{$11}#{$10}#{$9}"
323
+ end
324
+ new(uuid_binary, :uuid_old)
325
+ when :python_legacy
326
+ new(uuid_binary, :uuid_old)
327
+ else
328
+ raise ArgumentError, "Invalid representation: #{representation}"
329
+ end
330
+ end
331
+
160
332
  # Raised when providing an invalid type to the Binary.
161
333
  #
162
334
  # @since 2.0.0
data/lib/bson/boolean.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009-2014 MongoDB Inc.
1
+ # Copyright (C) 2009-2020 MongoDB Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -31,13 +31,22 @@ module BSON
31
31
  #
32
32
  # @param [ ByteBuffer ] buffer The byte buffer.
33
33
  #
34
+ # @option options [ nil | :bson ] :mode Decoding mode to use.
35
+ #
34
36
  # @return [ TrueClass, FalseClass ] The decoded boolean.
35
37
  #
36
38
  # @see http://bsonspec.org/#/specification
37
39
  #
38
40
  # @since 2.0.0
39
- def self.from_bson(buffer)
40
- buffer.get_byte == TrueClass::TRUE_BYTE
41
+ def self.from_bson(buffer, **options)
42
+ case v = buffer.get_byte
43
+ when TrueClass::TRUE_BYTE
44
+ true
45
+ when FalseClass::FALSE_BYTE
46
+ false
47
+ else
48
+ raise Error::BSONDecodeError, "Invalid boolean byte value: #{v}"
49
+ end
41
50
  end
42
51
 
43
52
  # Register this type when the module is loaded.