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
@@ -0,0 +1,854 @@
1
+ require 'spec_helper'
2
+
3
+ describe BSON::ByteBuffer do
4
+
5
+ let(:buffer) do
6
+ described_class.new
7
+ end
8
+
9
+ shared_examples_for 'does not write' do
10
+ it 'raises ArgumentError' do
11
+ expect do
12
+ modified
13
+ end.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'does not change write position' do
17
+ expect do
18
+ modified
19
+ end.to raise_error(ArgumentError)
20
+
21
+ expect(buffer.write_position).to eq(0)
22
+ end
23
+ end
24
+
25
+ describe '#put_byte' do
26
+
27
+ let(:modified) do
28
+ buffer.put_byte(BSON::Int32::BSON_TYPE)
29
+ end
30
+
31
+ it 'appends the byte to the byte buffer' do
32
+ expect(modified.to_s).to eq(BSON::Int32::BSON_TYPE.chr)
33
+ end
34
+
35
+ it 'increments the write position by 1' do
36
+ expect(modified.write_position).to eq(1)
37
+ end
38
+
39
+ context 'when it receives a numeric value' do
40
+ it 'raises the ArgumentError exception' do
41
+ expect{buffer.put_byte(1)}.to raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ context 'when it receives a nil value' do
46
+ it 'raises the ArgumentError exception' do
47
+ expect{buffer.put_byte(nil)}.to raise_error(ArgumentError)
48
+ end
49
+ end
50
+
51
+ context 'when given a string of length > 1' do
52
+
53
+ let(:modified) do
54
+ buffer.put_byte('xx')
55
+ end
56
+
57
+ it_behaves_like 'does not write'
58
+ end
59
+
60
+ context 'when given a string of length 0' do
61
+
62
+ let(:modified) do
63
+ buffer.put_byte('')
64
+ end
65
+
66
+ it_behaves_like 'does not write'
67
+ end
68
+
69
+ context 'when byte is not valid utf-8' do
70
+ let(:string) do
71
+ Utils.make_byte_string([254]).freeze
72
+ end
73
+
74
+ let(:modified) do
75
+ buffer.put_byte(string)
76
+ end
77
+
78
+ it 'writes the byte' do
79
+ expect(modified.to_s).to eq(string)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#put_bytes' do
85
+
86
+ let(:modified) do
87
+ buffer.put_bytes(BSON::Int32::BSON_TYPE)
88
+ buffer
89
+ end
90
+
91
+ it 'increments the write position by 1' do
92
+ expect(modified.write_position).to eq(1)
93
+ end
94
+
95
+ context 'when it receives a numeric value' do
96
+ it 'raises the ArgumentError exception' do
97
+ expect{buffer.put_bytes(1)}.to raise_error(ArgumentError)
98
+ end
99
+ end
100
+
101
+ context 'when it receives a nil value' do
102
+ it 'raises the ArgumentError exception' do
103
+ expect{buffer.put_bytes(nil)}.to raise_error(ArgumentError)
104
+ end
105
+ end
106
+
107
+ context 'when given a string with null bytes' do
108
+ let(:byte_str) { [0, 239, 254, 0].map(&:chr).join }
109
+
110
+ let(:modified) do
111
+ buffer.put_bytes(byte_str)
112
+ end
113
+
114
+ before do
115
+ expect(buffer.write_position).to eq(0)
116
+ expect(byte_str.length).to eq(4)
117
+ end
118
+
119
+ it 'writes the string' do
120
+ expect(modified.write_position).to eq(4)
121
+ end
122
+ end
123
+
124
+ context 'when bytes are not valid utf-8' do
125
+ let(:string) do
126
+ Utils.make_byte_string([254, 0, 255]).freeze
127
+ end
128
+
129
+ let(:modified) do
130
+ buffer.put_bytes(string)
131
+ end
132
+
133
+ it 'writes the bytes' do
134
+ expect(modified.to_s).to eq(string)
135
+ end
136
+ end
137
+ end
138
+
139
+ shared_examples_for 'bson string writer' do
140
+
141
+ context 'given empty string' do
142
+ let(:modified) do
143
+ buffer.put_string('')
144
+ end
145
+
146
+ it 'writes length and null terminator' do
147
+ expect(modified.write_position).to eq(5)
148
+ end
149
+ end
150
+
151
+ context 'when string is not valid utf-8 in utf-8 encoding' do
152
+ let(:string) do
153
+ Utils.make_byte_string([254, 253, 255], 'utf-8')
154
+ end
155
+
156
+ before do
157
+ expect(string.encoding.name).to eq('UTF-8')
158
+ end
159
+
160
+ it 'raises EncodingError' do
161
+ # Precise exception classes and messages differ between MRI and JRuby
162
+ expect do
163
+ modified
164
+ end.to raise_error(EncodingError)
165
+ end
166
+ end
167
+
168
+ context 'when string is in binary encoding and cannot be encoded in utf-8' do
169
+ let(:string) do
170
+ Utils.make_byte_string([254, 253, 255], 'binary')
171
+ end
172
+
173
+ before do
174
+ expect(string.encoding.name).to eq('ASCII-8BIT')
175
+ end
176
+
177
+ it 'raises Encoding::UndefinedConversionError' do
178
+ expect do
179
+ modified
180
+ end.to raise_error(Encoding::UndefinedConversionError, /from ASCII-8BIT to UTF-8/)
181
+ end
182
+ end
183
+ end
184
+
185
+ describe '#put_string' do
186
+
187
+ let(:modified) do
188
+ buffer.put_string(string)
189
+ end
190
+
191
+ it_behaves_like 'bson string writer'
192
+
193
+ context 'when the buffer does not need to be expanded' do
194
+
195
+ context 'when the string is UTF-8' do
196
+
197
+ let!(:modified) do
198
+ buffer.put_string('testing')
199
+ end
200
+
201
+ it 'appends the string to the byte buffer' do
202
+ expect(modified.to_s).to eq("#{8.to_bson.to_s}testing#{BSON::NULL_BYTE}")
203
+ end
204
+
205
+ it 'increments the write position by length + 5' do
206
+ expect(modified.write_position).to eq(12)
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'when the buffer needs to be expanded' do
212
+
213
+ let(:string) do
214
+ 300.times.inject(""){ |s, i| s << "#{i}" }
215
+ end
216
+
217
+ context 'when no bytes exist in the buffer' do
218
+
219
+ let!(:modified) do
220
+ buffer.put_string(string)
221
+ end
222
+
223
+ it 'appends the string to the byte buffer' do
224
+ expect(modified.to_s).to eq("#{(string.bytesize + 1).to_bson.to_s}#{string}#{BSON::NULL_BYTE}")
225
+ end
226
+
227
+ it 'increments the write position by length + 5' do
228
+ expect(modified.write_position).to eq(string.bytesize + 5)
229
+ end
230
+ end
231
+
232
+ context 'when bytes exist in the buffer' do
233
+
234
+ let!(:modified) do
235
+ buffer.put_int32(4).put_string(string)
236
+ end
237
+
238
+ it 'appends the string to the byte buffer' do
239
+ expect(modified.to_s).to eq(
240
+ "#{[ 4 ].pack(BSON::Int32::PACK)}#{(string.bytesize + 1).to_bson.to_s}#{string}#{BSON::NULL_BYTE}"
241
+ )
242
+ end
243
+
244
+ it 'increments the write position by length + 5' do
245
+ expect(modified.write_position).to eq(string.bytesize + 9)
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'when string is in an encoding other than utf-8' do
251
+ let(:string) do
252
+ # "\xfe"
253
+ Utils.make_byte_string([254], 'iso-8859-1')
254
+ end
255
+
256
+ let(:expected) do
257
+ # \xc3\xbe == \u00fe
258
+ Utils.make_byte_string([3, 0, 0, 0, 0xc3, 0xbe, 0])
259
+ end
260
+
261
+ it 'is written as utf-8' do
262
+ expect(modified.to_s).to eq(expected)
263
+ end
264
+ end
265
+ end
266
+
267
+ describe '#put_cstring' do
268
+
269
+ let(:modified) do
270
+ buffer.put_cstring(string)
271
+ end
272
+
273
+ it_behaves_like 'bson string writer'
274
+
275
+ context 'when argument is a string' do
276
+ context 'when the string is valid' do
277
+
278
+ let!(:modified) do
279
+ buffer.put_cstring('testing')
280
+ end
281
+
282
+ it 'appends the string plus null byte to the byte buffer' do
283
+ expect(modified.to_s).to eq("testing#{BSON::NULL_BYTE}")
284
+ end
285
+
286
+ it 'increments the write position by the length + 1' do
287
+ expect(modified.write_position).to eq(8)
288
+ end
289
+
290
+ it 'mutates receiver' do
291
+ modified
292
+ expect(buffer.write_position).to eq(8)
293
+ end
294
+ end
295
+
296
+ context "when the string contains a null byte" do
297
+
298
+ let(:string) do
299
+ "test#{BSON::NULL_BYTE}ing"
300
+ end
301
+
302
+ it 'raises ArgumentError' do
303
+ expect {
304
+ modified
305
+ }.to raise_error(ArgumentError, /String .* contains null bytes/)
306
+ end
307
+ end
308
+
309
+ context 'when string is in an encoding other than utf-8' do
310
+ let(:string) do
311
+ # "\xfe"
312
+ Utils.make_byte_string([254], 'iso-8859-1')
313
+ end
314
+
315
+ let(:expected) do
316
+ # \xc3\xbe == \u00fe
317
+ # No length prefix
318
+ Utils.make_byte_string([0xc3, 0xbe, 0])
319
+ end
320
+
321
+ it 'is written as utf-8' do
322
+ expect(modified.to_s).to eq(expected)
323
+ end
324
+ end
325
+ end
326
+
327
+ context 'when argument is a symbol' do
328
+ let(:modified) do
329
+ buffer.put_cstring(:testing)
330
+ end
331
+
332
+ it 'writes' do
333
+ expect(modified.to_s).to eq("testing#{BSON::NULL_BYTE}")
334
+ end
335
+
336
+ it 'increments the write position by the length + 1' do
337
+ expect(modified.write_position).to eq(8)
338
+ end
339
+
340
+ it 'mutates receiver' do
341
+ modified
342
+ expect(buffer.write_position).to eq(8)
343
+ end
344
+
345
+ context 'when symbol includes a null byte' do
346
+ let(:modified) do
347
+ buffer.put_cstring(:"tes\x00ing")
348
+ end
349
+
350
+ it 'raises ArgumentError' do
351
+ expect {
352
+ modified
353
+ }.to raise_error(ArgumentError, /String .* contains null bytes/)
354
+ end
355
+
356
+ it 'does not change write position' do
357
+ begin
358
+ buffer.put_cstring(:"tes\x00ing")
359
+ rescue ArgumentError
360
+ end
361
+
362
+ expect(buffer.write_position).to eq(0)
363
+ end
364
+ end
365
+ end
366
+
367
+ context 'when argument is a Fixnum' do
368
+ let(:modified) do
369
+ buffer.put_cstring(1234)
370
+ end
371
+
372
+ it 'writes' do
373
+ expect(modified.to_s).to eq("1234#{BSON::NULL_BYTE}")
374
+ end
375
+
376
+ it 'increments the write position by the length + 1' do
377
+ expect(modified.write_position).to eq(5)
378
+ end
379
+ end
380
+
381
+ context 'when argument is of an unsupported type' do
382
+ let(:modified) do
383
+ buffer.put_cstring(1234.0)
384
+ end
385
+
386
+ it 'raises TypeError' do
387
+ expect do
388
+ modified
389
+ end.to raise_error(TypeError, /Invalid type for put_cstring/)
390
+ end
391
+
392
+ it 'does not change write position' do
393
+ begin
394
+ buffer.put_cstring(1234.0)
395
+ rescue TypeError
396
+ end
397
+
398
+ expect(buffer.write_position).to eq(0)
399
+ end
400
+ end
401
+ end
402
+
403
+ describe '#put_symbol' do
404
+ context 'normal symbol' do
405
+ let(:modified) do
406
+ buffer.put_symbol(:hello)
407
+ end
408
+
409
+ it 'writes the symbol as string' do
410
+ expect(modified.to_s).to eq("\x06\x00\x00\x00hello\x00")
411
+ end
412
+
413
+ it 'advances write position' do
414
+ # 4 byte length + 5 byte string + null byte
415
+ expect(modified.write_position).to eq(10)
416
+ end
417
+ end
418
+
419
+ context 'symbol with null byte' do
420
+ let(:modified) do
421
+ buffer.put_symbol(:"he\x00lo")
422
+ end
423
+
424
+ it 'writes the symbol as string' do
425
+ expect(modified.to_s).to eq("\x06\x00\x00\x00he\x00lo\x00")
426
+ end
427
+
428
+ it 'advances write position' do
429
+ # 4 byte length + 5 byte string + null byte
430
+ expect(modified.write_position).to eq(10)
431
+ end
432
+ end
433
+
434
+ context 'when symbol is not valid utf-8' do
435
+ let(:symbol) do
436
+ Utils.make_byte_string([254, 0, 255]).to_sym
437
+ end
438
+
439
+ let(:modified) do
440
+ buffer.put_symbol(symbol)
441
+ end
442
+
443
+ it 'raises EncodingError' do
444
+ # Precise exception classes and messages differ between MRI and JRuby
445
+ expect do
446
+ modified
447
+ end.to raise_error(EncodingError)
448
+ end
449
+ end
450
+ end
451
+
452
+ describe '#put_double' do
453
+
454
+ let(:modified) do
455
+ buffer.put_double(1.2332)
456
+ end
457
+
458
+ it 'appends the double to the buffer' do
459
+ expect(modified.to_s).to eq([ 1.2332 ].pack(Float::PACK))
460
+ end
461
+
462
+ it 'increments the write position by 8' do
463
+ expect(modified.write_position).to eq(8)
464
+ end
465
+
466
+ context 'when argument is an integer' do
467
+
468
+ let(:modified) do
469
+ buffer.put_double(3)
470
+ end
471
+
472
+ it 'writes a double' do
473
+ expect(modified.to_s).to eq([ 3 ].pack(Float::PACK))
474
+ end
475
+
476
+ it 'increments the write position by 8' do
477
+ expect(modified.write_position).to eq(8)
478
+ end
479
+ end
480
+
481
+ context 'when argument is a BigNum' do
482
+ let(:value) { 123456789012345678901234567890 }
483
+
484
+ let(:modified) do
485
+ buffer.put_double(value)
486
+ end
487
+
488
+ let(:actual) do
489
+ described_class.new(modified.to_s).get_double
490
+ end
491
+
492
+ it 'writes a double' do
493
+ expect(actual).to be_within(1).of(value)
494
+ end
495
+
496
+ it 'increments the write position by 8' do
497
+ expect(modified.write_position).to eq(8)
498
+ end
499
+ end
500
+
501
+ context 'when argument is a string' do
502
+
503
+ let(:modified) do
504
+ buffer.put_double("hello")
505
+ end
506
+
507
+ it 'raises TypeError' do
508
+ expect do
509
+ modified
510
+ end.to raise_error(TypeError, /no implicit conversion to float from string|ClassCastException:.*RubyString cannot be cast to.*RubyFloat/)
511
+ expect(buffer.write_position).to eq(0)
512
+ end
513
+ end
514
+ end
515
+
516
+ describe '#put_int32' do
517
+
518
+ context 'when the integer is 32 bit' do
519
+
520
+ context 'when the integer is positive' do
521
+
522
+ let!(:modified) do
523
+ buffer.put_int32(Integer::MAX_32BIT - 1)
524
+ end
525
+
526
+ let(:expected) do
527
+ [ Integer::MAX_32BIT - 1 ].pack(BSON::Int32::PACK)
528
+ end
529
+
530
+ it 'appends the int32 to the byte buffer' do
531
+ expect(modified.to_s).to eq(expected)
532
+ end
533
+
534
+ it 'increments the write position by 4' do
535
+ expect(modified.write_position).to eq(4)
536
+ end
537
+ end
538
+
539
+ context 'when the integer is negative' do
540
+
541
+ let!(:modified) do
542
+ buffer.put_int32(Integer::MIN_32BIT + 1)
543
+ end
544
+
545
+ let(:expected) do
546
+ [ Integer::MIN_32BIT + 1 ].pack(BSON::Int32::PACK)
547
+ end
548
+
549
+ it 'appends the int32 to the byte buffer' do
550
+ expect(modified.to_s).to eq(expected)
551
+ end
552
+
553
+ it 'increments the write position by 4' do
554
+ expect(modified.write_position).to eq(4)
555
+ end
556
+ end
557
+
558
+ context 'when the integer is not 32 bit' do
559
+
560
+ it 'raises an exception' do
561
+ expect {
562
+ buffer.put_int32(Integer::MAX_64BIT - 1)
563
+ }.to raise_error(RangeError)
564
+ end
565
+ end
566
+ end
567
+
568
+ context 'when argument is a float' do
569
+
570
+ let(:modified) do
571
+ buffer.put_int32(4.934)
572
+ end
573
+
574
+ let(:expected) do
575
+ [ 4 ].pack(BSON::Int32::PACK)
576
+ end
577
+
578
+ it 'appends the int32 to the byte buffer' do
579
+ expect(modified.to_s).to eq(expected)
580
+ end
581
+
582
+ it 'increments the write position by 4' do
583
+ expect(modified.write_position).to eq(4)
584
+ end
585
+ end
586
+ end
587
+
588
+ describe '#put_uint32' do
589
+ context 'when argument is a float' do
590
+ it 'raises an Argument Error' do
591
+ expect{ buffer.put_uint32(4.934) }.to raise_error(ArgumentError, "put_uint32: incorrect type: float, expected: integer")
592
+ end
593
+ end
594
+
595
+ context 'when number is in range' do
596
+ let(:modified) do
597
+ buffer.put_uint32(5)
598
+ end
599
+
600
+ it 'returns gets the correct number from the buffer' do
601
+ expect(modified.get_uint32).to eq(5)
602
+ end
603
+
604
+ it 'returns the length of the buffer' do
605
+ expect(modified.length).to eq(4)
606
+ end
607
+ end
608
+
609
+ context 'when number is 0' do
610
+ let(:modified) do
611
+ buffer.put_uint32(0)
612
+ end
613
+
614
+ it 'returns gets the correct number from the buffer' do
615
+ expect(modified.get_uint32).to eq(0)
616
+ end
617
+
618
+ it 'returns the length of the buffer' do
619
+ expect(modified.length).to eq(4)
620
+ end
621
+ end
622
+
623
+ context 'when number doesn\'t fit in signed int32' do
624
+ let(:modified) do
625
+ buffer.put_uint32(4294967295)
626
+ end
627
+
628
+ let(:expected) do
629
+ [ 4294967295 ].pack(BSON::Int32::PACK)
630
+ end
631
+
632
+ it 'appends the int32 to the byte buffer' do
633
+ expect(modified.to_s).to eq(expected)
634
+ end
635
+
636
+ it 'get returns correct number' do
637
+ expect(modified.get_uint32).to eq(4294967295)
638
+ end
639
+
640
+ it 'returns the length of the buffer' do
641
+ expect(modified.length).to eq(4)
642
+ end
643
+ end
644
+
645
+ context 'when number is 2^31' do
646
+ let(:modified) do
647
+ buffer.put_uint32(2147483648)
648
+ end
649
+
650
+ it 'returns gets the correct number from the buffer' do
651
+ expect(modified.get_uint32).to eq(2147483648)
652
+ end
653
+
654
+ it 'returns the length of the buffer' do
655
+ expect(modified.length).to eq(4)
656
+ end
657
+ end
658
+
659
+ context 'when number is 2^31-1' do
660
+ let(:modified) do
661
+ buffer.put_uint32(2147483647)
662
+ end
663
+
664
+ it 'returns gets the correct number from the buffer' do
665
+ expect(modified.get_uint32).to eq(2147483647)
666
+ end
667
+
668
+ it 'returns the length of the buffer' do
669
+ expect(modified.length).to eq(4)
670
+ end
671
+ end
672
+
673
+ context 'when number is not in range' do
674
+ it 'raises error on out of top range' do
675
+ expect{ buffer.put_uint32(4294967296) }.to raise_error(RangeError, "Number 4294967296 is out of range [0, 2^32)")
676
+ end
677
+
678
+ it 'raises error on out of bottom range' do
679
+ expect{ buffer.put_uint32(-1) }.to raise_error(RangeError, "Number -1 is out of range [0, 2^32)")
680
+ end
681
+ end
682
+ end
683
+
684
+ describe '#put_int64' do
685
+
686
+ context 'when the integer is 64 bit' do
687
+
688
+ context 'when the integer is positive' do
689
+
690
+ let!(:modified) do
691
+ buffer.put_int64(Integer::MAX_64BIT - 1)
692
+ end
693
+
694
+ let(:expected) do
695
+ [ Integer::MAX_64BIT - 1 ].pack(BSON::Int64::PACK)
696
+ end
697
+
698
+ it 'appends the int64 to the byte buffer' do
699
+ expect(modified.to_s).to eq(expected)
700
+ end
701
+
702
+ it 'increments the write position by 8' do
703
+ expect(modified.write_position).to eq(8)
704
+ end
705
+ end
706
+
707
+ context 'when the integer is negative' do
708
+
709
+ let!(:modified) do
710
+ buffer.put_int64(Integer::MIN_64BIT + 1)
711
+ end
712
+
713
+ let(:expected) do
714
+ [ Integer::MIN_64BIT + 1 ].pack(BSON::Int64::PACK)
715
+ end
716
+
717
+ it 'appends the int64 to the byte buffer' do
718
+ expect(modified.to_s).to eq(expected)
719
+ end
720
+
721
+ it 'increments the write position by 8' do
722
+ expect(modified.write_position).to eq(8)
723
+ end
724
+ end
725
+
726
+ context 'when the integer is larger than 64 bit' do
727
+
728
+ it 'raises an exception' do
729
+ expect {
730
+ buffer.put_int64(Integer::MAX_64BIT + 1)
731
+ }.to raise_error(RangeError)
732
+ end
733
+ end
734
+ end
735
+
736
+ context 'when integer fits in 32 bits' do
737
+ let(:modified) do
738
+ buffer.put_int64(1)
739
+ end
740
+
741
+ it 'increments the write position by 8' do
742
+ expect(modified.write_position).to eq(8)
743
+ end
744
+ end
745
+
746
+ context 'when argument is a float' do
747
+
748
+ let(:modified) do
749
+ buffer.put_int64(4.934)
750
+ end
751
+
752
+ let(:expected) do
753
+ [ 4 ].pack(BSON::Int64::PACK)
754
+ end
755
+
756
+ it 'appends the int64 to the byte buffer' do
757
+ expect(modified.to_s).to eq(expected)
758
+ end
759
+
760
+ it 'increments the write position by 8' do
761
+ expect(modified.write_position).to eq(8)
762
+ end
763
+ end
764
+ end
765
+
766
+ describe '#replace_int32' do
767
+
768
+ let(:exp_0) do
769
+ [ 0 ].pack(BSON::Int32::PACK)
770
+ end
771
+
772
+ let(:exp_first) do
773
+ [ 5 ].pack(BSON::Int32::PACK)
774
+ end
775
+
776
+ let(:exp_second) do
777
+ [ 4 ].pack(BSON::Int32::PACK)
778
+ end
779
+
780
+ let(:exp_42) do
781
+ [ 42 ].pack(BSON::Int32::PACK)
782
+ end
783
+
784
+ let(:modified) do
785
+ buffer.replace_int32(0, 5)
786
+ end
787
+
788
+ context 'when there is sufficient data in buffer' do
789
+
790
+ before do
791
+ buffer.put_int32(0).put_int32(4)
792
+ end
793
+
794
+ it 'replaces the int32 at the location' do
795
+ expect(modified.to_s).to eq("#{exp_first}#{exp_second}")
796
+ end
797
+
798
+ context 'when the position is negative' do
799
+
800
+ let(:modified) do
801
+ buffer.replace_int32(-1, 5)
802
+ end
803
+
804
+ it 'raises ArgumentError' do
805
+ expect do
806
+ modified
807
+ end.to raise_error(ArgumentError, /Position.*cannot be negative/)
808
+ end
809
+ end
810
+
811
+ context 'when the position is 4 bytes prior to write position' do
812
+
813
+ let(:modified) do
814
+ buffer.replace_int32(4, 42)
815
+ end
816
+
817
+ it 'replaces the int32 at the location' do
818
+ expect(modified.to_s).to eq("#{exp_0}#{exp_42}")
819
+ end
820
+ end
821
+
822
+ context 'when the position exceeds allowed range' do
823
+
824
+ let(:modified) do
825
+ # Buffer has 8 bytes but we can only write up to position 4
826
+ buffer.replace_int32(5, 42)
827
+ end
828
+
829
+ it 'raises ArgumentError' do
830
+ expect do
831
+ modified
832
+ end.to raise_error(ArgumentError, /Position.*is out of bounds/)
833
+ end
834
+ end
835
+ end
836
+
837
+ context 'when there is insufficient data in buffer' do
838
+
839
+ before do
840
+ buffer.put_bytes("aa")
841
+ end
842
+
843
+ let(:modified) do
844
+ buffer.replace_int32(1, 42)
845
+ end
846
+
847
+ it 'raises ArgumentError' do
848
+ expect do
849
+ modified
850
+ end.to raise_error(ArgumentError, /Buffer does not have enough data/)
851
+ end
852
+ end
853
+ end
854
+ end