bson 4.15.0 → 5.0.2

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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +94 -10
  3. data/Rakefile +68 -39
  4. data/ext/bson/bson-native.h +12 -4
  5. data/ext/bson/extconf.rb +8 -3
  6. data/ext/bson/init.c +11 -11
  7. data/ext/bson/read.c +39 -9
  8. data/ext/bson/util.c +171 -16
  9. data/ext/bson/write.c +34 -39
  10. data/lib/bson/active_support.rb +1 -0
  11. data/lib/bson/array.rb +58 -32
  12. data/lib/bson/big_decimal.rb +16 -6
  13. data/lib/bson/binary.rb +271 -129
  14. data/lib/bson/boolean.rb +1 -0
  15. data/lib/bson/code.rb +10 -12
  16. data/lib/bson/code_with_scope.rb +9 -11
  17. data/lib/bson/config.rb +1 -27
  18. data/lib/bson/date.rb +2 -1
  19. data/lib/bson/date_time.rb +2 -1
  20. data/lib/bson/db_pointer.rb +12 -13
  21. data/lib/bson/dbref.rb +11 -9
  22. data/lib/bson/decimal128/builder.rb +10 -9
  23. data/lib/bson/decimal128.rb +25 -111
  24. data/lib/bson/document.rb +1 -0
  25. data/lib/bson/environment.rb +1 -0
  26. data/lib/bson/error/bson_decode_error.rb +11 -0
  27. data/lib/bson/error/ext_json_parse_error.rb +11 -0
  28. data/lib/bson/error/illegal_key.rb +23 -0
  29. data/lib/bson/error/invalid_binary_type.rb +37 -0
  30. data/lib/bson/error/invalid_dbref_argument.rb +12 -0
  31. data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
  32. data/lib/bson/error/invalid_decimal128_range.rb +27 -0
  33. data/lib/bson/error/invalid_decimal128_string.rb +26 -0
  34. data/lib/bson/error/invalid_key.rb +24 -0
  35. data/lib/bson/error/invalid_object_id.rb +11 -0
  36. data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
  37. data/lib/bson/error/unrepresentable_precision.rb +19 -0
  38. data/lib/bson/error/unserializable_class.rb +13 -0
  39. data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
  40. data/lib/bson/error/unsupported_type.rb +11 -0
  41. data/lib/bson/error.rb +16 -28
  42. data/lib/bson/ext_json.rb +2 -1
  43. data/lib/bson/false_class.rb +2 -1
  44. data/lib/bson/float.rb +3 -2
  45. data/lib/bson/hash.rb +128 -73
  46. data/lib/bson/int32.rb +17 -5
  47. data/lib/bson/int64.rb +17 -5
  48. data/lib/bson/integer.rb +4 -5
  49. data/lib/bson/json.rb +1 -0
  50. data/lib/bson/max_key.rb +8 -10
  51. data/lib/bson/min_key.rb +8 -10
  52. data/lib/bson/nil_class.rb +1 -0
  53. data/lib/bson/object.rb +7 -27
  54. data/lib/bson/object_id.rb +84 -120
  55. data/lib/bson/open_struct.rb +3 -2
  56. data/lib/bson/regexp.rb +36 -65
  57. data/lib/bson/registry.rb +2 -6
  58. data/lib/bson/specialized.rb +2 -1
  59. data/lib/bson/string.rb +4 -27
  60. data/lib/bson/symbol.rb +23 -20
  61. data/lib/bson/time.rb +3 -2
  62. data/lib/bson/time_with_zone.rb +13 -1
  63. data/lib/bson/timestamp.rb +3 -2
  64. data/lib/bson/true_class.rb +2 -1
  65. data/lib/bson/undefined.rb +15 -1
  66. data/lib/bson/version.rb +3 -1
  67. data/lib/bson.rb +3 -2
  68. data/spec/bson/array_spec.rb +19 -60
  69. data/spec/bson/big_decimal_spec.rb +16 -4
  70. data/spec/bson/binary_spec.rb +129 -81
  71. data/spec/bson/binary_uuid_spec.rb +1 -0
  72. data/spec/bson/boolean_spec.rb +1 -0
  73. data/spec/bson/byte_buffer_read_spec.rb +1 -0
  74. data/spec/bson/byte_buffer_spec.rb +1 -0
  75. data/spec/bson/byte_buffer_write_spec.rb +1 -0
  76. data/spec/bson/code_spec.rb +5 -3
  77. data/spec/bson/code_with_scope_spec.rb +5 -3
  78. data/spec/bson/config_spec.rb +1 -35
  79. data/spec/bson/date_spec.rb +1 -0
  80. data/spec/bson/date_time_spec.rb +1 -0
  81. data/spec/bson/dbref_legacy_spec.rb +20 -3
  82. data/spec/bson/dbref_spec.rb +9 -9
  83. data/spec/bson/decimal128_spec.rb +40 -20
  84. data/spec/bson/document_as_spec.rb +1 -0
  85. data/spec/bson/document_spec.rb +1 -1
  86. data/spec/bson/ext_json_parse_spec.rb +1 -0
  87. data/spec/bson/false_class_spec.rb +8 -0
  88. data/spec/bson/float_spec.rb +8 -3
  89. data/spec/bson/hash_as_spec.rb +1 -0
  90. data/spec/bson/hash_spec.rb +87 -75
  91. data/spec/bson/int32_spec.rb +21 -6
  92. data/spec/bson/int64_spec.rb +21 -6
  93. data/spec/bson/integer_spec.rb +45 -13
  94. data/spec/bson/json_spec.rb +1 -0
  95. data/spec/bson/max_key_spec.rb +5 -3
  96. data/spec/bson/min_key_spec.rb +5 -3
  97. data/spec/bson/nil_class_spec.rb +1 -0
  98. data/spec/bson/object_id_spec.rb +57 -4
  99. data/spec/bson/object_spec.rb +2 -1
  100. data/spec/bson/open_struct_spec.rb +14 -71
  101. data/spec/bson/raw_spec.rb +9 -15
  102. data/spec/bson/regexp_spec.rb +4 -3
  103. data/spec/bson/registry_spec.rb +2 -1
  104. data/spec/bson/string_spec.rb +13 -38
  105. data/spec/bson/symbol_raw_spec.rb +25 -0
  106. data/spec/bson/symbol_spec.rb +15 -18
  107. data/spec/bson/time_spec.rb +1 -0
  108. data/spec/bson/time_with_zone_spec.rb +1 -0
  109. data/spec/bson/timestamp_spec.rb +1 -0
  110. data/spec/bson/true_class_spec.rb +8 -0
  111. data/spec/bson/undefined_spec.rb +27 -0
  112. data/spec/bson_spec.rb +1 -0
  113. data/spec/runners/common_driver.rb +6 -5
  114. data/spec/runners/corpus.rb +6 -0
  115. data/spec/runners/corpus_legacy.rb +1 -0
  116. data/spec/spec_helper.rb +1 -0
  117. data/spec/spec_tests/common_driver_spec.rb +9 -4
  118. data/spec/spec_tests/corpus_legacy_spec.rb +1 -0
  119. data/spec/spec_tests/corpus_spec.rb +13 -3
  120. data/spec/spec_tests/data/corpus/binary.json +5 -0
  121. data/spec/spec_tests/data/corpus/code.json +13 -13
  122. data/spec/spec_tests/data/corpus/decimal128-4.json +48 -0
  123. data/spec/spec_tests/data/corpus/decimal128-6.json +12 -0
  124. data/spec/spec_tests/data/corpus/decimal128-7.json +4 -0
  125. data/spec/spec_tests/data/corpus/document.json +20 -0
  126. data/spec/spec_tests/data/corpus/symbol.json +7 -7
  127. data/spec/spec_tests/data/corpus/top.json +18 -3
  128. data/spec/support/shared_examples.rb +28 -5
  129. data/spec/support/spec_config.rb +1 -0
  130. data/spec/support/utils.rb +49 -1
  131. metadata +114 -164
  132. checksums.yaml.gz.sig +0 -0
  133. data/spec/shared/LICENSE +0 -20
  134. data/spec/shared/bin/get-mongodb-download-url +0 -17
  135. data/spec/shared/bin/s3-copy +0 -45
  136. data/spec/shared/bin/s3-upload +0 -69
  137. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  138. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  139. data/spec/shared/lib/mrss/constraints.rb +0 -386
  140. data/spec/shared/lib/mrss/docker_runner.rb +0 -271
  141. data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
  142. data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
  143. data/spec/shared/lib/mrss/server_version_registry.rb +0 -120
  144. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  145. data/spec/shared/lib/mrss/utils.rb +0 -15
  146. data/spec/shared/share/Dockerfile.erb +0 -338
  147. data/spec/shared/share/haproxy-1.conf +0 -16
  148. data/spec/shared/share/haproxy-2.conf +0 -17
  149. data/spec/shared/shlib/distro.sh +0 -74
  150. data/spec/shared/shlib/server.sh +0 -367
  151. data/spec/shared/shlib/set_env.sh +0 -131
  152. data.tar.gz.sig +0 -0
  153. metadata.gz.sig +0 -1
