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
@@ -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