bson 4.1.1 → 4.2.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) 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/ext/bson/{native.c → bson_native.c} +48 -8
  6. data/ext/bson/extconf.rb +1 -1
  7. data/ext/bson/native-endian.h +1 -1
  8. data/lib/bson.rb +3 -1
  9. data/lib/bson/config.rb +1 -1
  10. data/lib/bson/decimal128.rb +318 -0
  11. data/lib/bson/decimal128/builder.rb +448 -0
  12. data/lib/bson/document.rb +2 -2
  13. data/lib/bson/environment.rb +13 -1
  14. data/lib/bson/int32.rb +46 -0
  15. data/lib/bson/int64.rb +46 -0
  16. data/lib/bson/max_key.rb +1 -1
  17. data/lib/bson/min_key.rb +1 -1
  18. data/lib/bson/object_id.rb +2 -1
  19. data/lib/bson/open_struct.rb +57 -0
  20. data/lib/bson/regexp.rb +1 -1
  21. data/lib/bson/registry.rb +1 -1
  22. data/lib/bson/version.rb +2 -2
  23. data/spec/bson/decimal128_spec.rb +1583 -0
  24. data/spec/bson/document_spec.rb +1 -1
  25. data/spec/bson/driver_bson_spec.rb +77 -0
  26. data/spec/bson/int32_spec.rb +58 -0
  27. data/spec/bson/int64_spec.rb +58 -0
  28. data/spec/bson/open_struct_spec.rb +144 -0
  29. data/spec/spec_helper.rb +4 -0
  30. data/spec/support/common_driver.rb +347 -0
  31. data/spec/support/driver-spec-tests/decimal128/decimal128-1.json +363 -0
  32. data/spec/support/driver-spec-tests/decimal128/decimal128-2.json +793 -0
  33. data/spec/support/driver-spec-tests/decimal128/decimal128-3.json +1771 -0
  34. data/spec/support/driver-spec-tests/decimal128/decimal128-4.json +165 -0
  35. data/spec/support/driver-spec-tests/decimal128/decimal128-5.json +402 -0
  36. data/spec/support/driver-spec-tests/decimal128/decimal128-6.json +131 -0
  37. data/spec/support/driver-spec-tests/decimal128/decimal128-7.json +327 -0
  38. metadata +29 -4
  39. 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)