bson 4.1.1-java → 4.2.0-java

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 (82) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +18 -3
  5. data/lib/bson-ruby.jar +0 -0
  6. data/lib/bson.rb +6 -4
  7. data/lib/bson/array.rb +1 -1
  8. data/lib/bson/binary.rb +4 -2
  9. data/lib/bson/code.rb +1 -1
  10. data/lib/bson/code_with_scope.rb +1 -1
  11. data/lib/bson/config.rb +1 -1
  12. data/lib/bson/date.rb +1 -1
  13. data/lib/bson/date_time.rb +1 -1
  14. data/lib/bson/decimal128.rb +318 -0
  15. data/lib/bson/decimal128/builder.rb +448 -0
  16. data/lib/bson/document.rb +2 -2
  17. data/lib/bson/environment.rb +13 -1
  18. data/lib/bson/false_class.rb +1 -1
  19. data/lib/bson/float.rb +1 -1
  20. data/lib/bson/hash.rb +1 -1
  21. data/lib/bson/int32.rb +46 -0
  22. data/lib/bson/int64.rb +46 -0
  23. data/lib/bson/integer.rb +1 -1
  24. data/lib/bson/max_key.rb +1 -1
  25. data/lib/bson/min_key.rb +1 -1
  26. data/lib/bson/object_id.rb +3 -2
  27. data/lib/bson/open_struct.rb +57 -0
  28. data/lib/bson/regexp.rb +87 -19
  29. data/lib/bson/registry.rb +1 -1
  30. data/lib/bson/specialized.rb +1 -1
  31. data/lib/bson/string.rb +1 -1
  32. data/lib/bson/symbol.rb +1 -1
  33. data/lib/bson/time.rb +1 -1
  34. data/lib/bson/timestamp.rb +2 -2
  35. data/lib/bson/true_class.rb +1 -1
  36. data/lib/bson/version.rb +2 -2
  37. data/spec/bson/array_spec.rb +1 -1
  38. data/spec/bson/binary_spec.rb +2 -1
  39. data/spec/bson/corpus_spec.rb +68 -0
  40. data/spec/bson/decimal128_spec.rb +1583 -0
  41. data/spec/bson/document_spec.rb +1 -1
  42. data/spec/bson/driver_bson_spec.rb +77 -0
  43. data/spec/bson/int32_spec.rb +58 -0
  44. data/spec/bson/int64_spec.rb +58 -0
  45. data/spec/bson/open_struct_spec.rb +144 -0
  46. data/spec/bson/raw_spec.rb +540 -0
  47. data/spec/bson/regexp_spec.rb +7 -7
  48. data/spec/bson/timestamp_spec.rb +1 -1
  49. data/spec/spec_helper.rb +5 -0
  50. data/spec/support/common_driver.rb +347 -0
  51. data/spec/support/corpus-tests/array.json +43 -0
  52. data/spec/support/corpus-tests/boolean.json +27 -0
  53. data/spec/support/corpus-tests/code.json +67 -0
  54. data/spec/support/corpus-tests/code_w_scope.json +78 -0
  55. data/spec/support/corpus-tests/document.json +36 -0
  56. data/spec/support/corpus-tests/double.json +69 -0
  57. data/spec/support/corpus-tests/failures/binary.json +69 -0
  58. data/spec/support/corpus-tests/failures/datetime.json +31 -0
  59. data/spec/support/corpus-tests/failures/dbpointer.json +42 -0
  60. data/spec/support/corpus-tests/failures/int64.json +38 -0
  61. data/spec/support/corpus-tests/failures/symbol.json +62 -0
  62. data/spec/support/corpus-tests/failures/undefined.json +13 -0
  63. data/spec/support/corpus-tests/int32.json +38 -0
  64. data/spec/support/corpus-tests/maxkey.json +12 -0
  65. data/spec/support/corpus-tests/minkey.json +12 -0
  66. data/spec/support/corpus-tests/null.json +12 -0
  67. data/spec/support/corpus-tests/oid.json +28 -0
  68. data/spec/support/corpus-tests/regex.json +37 -0
  69. data/spec/support/corpus-tests/string.json +67 -0
  70. data/spec/support/corpus-tests/timestamp.json +18 -0
  71. data/spec/support/corpus-tests/top.json +62 -0
  72. data/spec/support/corpus.rb +265 -0
  73. data/spec/support/driver-spec-tests/decimal128/decimal128-1.json +363 -0
  74. data/spec/support/driver-spec-tests/decimal128/decimal128-2.json +793 -0
  75. data/spec/support/driver-spec-tests/decimal128/decimal128-3.json +1771 -0
  76. data/spec/support/driver-spec-tests/decimal128/decimal128-4.json +165 -0
  77. data/spec/support/driver-spec-tests/decimal128/decimal128-5.json +402 -0
  78. data/spec/support/driver-spec-tests/decimal128/decimal128-6.json +131 -0
  79. data/spec/support/driver-spec-tests/decimal128/decimal128-7.json +327 -0
  80. data/spec/support/shared_examples.rb +1 -1
  81. metadata +77 -4
  82. metadata.gz.sig +0 -0
