bson 4.2.2 → 4.12.1

Sign up to get free protection for your applications and to get access to all the features.
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.