bson 4.1.1 → 5.2.0

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