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.
- checksums.yaml +4 -4
- data/README.md +94 -10
- data/Rakefile +68 -39
- data/ext/bson/bson-native.h +12 -4
- data/ext/bson/extconf.rb +8 -3
- data/ext/bson/init.c +11 -11
- data/ext/bson/read.c +39 -9
- data/ext/bson/util.c +171 -16
- data/ext/bson/write.c +34 -39
- data/lib/bson/active_support.rb +1 -0
- data/lib/bson/array.rb +58 -32
- data/lib/bson/big_decimal.rb +16 -6
- data/lib/bson/binary.rb +271 -129
- data/lib/bson/boolean.rb +1 -0
- data/lib/bson/code.rb +10 -12
- data/lib/bson/code_with_scope.rb +9 -11
- data/lib/bson/config.rb +1 -27
- data/lib/bson/date.rb +2 -1
- data/lib/bson/date_time.rb +2 -1
- data/lib/bson/db_pointer.rb +12 -13
- data/lib/bson/dbref.rb +11 -9
- data/lib/bson/decimal128/builder.rb +10 -9
- data/lib/bson/decimal128.rb +25 -111
- data/lib/bson/document.rb +1 -0
- data/lib/bson/environment.rb +1 -0
- data/lib/bson/error/bson_decode_error.rb +11 -0
- data/lib/bson/error/ext_json_parse_error.rb +11 -0
- data/lib/bson/error/illegal_key.rb +23 -0
- data/lib/bson/error/invalid_binary_type.rb +37 -0
- data/lib/bson/error/invalid_dbref_argument.rb +12 -0
- data/lib/bson/error/invalid_decimal128_argument.rb +25 -0
- data/lib/bson/error/invalid_decimal128_range.rb +27 -0
- data/lib/bson/error/invalid_decimal128_string.rb +26 -0
- data/lib/bson/error/invalid_key.rb +24 -0
- data/lib/bson/error/invalid_object_id.rb +11 -0
- data/lib/bson/error/invalid_regexp_pattern.rb +13 -0
- data/lib/bson/error/unrepresentable_precision.rb +19 -0
- data/lib/bson/error/unserializable_class.rb +13 -0
- data/lib/bson/error/unsupported_binary_subtype.rb +12 -0
- data/lib/bson/error/unsupported_type.rb +11 -0
- data/lib/bson/error.rb +16 -28
- data/lib/bson/ext_json.rb +2 -1
- data/lib/bson/false_class.rb +2 -1
- data/lib/bson/float.rb +3 -2
- data/lib/bson/hash.rb +128 -73
- data/lib/bson/int32.rb +17 -5
- data/lib/bson/int64.rb +17 -5
- data/lib/bson/integer.rb +4 -5
- data/lib/bson/json.rb +1 -0
- data/lib/bson/max_key.rb +8 -10
- data/lib/bson/min_key.rb +8 -10
- data/lib/bson/nil_class.rb +1 -0
- data/lib/bson/object.rb +7 -27
- data/lib/bson/object_id.rb +84 -120
- data/lib/bson/open_struct.rb +3 -2
- data/lib/bson/regexp.rb +36 -65
- data/lib/bson/registry.rb +2 -6
- data/lib/bson/specialized.rb +2 -1
- data/lib/bson/string.rb +4 -27
- data/lib/bson/symbol.rb +23 -20
- data/lib/bson/time.rb +3 -2
- data/lib/bson/time_with_zone.rb +13 -1
- data/lib/bson/timestamp.rb +3 -2
- data/lib/bson/true_class.rb +2 -1
- data/lib/bson/undefined.rb +15 -1
- data/lib/bson/version.rb +3 -1
- data/lib/bson.rb +3 -2
- data/spec/bson/array_spec.rb +19 -60
- data/spec/bson/big_decimal_spec.rb +16 -4
- data/spec/bson/binary_spec.rb +129 -81
- data/spec/bson/binary_uuid_spec.rb +1 -0
- data/spec/bson/boolean_spec.rb +1 -0
- data/spec/bson/byte_buffer_read_spec.rb +1 -0
- data/spec/bson/byte_buffer_spec.rb +1 -0
- data/spec/bson/byte_buffer_write_spec.rb +1 -0
- data/spec/bson/code_spec.rb +5 -3
- data/spec/bson/code_with_scope_spec.rb +5 -3
- data/spec/bson/config_spec.rb +1 -35
- data/spec/bson/date_spec.rb +1 -0
- data/spec/bson/date_time_spec.rb +1 -0
- data/spec/bson/dbref_legacy_spec.rb +20 -3
- data/spec/bson/dbref_spec.rb +9 -9
- data/spec/bson/decimal128_spec.rb +40 -20
- data/spec/bson/document_as_spec.rb +1 -0
- data/spec/bson/document_spec.rb +1 -1
- data/spec/bson/ext_json_parse_spec.rb +1 -0
- data/spec/bson/false_class_spec.rb +8 -0
- data/spec/bson/float_spec.rb +8 -3
- data/spec/bson/hash_as_spec.rb +1 -0
- data/spec/bson/hash_spec.rb +87 -75
- data/spec/bson/int32_spec.rb +21 -6
- data/spec/bson/int64_spec.rb +21 -6
- data/spec/bson/integer_spec.rb +45 -13
- data/spec/bson/json_spec.rb +1 -0
- data/spec/bson/max_key_spec.rb +5 -3
- data/spec/bson/min_key_spec.rb +5 -3
- data/spec/bson/nil_class_spec.rb +1 -0
- data/spec/bson/object_id_spec.rb +57 -4
- data/spec/bson/object_spec.rb +2 -1
- data/spec/bson/open_struct_spec.rb +14 -71
- data/spec/bson/raw_spec.rb +9 -15
- data/spec/bson/regexp_spec.rb +4 -3
- data/spec/bson/registry_spec.rb +2 -1
- data/spec/bson/string_spec.rb +13 -38
- data/spec/bson/symbol_raw_spec.rb +25 -0
- data/spec/bson/symbol_spec.rb +15 -18
- data/spec/bson/time_spec.rb +1 -0
- data/spec/bson/time_with_zone_spec.rb +1 -0
- data/spec/bson/timestamp_spec.rb +1 -0
- data/spec/bson/true_class_spec.rb +8 -0
- data/spec/bson/undefined_spec.rb +27 -0
- data/spec/bson_spec.rb +1 -0
- data/spec/runners/common_driver.rb +6 -5
- data/spec/runners/corpus.rb +6 -0
- data/spec/runners/corpus_legacy.rb +1 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/spec_tests/common_driver_spec.rb +9 -4
- data/spec/spec_tests/corpus_legacy_spec.rb +1 -0
- data/spec/spec_tests/corpus_spec.rb +13 -3
- data/spec/spec_tests/data/corpus/binary.json +5 -0
- data/spec/spec_tests/data/corpus/code.json +13 -13
- data/spec/spec_tests/data/corpus/decimal128-4.json +48 -0
- data/spec/spec_tests/data/corpus/decimal128-6.json +12 -0
- data/spec/spec_tests/data/corpus/decimal128-7.json +4 -0
- data/spec/spec_tests/data/corpus/document.json +20 -0
- data/spec/spec_tests/data/corpus/symbol.json +7 -7
- data/spec/spec_tests/data/corpus/top.json +18 -3
- data/spec/support/shared_examples.rb +28 -5
- data/spec/support/spec_config.rb +1 -0
- data/spec/support/utils.rb +49 -1
- metadata +114 -164
- checksums.yaml.gz.sig +0 -0
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/bin/s3-copy +0 -45
- data/spec/shared/bin/s3-upload +0 -69
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -231
- data/spec/shared/lib/mrss/constraints.rb +0 -386
- data/spec/shared/lib/mrss/docker_runner.rb +0 -271
- data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -191
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -120
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
- data/spec/shared/lib/mrss/utils.rb +0 -15
- data/spec/shared/share/Dockerfile.erb +0 -338
- data/spec/shared/share/haproxy-1.conf +0 -16
- data/spec/shared/share/haproxy-2.conf +0 -17
- data/spec/shared/shlib/distro.sh +0 -74
- data/spec/shared/shlib/server.sh +0 -367
- data/spec/shared/shlib/set_env.sh +0 -131
- data.tar.gz.sig +0 -0
- 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
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
66
|
+
rb_bson_byte_buffer_put_hash(rb_buffer, val);
|
68
67
|
break;
|
69
68
|
default:{
|
70
|
-
rb_funcall(val, rb_intern("to_bson"),
|
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
|
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
|
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
|
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"),
|
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
|
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
|
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
|
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
|
665
|
+
pvt_put_field(b, self, *array_element);
|
671
666
|
}
|
672
667
|
pvt_put_byte(b, 0);
|
673
668
|
|
data/lib/bson/active_support.rb
CHANGED
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
|
44
|
+
def to_bson(buffer = ByteBuffer.new)
|
45
45
|
if buffer.respond_to?(:put_array)
|
46
|
-
buffer.put_array(self
|
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,
|
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
|
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::
|
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(
|
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
|
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.
|
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
|
-
|
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
|
-
|
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,
|
152
|
+
raise Error::BSONDecodeError,
|
153
|
+
"Expected array to take #{expected_byte_size} bytes but it took #{actual_byte_size} bytes"
|
141
154
|
end
|
142
|
-
|
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
|
-
|
156
|
-
::Array.send(:include, Array)
|
157
|
-
::Array.send(:extend, Array::ClassMethods)
|
182
|
+
::Array.include Array
|
183
|
+
::Array.extend Array::ClassMethods
|
158
184
|
end
|