@@ -0,0 +1,448 @@
1
+ # Copyright (C) 2016 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
+ module BSON
16
+ class Decimal128
17
+
18
+ # Helper module for parsing String, Integer, Float, BigDecimal, and Decimal128
19
+ # objects into other objects.
20
+ #
21
+ # @api private
22
+ #
23
+ # @since 4.2.0
24
+ module Builder
25
+
26
+ # Infinity mask.
27
+ #
28
+ # @since 4.2.0
29
+ INFINITY_MASK = 0x7800000000000000.freeze
30
+
31
+ # NaN mask.
32
+ #
33
+ # @since 4.2.0
34
+ NAN_MASK = 0x7c00000000000000.freeze
35
+
36
+ # SNaN mask.
37
+ #
38
+ # @since 4.2.0
39
+ SNAN_MASK = (1 << 57).freeze
40
+
41
+ # Signed bit mask.
42
+ #
43
+ # @since 4.2.0
44
+ SIGN_BIT_MASK = (1 << 63).freeze
45
+
46
+ # The two highest bits of the 64 high order bits.
47
+ #
48
+ # @since 4.2.0
49
+ TWO_HIGHEST_BITS_SET = (3 << 61).freeze
50
+
51
+ extend self
52
+
53
+ # Convert parts representing a Decimal128 into the corresponding bits.
54
+ #
55
+ # @param [ Integer ] significand The significand.
56
+ # @param [ Integer ] exponent The exponent.
57
+ # @param [ true, false ] is_negative Whether the value is negative.
58
+ #
59
+ # @return [ Array ] Tuple of the low and high bits.
60
+ #
61
+ # @since 4.2.0
62
+ def parts_to_bits(significand, exponent, is_negative)
63
+ validate_range!(exponent, significand)
64
+ exponent = exponent + Decimal128::EXPONENT_OFFSET
65
+ high = significand >> 64
66
+ low = (high << 64) ^ significand
67
+
68
+ if high >> 49 == 1
69
+ high = high & 0x7fffffffffff
70
+ high |= TWO_HIGHEST_BITS_SET
71
+ high |= (exponent & 0x3fff) << 47
72
+ else
73
+ high |= exponent << 49
74
+ end
75
+
76
+ if is_negative
77
+ high |= SIGN_BIT_MASK
78
+ end
79
+
80
+
81
+ [ low, high ]
82
+ end
83
+
84
+ private
85
+
86
+ def validate_range!(exponent, significand)
87
+ unless valid_significand?(significand) && valid_exponent?(exponent)
88
+ raise Decimal128::InvalidRange.new
89
+ end
90
+ end
91
+
92
+ def valid_significand?(significand)
93
+ significand.to_s.length <= Decimal128::MAX_DIGITS_OF_PRECISION
94
+ end
95
+
96
+ def valid_exponent?(exponent)
97
+ exponent <= Decimal128::MAX_EXPONENT && exponent >= Decimal128::MIN_EXPONENT
98
+ end
99
+
100
+ # Helper class for parsing a String into Decimal128 high and low bits.
101
+ #
102
+ # @api private
103
+ #
104
+ # @since 4.2.0
105
+ class FromString
106
+
107
+ # Regex matching a string representing NaN.
108
+ #
109
+ # @return [ Regex ] A regex matching a NaN string.
110
+ #
111
+ # @since 4.2.0
112
+ NAN_REGEX = /^(\-)?(S)?NaN$/i.freeze
113
+
114
+ # Regex matching a string representing positive or negative Infinity.
115
+ #
116
+ # @return [ Regex ] A regex matching a positive or negative Infinity string.
117
+ #
118
+ # @since 4.2.0
119
+ INFINITY_REGEX = /^(\+|\-)?Inf(inity)?$/i.freeze
120
+
121
+ # Regex for the fraction, including leading zeros.
122
+ #
123
+ # @return [ Regex ] The regex for matching the fraction,
124
+ # including leading zeros.
125
+ #
126
+ # @since 4.2.0
127
+ SIGNIFICAND_WITH_LEADING_ZEROS_REGEX = /(0*)(\d+)/.freeze
128
+
129
+ # Regex for separating a negative sign from the significands.
130
+ #
131
+ # @return [ Regex ] The regex for separating a sign from significands.
132
+ #
133
+ # @since 4.2.0
134
+ SIGN_AND_DIGITS_REGEX = /^(\-)?(\S+)/.freeze
135
+
136
+ # Regex matching a scientific exponent.
137
+ #
138
+ # @return [ Regex ] A regex matching E, e, E+, e+.
139
+ #
140
+ # @since 4.2.0
141
+ SCIENTIFIC_EXPONENT_REGEX = /E\+?/i.freeze
142
+
143
+ # Regex for capturing trailing zeros.
144
+ #
145
+ # @since 4.2.0
146
+ TRAILING_ZEROS_REGEX = /[1-9]*(0+)$/.freeze
147
+
148
+ # Regex for a valid decimal128 string format.
149
+ #
150
+ # @return [ Regex ] The regex for a valid decimal128 string.
151
+ #
152
+ # @since 4.2.0
153
+ VALID_DECIMAL128_STRING_REGEX = /^[\-\+]?(\d+(\.\d*)?|\.\d+)(E[\-\+]?\d+)?$/i.freeze
154
+
155
+ # Initialize the FromString Builder object.
156
+ #
157
+ # @example Create the FromString builder.
158
+ # Builder::FromString.new(string)
159
+ #
160
+ # @param [ String ] string The string to create a Decimal128 from.
161
+ #
162
+ # @since 4.2.0
163
+ def initialize(string)
164
+ @string = string
165
+ end
166
+
167
+ # Get the bits representing the Decimal128 that the string corresponds to.
168
+ #
169
+ # @example Get the bits for the Decimal128 object created from the string.
170
+ # builder.bits
171
+ #
172
+ # @return [ Array ] Tuple of the low and high bits.
173
+ #
174
+ # @since 4.2.0
175
+ def bits
176
+ if special?
177
+ to_special_bits
178
+ else
179
+ validate_format!
180
+ to_bits
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def to_bits
187
+ original, sign, digits_str = SIGN_AND_DIGITS_REGEX.match(@string).to_a
188
+ digits, e, scientific_exp = digits_str.partition(SCIENTIFIC_EXPONENT_REGEX)
189
+ before_decimal, decimal, after_decimal = digits.partition('.')
190
+
191
+ significand_str = before_decimal << after_decimal
192
+ significand_str = SIGNIFICAND_WITH_LEADING_ZEROS_REGEX.match(significand_str).to_a[2]
193
+
194
+ exponent = -(after_decimal.length)
195
+ exponent = exponent + scientific_exp.to_i
196
+ exponent, significand_str = round_exact(exponent, significand_str)
197
+ exponent, significand_str = clamp(exponent, significand_str)
198
+
199
+ Builder.parts_to_bits(significand_str.to_i, exponent, sign == '-')
200
+ end
201
+
202
+ def round_exact(exponent, significand)
203
+ if exponent < Decimal128::MIN_EXPONENT
204
+ if significand.to_i == 0
205
+ round = Decimal128::MIN_EXPONENT - exponent
206
+ exponent += round
207
+ elsif trailing_zeros = TRAILING_ZEROS_REGEX.match(significand)
208
+ round = [ (Decimal128::MIN_EXPONENT - exponent),
209
+ trailing_zeros[1].size ].min
210
+ significand = significand[0...-round]
211
+ exponent += round
212
+ end
213
+ elsif significand.length > Decimal128::MAX_DIGITS_OF_PRECISION
214
+ trailing_zeros = TRAILING_ZEROS_REGEX.match(significand)
215
+ if trailing_zeros
216
+ round = [ trailing_zeros[1].size,
217
+ (significand.length - Decimal128::MAX_DIGITS_OF_PRECISION),
218
+ (Decimal128::MAX_EXPONENT - exponent)].min
219
+ significand = significand[0...-round]
220
+ exponent += round
221
+ end
222
+ end
223
+ [ exponent, significand ]
224
+ end
225
+
226
+ def clamp(exponent, significand)
227
+ if exponent > Decimal128::MAX_EXPONENT
228
+ if significand.to_i == 0
229
+ adjust = exponent - Decimal128::MAX_EXPONENT
230
+ significand = '0'
231
+ else
232
+ adjust = [ (exponent - Decimal128::MAX_EXPONENT),
233
+ Decimal128::MAX_DIGITS_OF_PRECISION - significand.length ].min
234
+ significand << '0'* adjust
235
+ end
236
+ exponent -= adjust
237
+ end
238
+
239
+ [ exponent, significand ]
240
+ end
241
+
242
+ def to_special_bits
243
+ high = 0
244
+ if match = NAN_REGEX.match(@string)
245
+ high = NAN_MASK
246
+ high = high | SIGN_BIT_MASK if match[1]
247
+ high = high | SNAN_MASK if match[2]
248
+ elsif match = INFINITY_REGEX.match(@string)
249
+ high = INFINITY_MASK
250
+ high = high | SIGN_BIT_MASK if match[1] == '-'
251
+ end
252
+ [ 0, high ]
253
+ end
254
+
255
+ def special?
256
+ @string =~ NAN_REGEX || @string =~ INFINITY_REGEX
257
+ end
258
+
259
+ def validate_format!
260
+ raise BSON::Decimal128::InvalidString.new unless @string =~ VALID_DECIMAL128_STRING_REGEX
261
+ end
262
+ end
263
+
264
+ # Helper class for parsing a BigDecimal into Decimal128 high and low bits.
265
+ #
266
+ # @api private
267
+ #
268
+ # @since 4.2.0
269
+ class FromBigDecimal
270
+
271
+ # Initialize the FromBigDecimal Builder object.
272
+ #
273
+ # @example Create the FromBigDecimal builder.
274
+ # Builder::FromBigDecimal.new(big_decimal)
275
+ #
276
+ # @param [ BigDecimal ] big_decimal The big decimal object to
277
+ # create a Decimal128 from.
278
+ #
279
+ # @since 4.2.0
280
+ def initialize(big_decimal)
281
+ @big_decimal = big_decimal
282
+ end
283
+
284
+ # Get the bits representing the Decimal128 that the big decimal corresponds to.
285
+ #
286
+ # @example Get the bits for the Decimal128 object created from the big decimal.
287
+ # builder.bits
288
+ #
289
+ # @return [ Array ] Tuple of the low and high bits.
290
+ #
291
+ # @since 4.2.0
292
+ def bits
293
+ if special?
294
+ to_special_bits
295
+ else
296
+ to_bits
297
+ end
298
+ end
299
+
300
+ private
301
+
302
+ def to_special_bits
303
+ case @big_decimal.sign
304
+ when BigDecimal::SIGN_POSITIVE_INFINITE
305
+ high = INFINITY_MASK
306
+ when BigDecimal::SIGN_NEGATIVE_INFINITE
307
+ high = INFINITY_MASK | SIGN_BIT_MASK
308
+ when BigDecimal::SIGN_NaN
309
+ high = NAN_MASK
310
+ end
311
+ [ 0, high ]
312
+ end
313
+
314
+ def to_bits
315
+ sign, significand_str, base, exp = @big_decimal.split
316
+ exponent = @big_decimal.zero? ? 0 : exp - significand_str.length
317
+ is_negative = (sign == BigDecimal::SIGN_NEGATIVE_FINITE || sign == BigDecimal::SIGN_NEGATIVE_ZERO)
318
+ Builder.parts_to_bits(significand_str.to_i,
319
+ exponent,
320
+ is_negative)
321
+ end
322
+
323
+ def special?
324
+ @big_decimal.infinite? || @big_decimal.nan?
325
+ end
326
+ end
327
+
328
+ # Helper class for getting a String representation of a Decimal128 object.
329
+ #
330
+ # @api private
331
+ #
332
+ # @since 4.2.0
333
+ class ToString
334
+
335
+ # String representing a NaN value.
336
+ #
337
+ # @return [ String ] The string representing NaN.
338
+ #
339
+ # @since 4.2.0
340
+ NAN_STRING = 'NaN'.freeze
341
+
342
+ # String representing an Infinity value.
343
+ #
344
+ # @return [ String ] The string representing Infinity.
345
+ #
346
+ # @since 4.2.0
347
+ INFINITY_STRING = 'Infinity'.freeze
348
+
349
+ # Initialize the FromBigDecimal Builder object.
350
+ #
351
+ # @example Create the ToString builder.
352
+ # Builder::ToString.new(big_decimal)
353
+ #
354
+ # @param [ Decimal128 ] decimal128 The decimal128 object to
355
+ # create a String from.
356
+ #
357
+ # @since 4.2.0
358
+ def initialize(decimal128)
359
+ @decimal128 = decimal128
360
+ end
361
+
362
+ # Get the string representing the Decimal128 object.
363
+ #
364
+ # @example Get a string representing the decimal128.
365
+ # builder.string
366
+ #
367
+ # @return [ String ] The string representing the decimal128 object.
368
+ #
369
+ # @since 4.2.0
370
+ def string
371
+ return NAN_STRING if nan?
372
+ str = infinity? ? INFINITY_STRING : create_string
373
+ negative? ? '-' << str : str
374
+ end
375
+
376
+ private
377
+
378
+ def create_string
379
+ if use_scientific_notation?
380
+ exp_pos_sign = exponent < 0 ? '' : '+'
381
+ if significand.length > 1
382
+ str = "#{significand[0]}.#{significand[1..-1]}E#{exp_pos_sign}#{scientific_exponent}"
383
+ else
384
+ str = "#{significand}E#{exp_pos_sign}#{scientific_exponent}"
385
+ end
386
+ elsif exponent < 0
387
+ if significand.length > exponent.abs
388
+ decimal_point_index = significand.length - exponent.abs
389
+ str = "#{significand[0..decimal_point_index-1]}.#{significand[decimal_point_index..-1]}"
390
+ else
391
+ left_zero_pad = (exponent + significand.length).abs
392
+ str = "0.#{'0' * left_zero_pad}#{significand}"
393
+ end
394
+ end
395
+ str || significand
396
+ end
397
+
398
+ def scientific_exponent
399
+ @scientific_exponent ||= (significand.length - 1) + exponent
400
+ end
401
+
402
+ def use_scientific_notation?
403
+ exponent > 0 || scientific_exponent < -6
404
+ end
405
+
406
+ def exponent
407
+ @exponent ||= two_highest_bits_set? ?
408
+ ((high_bits & 0x1fffe00000000000) >> 47) - Decimal128::EXPONENT_OFFSET :
409
+ ((high_bits & 0x7fff800000000000) >> 49) - Decimal128::EXPONENT_OFFSET
410
+ end
411
+
412
+ def significand
413
+ @significand ||= two_highest_bits_set? ? '0' : bits_to_significand.to_s
414
+ end
415
+
416
+ def bits_to_significand
417
+ significand = high_bits & 0x1ffffffffffff
418
+ significand = significand << 64
419
+ significand |= low_bits
420
+ end
421
+
422
+ def two_highest_bits_set?
423
+ high_bits & TWO_HIGHEST_BITS_SET == TWO_HIGHEST_BITS_SET
424
+ end
425
+
426
+ def nan?
427
+ high_bits & NAN_MASK == NAN_MASK
428
+ end
429
+
430
+ def negative?
431
+ high_bits & SIGN_BIT_MASK == SIGN_BIT_MASK
432
+ end
433
+
434
+ def infinity?
435
+ high_bits & INFINITY_MASK == INFINITY_MASK
436
+ end
437
+
438
+ def high_bits
439
+ @decimal128.instance_variable_get(:@high)
440
+ end
441
+
442
+ def low_bits
443
+ @decimal128.instance_variable_get(:@low)
444
+ end
445
+ end
446
+ end
447
+ end
448
+ end
@@ -144,7 +144,7 @@ module BSON
144
144
  #
145
145
  # @param [ BSON::Document, Hash ] other The document/hash to merge with.
146
146
  #
147
- # @eturn [ BSON::Document ] The result of the merge.
147
+ # @return [ BSON::Document ] The result of the merge.
148
148
  #
149
149
  # @since 3.0.0
150
150
  def merge(other, &block)
@@ -159,7 +159,7 @@ module BSON
159
159
  #
160
160
  # @param [ BSON::Document, Hash ] other The document/hash to merge with.
161
161
  #
162
- # @eturn [ BSON::Document ] The result of the merge.
162
+ # @return [ BSON::Document ] The result of the merge.
163
163
  #
164
164
  # @since 3.0.0
165
165
  def merge!(other)