data/ext/bson/util.c CHANGED
@@ -21,6 +21,18 @@
21
21
  */
22
22
  static char rb_bson_machine_id_hash[HOST_NAME_HASH_MAX];
23
23
 
24
+ /**
25
+ * Holds a reference to the SecureRandom module, or Qnil if the modle is
26
+ * not available.
27
+ */
28
+ static VALUE pvt_SecureRandom = Qnil;
29
+
30
+ /**
31
+ * Indicates whether or not the SecureRandom module responds to the
32
+ * `random_number` method (depends on Ruby version).
33
+ */
34
+ static int pvt_has_random_number = 0;
35
+
24
36
  void rb_bson_generate_machine_id(VALUE rb_md5_class, char *rb_bson_machine_id)
25
37
  {
26
38
  VALUE digest = rb_funcall(rb_md5_class, rb_intern("digest"), 1, rb_str_new2(rb_bson_machine_id));
@@ -29,31 +41,69 @@ void rb_bson_generate_machine_id(VALUE rb_md5_class, char *rb_bson_machine_id)
29
41
 
30
42
  /**
31
43
  * Generate the next object id.
44
+ *
45
+ * Specification:
46
+ * https://github.com/mongodb/specifications/blob/master/source/bson-objectid/objectid.md
47
+ *
48
+ * The ObjectID BSON type is a 12-byte value consisting of three different portions (fields):
49
+ * * a 4-byte value representing the seconds since the Unix epoch in the highest order bytes,
50
+ * * a 5-byte random number unique to a machine and process,
51
+ * * a 3-byte counter, starting with a random value.
32
52
  */
33
53
  VALUE rb_bson_object_id_generator_next(int argc, VALUE* args, VALUE self)
34
54
  {
35
55
  char bytes[12];
36
- uint32_t t;
37
- uint32_t c;
38
- uint16_t pid = BSON_UINT16_TO_BE(getpid());
56
+ uint32_t time_component;
57
+ uint8_t* random_component;
58
+ uint32_t counter_component;
59
+ VALUE timestamp;
60
+ VALUE rb_bson_object_id_class;
39
61
 
40
- if (argc == 0 || (argc == 1 && *args == Qnil)) {
41
- t = BSON_UINT32_TO_BE((int) time(NULL));
42
- }
43
- else {
44
- t = BSON_UINT32_TO_BE(NUM2ULONG(rb_funcall(*args, rb_intern("to_i"), 0)));
45
- }
62
+ rb_bson_object_id_class = pvt_const_get_2("BSON", "ObjectId");
63
+
64
+ /* "Drivers SHOULD have an accessor method on an ObjectID class for
65
+ * obtaining the timestamp value." */
66
+
67
+ timestamp = rb_funcall(rb_bson_object_id_class, rb_intern("timestamp"), 0);
68
+ time_component = BSON_UINT32_TO_BE(NUM2UINT(timestamp));
69
+
70
+ /* "A 5-byte field consisting of a random value generated once per process.
71
+ * This random value is unique to the machine and process.
72
+ *
73
+ * "Drivers MUST NOT have an accessor method on an ObjectID class for
74
+ * obtaining this value."
75
+ */
46
76
 
47
- c = BSON_UINT32_TO_BE(rb_bson_object_id_counter << 8);
77
+ random_component = pvt_get_object_id_random_value();
78
+
79
+ /* shift left 8 bits, so that the first three bytes of the result are
80
+ * the meaningful ones */
81
+ counter_component = BSON_UINT32_TO_BE(rb_bson_object_id_counter << 8);
82
+
83
+ memcpy(&bytes, &time_component, 4);
84
+ memcpy(&bytes[4], random_component, 5);
85
+ memcpy(&bytes[9], &counter_component, 3);
86
+
87
+ rb_bson_object_id_counter = (rb_bson_object_id_counter + 1) % 0x1000000;
48
88
 
49
- memcpy(&bytes, &t, 4);
50
- memcpy(&bytes[4], rb_bson_machine_id_hash, 3);
51
- memcpy(&bytes[7], &pid, 2);
52
- memcpy(&bytes[9], &c, 3);
53
- rb_bson_object_id_counter++;
54
89
  return rb_str_new(bytes, 12);
55
90
  }
56
91
 
92
+ /**
93
+ * Reset the counter. This is purely as an aid for testing.
94
+ *
95
+ * @param [ Integer ] i the value to set the counter to (default is 0)
96
+ */
97
+ VALUE rb_bson_object_id_generator_reset_counter(int argc, VALUE* args, VALUE self) {
98
+ switch(argc) {
99
+ case 0: rb_bson_object_id_counter = 0; break;
100
+ case 1: rb_bson_object_id_counter = FIX2INT(args[0]); break;
101
+ default: rb_raise(rb_eArgError, "Expected 0 or 1 arguments, got %d", argc);
102
+ }
103
+
104
+ return T_NIL;
105
+ }
106
+
57
107
  /**
58
108
  * Returns a Ruby constant nested one level, e.g. BSON::Document.
59
109
  */
@@ -77,7 +127,7 @@ VALUE pvt_const_get_3(const char *c1, const char *c2, const char *c3) {
77
127
  int pvt_get_mode_option(int argc, VALUE *argv) {
78
128
  VALUE opts;
79
129
  VALUE mode;
80
-
130
+
81
131
  rb_scan_args(argc, argv, ":", &opts);
82
132
  if (NIL_P(opts)) {
83
133
  return BSON_MODE_DEFAULT;
@@ -93,3 +143,108 @@ int pvt_get_mode_option(int argc, VALUE *argv) {
93
143
  }
94
144
  }
95
145
  }
146
+
147
+ /**
148
+ * Returns the random number associated with this host and process. If the
149
+ * process ID changes (e.g. via fork), this will detect the change and
150
+ * generate another random number.
151
+ */
152
+ uint8_t* pvt_get_object_id_random_value() {
153
+ static pid_t remembered_pid = 0;
154
+ static uint8_t remembered_value[BSON_OBJECT_ID_RANDOM_VALUE_LENGTH] = {0};
155
+ pid_t pid = getpid();
156
+
157
+ if (remembered_pid != pid) {
158
+ remembered_pid = pid;
159
+ pvt_rand_buf(remembered_value, BSON_OBJECT_ID_RANDOM_VALUE_LENGTH, pid);
160
+ }
161
+
162
+ return remembered_value;
163
+ }
164
+
165
+ /**
166
+ * Attempts to load the SecureRandom module
167
+ */
168
+ VALUE pvt_load_secure_random(VALUE _arg) {
169
+ rb_require("securerandom");
170
+ pvt_SecureRandom = rb_const_get(rb_cObject, rb_intern("SecureRandom"));
171
+ pvt_has_random_number = rb_respond_to(pvt_SecureRandom, rb_intern("random_number"));
172
+
173
+ // mark SecureRandom so it does not get moved
174
+ rb_global_variable(&pvt_SecureRandom);
175
+
176
+ return Qnil;
177
+ }
178
+
179
+ /**
180
+ * The fallback, if loading `securerandom` fails.
181
+ */
182
+ VALUE pvt_rescue_load_secure_random(VALUE _arg, VALUE _exception) {
183
+ pvt_SecureRandom = Qnil;
184
+
185
+ return Qnil;
186
+ }
187
+
188
+ /**
189
+ * Initializes the RNG.
190
+ */
191
+ void pvt_init_rand() {
192
+ // SecureRandom may fail to load because it's not present (LoadError), or
193
+ // because it can't find a random device (NotImplementedError).
194
+ rb_rescue2(pvt_load_secure_random, Qnil, pvt_rescue_load_secure_random, Qnil,
195
+ rb_eLoadError, rb_eNotImpError, 0);
196
+ }
197
+
198
+ /**
199
+ * Fills the buffer with random bytes. It prefers to use SecureRandom for
200
+ * this, but in the very unlikely event that SecureRandom is not available,
201
+ * it will fall back to a much-less-ideal generator using srand/rand.
202
+ *
203
+ * The `pid` argument is only used by the fallback, if SecureRandom is not
204
+ * available.
205
+ */
206
+ void pvt_rand_buf(uint8_t* bytes, int len, int pid) {
207
+ if (pvt_SecureRandom != Qnil) {
208
+ VALUE rb_bytes = rb_funcall(pvt_SecureRandom, rb_intern("bytes"), 1, INT2NUM(len));
209
+ memcpy(bytes, StringValuePtr(rb_bytes), len);
210
+
211
+ } else {
212
+ time_t t;
213
+ uint32_t seed;
214
+ int ofs = 0;
215
+
216
+ t = time(NULL);
217
+ seed = ((uint32_t)t << 16) + ((uint32_t)pid % 0xFFFF);
218
+ srand(seed);
219
+
220
+ while (ofs < len) {
221
+ int n = rand();
222
+ unsigned remaining = len - ofs;
223
+
224
+ if (remaining > sizeof(n)) remaining = sizeof(n);
225
+ memcpy(bytes+ofs, &n, remaining);
226
+
227
+ ofs += remaining;
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Returns a random integer between 0 and INT_MAX.
234
+ */
235
+ int pvt_rand() {
236
+ if (pvt_has_random_number) {
237
+ VALUE result = rb_funcall(pvt_SecureRandom, rb_intern("random_number"), 1, INT2NUM(INT_MAX));
238
+ return NUM2INT(result);
239
+
240
+ } else if (pvt_SecureRandom != Qnil) {
241
+ int result;
242
+ VALUE rb_result = rb_funcall(pvt_SecureRandom, rb_intern("bytes"), 1, INT2NUM(sizeof(result)));
243
+ memcpy(&result, StringValuePtr(rb_result), sizeof(result));
244
+ return result;
245
+
246
+ } else {
247
+ srand((unsigned)time(NULL));
248
+ return rand();
249
+ }
250
+ }
data/ext/bson/write.c CHANGED
@@ -20,18 +20,17 @@
20
20
  typedef struct{
21
21
  byte_buffer_t *b;
22
22
  VALUE buffer;
23
- VALUE validating_keys;
24
23
  } put_hash_context;
25
24
 
26
25
  static void pvt_replace_int32(byte_buffer_t *b, int32_t position, int32_t newval);
27
- static void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validating_keys);
26
+ static void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val);
28
27
  static void pvt_put_byte(byte_buffer_t *b, const char byte);
29
28
  static void pvt_put_int32(byte_buffer_t *b, const int32_t i32);
30
29
  static void pvt_put_uint32(byte_buffer_t *b, const uint32_t i32);
31
30
  static void pvt_put_int64(byte_buffer_t *b, const int64_t i);
32
31
  static void pvt_put_double(byte_buffer_t *b, double f);
33
32
  static void pvt_put_cstring(byte_buffer_t *b, const char *str, int32_t length, const char *data_type);
34
- static void pvt_put_bson_key(byte_buffer_t *b, VALUE string, VALUE validating_keys);
33
+ static void pvt_put_bson_key(byte_buffer_t *b, VALUE string);
35
34
  static VALUE pvt_bson_byte_buffer_put_bson_partial_string(VALUE self, const char *str, int32_t length);
36
35
  static VALUE pvt_bson_byte_buffer_put_binary_string(VALUE self, const char *str, int32_t length);
37
36
 
@@ -39,7 +38,7 @@ static int fits_int32(int64_t i64){
39
38
  return i64 >= INT32_MIN && i64 <= INT32_MAX;
40
39
  }
41
40
 
42
- void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validating_keys){
41
+ void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val){
43
42
  switch(TYPE(val)){
44
43
  case T_BIGNUM:
45
44
  case T_FIXNUM:{
@@ -55,7 +54,7 @@ void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validatin
55
54
  pvt_put_double(b, NUM2DBL(val));
56
55
  break;
57
56
  case T_ARRAY:
58
- rb_bson_byte_buffer_put_array(rb_buffer, val, validating_keys);
57
+ rb_bson_byte_buffer_put_array(rb_buffer, val);
59
58
  break;
60
59
  case T_TRUE:
61
60
  pvt_put_byte(b, 1);
@@ -64,10 +63,10 @@ void pvt_put_field(byte_buffer_t *b, VALUE rb_buffer, VALUE val, VALUE validatin
64
63
  pvt_put_byte(b, 0);
65
64
  break;
66
65
  case T_HASH:
67
- rb_bson_byte_buffer_put_hash(rb_buffer, val, validating_keys);
66
+ rb_bson_byte_buffer_put_hash(rb_buffer, val);
68
67
  break;
69
68
  default:{
70
- rb_funcall(val, rb_intern("to_bson"), 2, rb_buffer, validating_keys);
69
+ rb_funcall(val, rb_intern("to_bson"), 1, rb_buffer);
71
70
  break;
72
71
  }
73
72
  }
@@ -93,7 +92,7 @@ VALUE rb_bson_byte_buffer_put_byte(VALUE self, VALUE byte)
93
92
 
94
93
  str = RSTRING_PTR(byte);
95
94
  length = RSTRING_LEN(byte);
96
-
95
+
97
96
  if (length != 1) {
98
97
  rb_raise(rb_eArgError, "put_byte requires a string of length 1");
99
98
  }
@@ -129,7 +128,7 @@ VALUE rb_bson_byte_buffer_put_bytes(VALUE self, VALUE bytes)
129
128
  /* write the byte denoting the BSON type for the passed object */
130
129
  void pvt_put_type_byte(byte_buffer_t *b, VALUE val){
131
130
  char type_byte;
132
-
131
+
133
132
  switch (TYPE(val)){
134
133
  case T_BIGNUM:
135
134
  case T_FIXNUM:
@@ -169,7 +168,7 @@ void pvt_put_type_byte(byte_buffer_t *b, VALUE val){
169
168
  break;
170
169
  }
171
170
  }
172
-
171
+
173
172
  pvt_put_byte(b, type_byte);
174
173
  }
175
174
 
@@ -213,24 +212,24 @@ static VALUE pvt_bson_encode_to_utf8(VALUE string) {
213
212
  VALUE utf8_string;
214
213
  const char *str;
215
214
  int32_t length;
216
-
215
+
217
216
  existing_encoding_name = rb_funcall(
218
217
  rb_funcall(string, rb_intern("encoding"), 0),
219
218
  rb_intern("name"), 0);
220
-
219
+
221
220
  if (strcmp(RSTRING_PTR(existing_encoding_name), "UTF-8") == 0) {
222
221
  utf8_string = string;
223
-
222
+
224
223
  str = RSTRING_PTR(utf8_string);
225
224
  length = RSTRING_LEN(utf8_string);
226
-
225
+
227
226
  rb_bson_utf8_validate(str, length, true, "String");
228
227
  } else {
229
228
  encoding = rb_enc_str_new_cstr("UTF-8", rb_utf8_encoding());
230
229
  utf8_string = rb_funcall(string, rb_intern("encode"), 1, encoding);
231
230
  RB_GC_GUARD(encoding);
232
231
  }
233
-
232
+
234
233
  return utf8_string;
235
234
  }
236
235
 
@@ -240,15 +239,15 @@ VALUE rb_bson_byte_buffer_put_string(VALUE self, VALUE string)
240
239
  VALUE utf8_string;
241
240
  const char *str;
242
241
  int32_t length;
243
-
242
+
244
243
  utf8_string = pvt_bson_encode_to_utf8(string);
245
244
  /* At this point utf8_string contains valid utf-8 byte sequences only */
246
-
245
+
247
246
  str = RSTRING_PTR(utf8_string);
248
247
  length = RSTRING_LEN(utf8_string);
249
248
 
250
249
  RB_GC_GUARD(utf8_string);
251
-
250
+
252
251
  return pvt_bson_byte_buffer_put_binary_string(self, str, length);
253
252
  }
254
253
 
@@ -287,7 +286,7 @@ VALUE rb_bson_byte_buffer_put_cstring(VALUE self, VALUE obj)
287
286
  default:
288
287
  rb_raise(rb_eTypeError, "Invalid type for put_cstring");
289
288
  }
290
-
289
+
291
290
  str = RSTRING_PTR(string);
292
291
  length = RSTRING_LEN(string);
293
292
  RB_GC_GUARD(string);
@@ -327,15 +326,9 @@ VALUE rb_bson_byte_buffer_put_symbol(VALUE self, VALUE symbol)
327
326
  /**
328
327
  * Write a hash key to the byte buffer, validating it if requested
329
328
  */
330
- void pvt_put_bson_key(byte_buffer_t *b, VALUE string, VALUE validating_keys){
329
+ void pvt_put_bson_key(byte_buffer_t *b, VALUE string){
331
330
  char *c_str = RSTRING_PTR(string);
332
331
  size_t length = RSTRING_LEN(string);
333
-
334
- if (RTEST(validating_keys)) {
335
- if (length > 0 && (c_str[0] == '$' || memchr(c_str, '.', length))) {
336
- rb_exc_raise(rb_funcall(rb_bson_illegal_key, rb_intern("new"), 1, string));
337
- }
338
- }
339
332
 
340
333
  pvt_put_cstring(b, c_str, length, "Key");
341
334
  }
@@ -351,18 +344,18 @@ VALUE rb_bson_byte_buffer_replace_int32(VALUE self, VALUE position, VALUE newval
351
344
  {
352
345
  byte_buffer_t *b;
353
346
  long _position;
354
-
347
+
355
348
  _position = NUM2LONG(position);
356
349
  if (_position < 0) {
357
350
  rb_raise(rb_eArgError, "Position given to replace_int32 cannot be negative: %ld", _position);
358
351
  }
359
-
352
+
360
353
  TypedData_Get_Struct(self, byte_buffer_t, &rb_byte_buffer_data_type, b);
361
-
354
+
362
355
  if (b->write_position < 4) {
363
356
  rb_raise(rb_eArgError, "Buffer does not have enough data to use replace_int32");
364
357
  }
365
-
358
+
366
359
  if ((size_t) _position > b->write_position - 4) {
367
360
  rb_raise(rb_eArgError, "Position given to replace_int32 is out of bounds: %ld", _position);
368
361
  }
@@ -481,7 +474,6 @@ VALUE rb_bson_byte_buffer_put_decimal128(VALUE self, VALUE low, VALUE high)
481
474
 
482
475
  static int put_hash_callback(VALUE key, VALUE val, VALUE context){
483
476
  VALUE buffer = ((put_hash_context*)context)->buffer;
484
- VALUE validating_keys = ((put_hash_context*)context)->validating_keys;
485
477
  byte_buffer_t *b = ((put_hash_context*)context)->b;
486
478
  VALUE key_str;
487
479
 
@@ -489,23 +481,23 @@ static int put_hash_callback(VALUE key, VALUE val, VALUE context){
489
481
 
490
482
  switch(TYPE(key)){
491
483
  case T_STRING:
492
- pvt_put_bson_key(b, key, validating_keys);
484
+ pvt_put_bson_key(b, key);
493
485
  break;
494
486
  case T_SYMBOL:
495
487
  key_str = rb_sym_to_s(key);
496
488
  RB_GC_GUARD(key_str);
497
- pvt_put_bson_key(b, key_str, validating_keys);
489
+ pvt_put_bson_key(b, key_str);
498
490
  break;
499
491
  default:
500
- rb_bson_byte_buffer_put_cstring(buffer, rb_funcall(key, rb_intern("to_bson_key"), 1, validating_keys));
492
+ rb_bson_byte_buffer_put_cstring(buffer, rb_funcall(key, rb_intern("to_bson_key"), 0));
501
493
  }
502
494
 
503
- pvt_put_field(b, buffer, val, validating_keys);
495
+ pvt_put_field(b, buffer, val);
504
496
  return ST_CONTINUE;
505
497
  }
506
498
 
507
499
  /* The docstring is in init.c. */
508
- VALUE rb_bson_byte_buffer_put_hash(VALUE self, VALUE hash, VALUE validating_keys){
500
+ VALUE rb_bson_byte_buffer_put_hash(VALUE self, VALUE hash){
509
501
  byte_buffer_t *b = NULL;
510
502
  put_hash_context context = { NULL };
511
503
  size_t position = 0;
@@ -520,7 +512,6 @@ VALUE rb_bson_byte_buffer_put_hash(VALUE self, VALUE hash, VALUE validating_keys
520
512
  /* insert length placeholder */
521
513
  pvt_put_int32(b, 0);
522
514
  context.buffer = self;
523
- context.validating_keys = validating_keys;
524
515
  context.b = b;
525
516
 
526
517
  rb_hash_foreach(hash, put_hash_callback, (VALUE)&context);
@@ -642,6 +633,10 @@ void pvt_put_array_index(byte_buffer_t *b, int32_t index)
642
633
  c_str = buffer;
643
634
  snprintf(buffer, sizeof(buffer), "%d", index);
644
635
  }
636
+ // strlen is a potential vector for out-of-bounds errors, but
637
+ // the only way for that to happen here, specifically, is if `index`
638
+ // is greater than 10e16 - 1, which is far beyond the domain of an
639
+ // int32.
645
640
  length = strlen(c_str) + 1;
646
641
  ENSURE_BSON_WRITE(b, length);
647
642
  memcpy(WRITE_PTR(b), c_str, length);
@@ -649,7 +644,7 @@ void pvt_put_array_index(byte_buffer_t *b, int32_t index)
649
644
  }
650
645
 
651
646
  /* The docstring is in init.c. */
652
- VALUE rb_bson_byte_buffer_put_array(VALUE self, VALUE array, VALUE validating_keys){
647
+ VALUE rb_bson_byte_buffer_put_array(VALUE self, VALUE array){
653
648
  byte_buffer_t *b = NULL;
654
649
  size_t new_position = 0;
655
650
  int32_t new_length = 0;
@@ -667,7 +662,7 @@ VALUE rb_bson_byte_buffer_put_array(VALUE self, VALUE array, VALUE validating_ke
667
662
  for(int32_t index=0; index < RARRAY_LEN(array); index++, array_element++){
668
663
  pvt_put_type_byte(b, *array_element);
669
664
  pvt_put_array_index(b, index);
670
- pvt_put_field(b, self, *array_element, validating_keys);
665
+ pvt_put_field(b, self, *array_element);
671
666
  }
672
667
  pvt_put_byte(b, 0);
673
668
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # rubocop:todo all
2
3
  # Copyright (C) 2018-2020 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
data/lib/bson/array.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Copyright (C) 2009-2020 MongoDB Inc.
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,8 +14,8 @@
13
14
  # See the License for the specific language governing permissions and
14
15
  # limitations under the License.
15
16
 
17
+ # The top-level BSON module.
16
18
  module BSON
17
-
18
19
  # Injects behaviour for encoding and decoding arrays to
19
20
  # and from raw bytes as specified by the BSON spec.
20
21
  #
@@ -22,7 +23,6 @@ module BSON
22
23
  #
23
24
  # @since 2.0.0
24
25
  module Array
25
-
26
26
  # An array is type 0x04 in the BSON spec.
27
27
  #
28
28
  # @since 2.0.0
@@ -41,19 +41,21 @@ module BSON
41
41
  # @see http://bsonspec.org/#/specification
42
42
  #
43
43
  # @since 2.0.0
44
- def to_bson(buffer = ByteBuffer.new, validating_keys = Config.validating_keys?)
44
+ def to_bson(buffer = ByteBuffer.new)
45
45
  if buffer.respond_to?(:put_array)
46
- buffer.put_array(self, validating_keys)
46
+ buffer.put_array(self)
47
47
  else
48
48
  position = buffer.length
49
49
  buffer.put_int32(0)
50
50
  each_with_index do |value, index|
51
51
  unless value.respond_to?(:bson_type)
52
- raise Error::UnserializableClass, "Array element at position #{index} does not define its BSON serialized type: #{value}"
52
+ raise Error::UnserializableClass,
53
+ "Array element at position #{index} does not define its BSON serialized type: #{value}"
53
54
  end
55
+
54
56
  buffer.put_byte(value.bson_type)
55
57
  buffer.put_cstring(index.to_s)
56
- value.to_bson(buffer, validating_keys)
58
+ value.to_bson(buffer)
57
59
  end
58
60
  buffer.put_byte(NULL_BYTE)
59
61
  buffer.replace_int32(position, buffer.length - position)
@@ -68,13 +70,13 @@ module BSON
68
70
  #
69
71
  # @note This is used for repairing legacy bson data.
70
72
  #
71
- # @raise [ BSON::ObjectId::Invalid ] If the array is not 12 elements.
73
+ # @raise [ BSON::Error::InvalidObjectId ] If the array is not 12 elements.
72
74
  #
73
75
  # @return [ String ] The raw object id bytes.
74
76
  #
75
77
  # @since 2.0.0
76
78
  def to_bson_object_id
77
- ObjectId.repair(self) { pack("C*") }
79
+ ObjectId.repair(self) { pack('C*') }
78
80
  end
79
81
 
80
82
  # Converts the array to a normalized value in a BSON document.
@@ -86,11 +88,11 @@ module BSON
86
88
  #
87
89
  # @since 3.0.0
88
90
  def to_bson_normalized_value
89
- map { |value| value.to_bson_normalized_value }
91
+ map(&:to_bson_normalized_value)
90
92
  end
91
93
 
92
94
  # Converts this object to a representation directly serializable to
93
- # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json.rst).
95
+ # Extended JSON (https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md).
94
96
  #
95
97
  # This method recursively invokes +as_extended_json+ with the provided
96
98
  # options on each array element.
@@ -105,10 +107,15 @@ module BSON
105
107
  end
106
108
  end
107
109
 
110
+ # Class-level methods to be added to the Array class.
108
111
  module ClassMethods
109
-
110
112
  # Deserialize the array from BSON.
111
113
  #
114
+ # @note If the argument cannot be parsed, an exception will be raised
115
+ # and the argument will be left in an undefined state. The caller
116
+ # must explicitly call `rewind` on the buffer before trying to parse
117
+ # it again.
118
+ #
112
119
  # @param [ ByteBuffer ] buffer The byte buffer.
113
120
  #
114
121
  # @option options [ nil | :bson ] :mode Decoding mode to use.
@@ -116,43 +123,62 @@ module BSON
116
123
  # @return [ Array ] The decoded array.
117
124
  #
118
125
  # @see http://bsonspec.org/#/specification
119
- #
120
- # @since 2.0.0
121
126
  def from_bson(buffer, **options)
122
127
  if buffer.respond_to?(:get_array)
123
128
  buffer.get_array(**options)
124
129
  else
125
- array = new
130
+ parse_array_from_buffer(buffer, **options)
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ # Parse an array from the buffer.
137
+ #
138
+ # @param [ ByteBuf ] buffer the buffer to read from
139
+ # @param [ Hash ] options the optional keyword arguments
140
+ #
141
+ # @return [ Array ] the array that was parsed
142
+ #
143
+ # @raise [ BSON::Error::BSONDecodeError ] if the expected number of
144
+ # bytes were not read from the buffer
145
+ def parse_array_from_buffer(buffer, **options)
146
+ new.tap do |array|
126
147
  start_position = buffer.read_position
127
148
  expected_byte_size = buffer.get_int32
128
- while (type = buffer.get_byte) != NULL_BYTE
129
- buffer.get_cstring
130
- cls = BSON::Registry.get(type)
131
- value = if options.empty?
132
- cls.from_bson(buffer)
133
- else
134
- cls.from_bson(buffer, **options)
135
- end
136
- array << value
137
- end
149
+ parse_array_elements_from_buffer(array, buffer, **options)
138
150
  actual_byte_size = buffer.read_position - start_position
139
151
  if actual_byte_size != expected_byte_size
140
- raise Error::BSONDecodeError, "Expected array to take #{expected_byte_size} bytes but it took #{actual_byte_size} bytes"
152
+ raise Error::BSONDecodeError,
153
+ "Expected array to take #{expected_byte_size} bytes but it took #{actual_byte_size} bytes"
141
154
  end
142
- array
155
+ end
156
+ end
157
+
158
+ # Parse a sequence of array elements from the buffer.
159
+ #
160
+ # @param [ Array ] array the array to populate
161
+ # @param [ ByteBuf ] buffer the buffer to read from
162
+ # @param [ Hash ] options the optional keyword arguments
163
+ def parse_array_elements_from_buffer(array, buffer, **options)
164
+ while (type = buffer.get_byte) != NULL_BYTE
165
+ buffer.get_cstring
166
+ cls = BSON::Registry.get(type)
167
+ value = if options.empty?
168
+ cls.from_bson(buffer)
169
+ else
170
+ cls.from_bson(buffer, **options)
171
+ end
172
+ array << value
143
173
  end
144
174
  end
145
175
  end
146
176
 
147
177
  # Register this type when the module is loaded.
148
- #
149
- # @since 2.0.0
150
178
  Registry.register(BSON_TYPE, ::Array)
151
179
  end
152
180
 
153
181
  # Enrich the core Array class with this module.
154
- #
155
- # @since 2.0.0
156
- ::Array.send(:include, Array)
157
- ::Array.send(:extend, Array::ClassMethods)
182
+ ::Array.include Array
183
+ ::Array.extend Array::ClassMethods
158
184
  end