oj 3.17.3 → 3.17.6

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.
data/ext/oj/safe.c CHANGED
@@ -5,7 +5,7 @@ static VALUE max_hash_size_sym, max_array_size_sym, max_depth_sym, max_total_ele
5
5
  max_array_size_error_class, max_depth_error_class, max_total_elements_error_class;
6
6
 
7
7
  static void check_object_size(safe_T safe) {
8
- if (NIL_P(safe->max_hash_size)) {
8
+ if (!safe->max_hash_size_isset) {
9
9
  return;
10
10
  }
11
11
 
@@ -23,7 +23,7 @@ static void check_object_size(safe_T safe) {
23
23
  }
24
24
 
25
25
  static void check_array_size(safe_T safe) {
26
- if (NIL_P(safe->max_array_size)) {
26
+ if (!safe->max_array_size_isset) {
27
27
  return;
28
28
  }
29
29
 
@@ -41,7 +41,7 @@ static void check_array_size(safe_T safe) {
41
41
  }
42
42
 
43
43
  static void check_max_depth(safe_T safe, ojParser p) {
44
- if (NIL_P(safe->max_depth) || safe->max_depth >= (p->depth + 1)) {
44
+ if (!safe->max_depth_isset || safe->max_depth >= (p->depth + 1)) {
45
45
  return;
46
46
  }
47
47
 
@@ -55,7 +55,7 @@ static void check_max_total_elements(safe_T safe) {
55
55
  * null, true) are not counted. As a result, `current_elements_count`
56
56
  * always holds one less than the actual total.
57
57
  */
58
- if (NIL_P(safe->max_total_elements) || safe->max_total_elements > safe->current_elements_count) {
58
+ if (!safe->max_total_elements_isset || safe->max_total_elements > safe->current_elements_count) {
59
59
  return;
60
60
  }
61
61
 
@@ -140,58 +140,72 @@ DEFINE_DELEGATED_OBJECT_FUNCTION(add_float);
140
140
  DEFINE_DELEGATED_OBJECT_FUNCTION(add_big);
141
141
  DEFINE_DELEGATED_OBJECT_FUNCTION(add_str);
142
142
 
143
+ // The counting wrappers live in the parser function table, and the option
144
+ // setters of the usual parser write to the same slots, so an option set after
145
+ // the parser was built puts the unwrapped handler back and the limits stop
146
+ // being applied for that kind of value.
147
+ #define WRAP(funcs, slot, wrapper, saved) \
148
+ do { \
149
+ if (wrapper != (funcs)->slot) { \
150
+ safe->saved = (funcs)->slot; \
151
+ } \
152
+ (funcs)->slot = wrapper; \
153
+ } while (0)
154
+
155
+ static void wrap_funcs(ojParser p, safe_T safe) {
156
+ Funcs f = &p->funcs[ARRAY_FUN];
157
+
158
+ WRAP(f, open_object, safe_open_object, delegated_open_object_func);
159
+ WRAP(f, open_array, safe_open_array, delegated_open_array_func);
160
+ // The following overrides are done for counting objects
161
+ WRAP(f, add_null, safe_add_null, delegated_add_null_func);
162
+ WRAP(f, add_true, safe_add_true, delegated_add_true_func);
163
+ WRAP(f, add_false, safe_add_false, delegated_add_false_func);
164
+ WRAP(f, add_int, safe_add_int, delegated_add_int_func);
165
+ WRAP(f, add_float, safe_add_float, delegated_add_float_func);
166
+ WRAP(f, add_big, safe_add_big, delegated_add_big_func);
167
+ WRAP(f, add_str, safe_add_str, delegated_add_str_func);
168
+
169
+ f = &p->funcs[OBJECT_FUN];
170
+ WRAP(f, open_object, safe_open_object_key, delegated_open_object_key_func);
171
+ WRAP(f, open_array, safe_open_array_key, delegated_open_array_key_func);
172
+ WRAP(f, add_null, safe_add_null_key, delegated_add_null_key_func);
173
+ WRAP(f, add_true, safe_add_true_key, delegated_add_true_key_func);
174
+ WRAP(f, add_false, safe_add_false_key, delegated_add_false_key_func);
175
+ WRAP(f, add_int, safe_add_int_key, delegated_add_int_key_func);
176
+ WRAP(f, add_float, safe_add_float_key, delegated_add_float_key_func);
177
+ WRAP(f, add_big, safe_add_big_key, delegated_add_big_key_func);
178
+ WRAP(f, add_str, safe_add_str_key, delegated_add_str_key_func);
179
+ }
180
+
181
+ static VALUE safe_option(ojParser p, const char *key, VALUE value) {
182
+ safe_T safe = (safe_T)p->ctx;
183
+ VALUE rv;
184
+
185
+ if (0 == strcmp("omit_null", key)) {
186
+ return safe->omit_null ? Qtrue : Qfalse;
187
+ }
188
+ rv = safe->delegated_option_func(p, key, value);
189
+ if (0 == strcmp("omit_null=", key)) {
190
+ safe->omit_null = (Qtrue == rv);
191
+ }
192
+ wrap_funcs(p, safe);
193
+
194
+ return rv;
195
+ }
196
+
143
197
  void oj_init_safe_parser(ojParser p, safe_T safe, VALUE options) {
144
198
  // Safe parser inherits all members of usual parser
145
199
  oj_init_usual(p, &safe->usual);
146
200
 
147
201
  safe->delegated_start_func = p->start;
148
202
  p->start = safe_start;
203
+ safe->omit_null = false;
149
204
 
150
- Funcs f;
205
+ wrap_funcs(p, safe);
151
206
 
152
- // Array parser functions
153
- f = &p->funcs[ARRAY_FUN];
154
- safe->delegated_open_object_func = f->open_object;
155
- f->open_object = safe_open_object;
156
- safe->delegated_open_array_func = f->open_array;
157
- f->open_array = safe_open_array;
158
- // The following overrides are done for counting objects
159
- safe->delegated_add_null_func = f->add_null;
160
- f->add_null = safe_add_null;
161
- safe->delegated_add_true_func = f->add_true;
162
- f->add_true = safe_add_true;
163
- safe->delegated_add_false_func = f->add_false;
164
- f->add_false = safe_add_false;
165
- safe->delegated_add_int_func = f->add_int;
166
- f->add_int = safe_add_int;
167
- safe->delegated_add_float_func = f->add_float;
168
- f->add_float = safe_add_float;
169
- safe->delegated_add_big_func = f->add_big;
170
- f->add_big = safe_add_big;
171
- safe->delegated_add_str_func = f->add_str;
172
- f->add_str = safe_add_str;
173
-
174
- // Object parser functions
175
- f = &p->funcs[OBJECT_FUN];
176
- safe->delegated_open_object_key_func = f->open_object;
177
- f->open_object = safe_open_object_key;
178
- safe->delegated_open_array_key_func = f->open_array;
179
- f->open_array = safe_open_array_key;
180
- // The following overrides are done for counting objects
181
- safe->delegated_add_null_key_func = f->add_null;
182
- f->add_null = safe_add_null_key;
183
- safe->delegated_add_true_key_func = f->add_true;
184
- f->add_true = safe_add_true_key;
185
- safe->delegated_add_false_key_func = f->add_false;
186
- f->add_false = safe_add_false_key;
187
- safe->delegated_add_int_key_func = f->add_int;
188
- f->add_int = safe_add_int_key;
189
- safe->delegated_add_float_key_func = f->add_float;
190
- f->add_float = safe_add_float_key;
191
- safe->delegated_add_big_key_func = f->add_big;
192
- f->add_big = safe_add_big_key;
193
- safe->delegated_add_str_key_func = f->add_str;
194
- f->add_str = safe_add_str_key;
207
+ safe->delegated_option_func = p->option;
208
+ p->option = safe_option;
195
209
 
196
210
  SET_CONFIG(max_hash_size);
197
211
  SET_CONFIG(max_array_size);
data/ext/oj/safe.h CHANGED
@@ -8,11 +8,13 @@
8
8
  VALUE rb_##config_name = rb_hash_aref(options, config_name##_sym); \
9
9
  \
10
10
  if (RB_INTEGER_TYPE_P(rb_##config_name)) { \
11
- safe->config_name = NUM2LONG(rb_##config_name); \
11
+ safe->config_name = NUM2LONG(rb_##config_name); \
12
+ safe->config_name##_isset = true; \
12
13
  } else if (!NIL_P(rb_##config_name)) { \
13
14
  rb_raise(rb_eArgError, "Incorrect value provided for `" #config_name "`"); \
14
15
  } else { \
15
- safe->config_name = Qnil; \
16
+ safe->config_name = 0; \
17
+ safe->config_name##_isset = false; \
16
18
  } \
17
19
  } while (0);
18
20
 
@@ -49,10 +51,20 @@ typedef struct _safe_S {
49
51
  long int max_total_elements;
50
52
  long int max_json_size_bytes;
51
53
 
54
+ bool max_hash_size_isset;
55
+ bool max_array_size_isset;
56
+ bool max_depth_isset;
57
+ bool max_total_elements_isset;
58
+
52
59
  long int current_hash_size;
53
60
  long int current_array_size;
54
61
  long int current_elements_count;
55
62
 
63
+ // omit_null is kept here because the usual parser reports it by looking at
64
+ // the add_null slot, which is one of the slots wrapped below.
65
+ bool omit_null;
66
+
67
+ VALUE (*delegated_option_func)(struct _ojParser *p, const char *key, VALUE value);
56
68
  void (*delegated_start_func)(struct _ojParser *p);
57
69
 
58
70
  // Array functions
data/ext/oj/saj.c CHANGED
@@ -82,7 +82,14 @@ inline static void next_non_white(ParseInfo pi) {
82
82
  case '\f':
83
83
  case '\n':
84
84
  case '\r': break;
85
- case '/': skip_comment(pi); break;
85
+ case '/':
86
+ skip_comment(pi);
87
+ /* A comment that is not terminated by a newline ends on the null
88
+ * terminator. Stop here so the loop does not step past it. */
89
+ if ('\0' == *pi->s) {
90
+ return;
91
+ }
92
+ break;
86
93
  default: return;
87
94
  }
88
95
  }
@@ -118,14 +125,15 @@ static void skip_comment(ParseInfo pi) {
118
125
  if ('*' == *pi->s && '/' == *(pi->s + 1)) {
119
126
  pi->s++;
120
127
  return;
121
- } else if ('\0' == *pi->s) {
122
- if (pi->has_error) {
123
- call_error("comment not terminated", pi, __FILE__, __LINE__);
124
- } else {
125
- raise_error("comment not terminated", pi->str, pi->s);
126
- }
127
128
  }
128
129
  }
130
+ /* The loop only ends on the null terminator so the comment was never
131
+ * closed. */
132
+ if (pi->has_error) {
133
+ call_error("comment not terminated", pi, __FILE__, __LINE__);
134
+ } else {
135
+ raise_error("comment not terminated", pi->str, pi->s);
136
+ }
129
137
  } else if ('/' == *pi->s) {
130
138
  for (; 1; pi->s++) {
131
139
  switch (*pi->s) {
@@ -190,6 +198,12 @@ static void read_hash(ParseInfo pi, const char *key) {
190
198
  } else {
191
199
  while (1) {
192
200
  next_non_white(pi);
201
+ if ('"' != *pi->s) {
202
+ if (pi->has_error) {
203
+ call_error("invalid format, expected a key", pi, __FILE__, __LINE__);
204
+ }
205
+ raise_error("invalid format, expected a key", pi->str, pi->s);
206
+ }
193
207
  ks = read_quoted_value(pi);
194
208
  next_non_white(pi);
195
209
  if (':' == *pi->s) {
@@ -606,6 +620,19 @@ static void saj_parse(VALUE handler, char *json) {
606
620
  }
607
621
  }
608
622
 
623
+ struct _sajArgs {
624
+ VALUE handler;
625
+ char *json;
626
+ };
627
+
628
+ static VALUE protect_saj_parse(VALUE x) {
629
+ struct _sajArgs *args = (struct _sajArgs *)x;
630
+
631
+ saj_parse(args->handler, args->json);
632
+
633
+ return Qnil;
634
+ }
635
+
609
636
  /* call-seq: saj_parse(handler, io)
610
637
  *
611
638
  * Parses an IO stream or file containing an JSON document. Raises an exception
@@ -670,8 +697,17 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
670
697
  rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
671
698
  }
672
699
  }
673
- saj_parse(*argv, json);
674
- OJ_R_FREE(json);
675
-
700
+ {
701
+ // saj_parse() raises on a malformed document so the json buffer has to
702
+ // be freed even when the parse does not return normally.
703
+ struct _sajArgs args = {*argv, json};
704
+ int ex = 0;
705
+
706
+ rb_protect(protect_saj_parse, (VALUE)&args, &ex);
707
+ OJ_R_FREE(json);
708
+ if (0 != ex) {
709
+ rb_jump_tag(ex);
710
+ }
711
+ }
676
712
  return Qnil;
677
713
  }
data/ext/oj/sparse.c CHANGED
@@ -290,13 +290,13 @@ static void read_escaped_str(ParseInfo pi) {
290
290
  case NEXT_HASH_NEW:
291
291
  case NEXT_HASH_KEY:
292
292
  if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
293
- parent->klen = buf_len(&buf);
294
- parent->key = malloc(parent->klen + 1);
295
- memcpy((char *)parent->key, buf.head, parent->klen);
296
- *(char *)(parent->key + parent->klen) = '\0';
293
+ parent->klen = buf_len(&buf);
294
+ parent->key = oj_strndup(buf.head, parent->klen);
295
+ parent->kalloc = 1;
297
296
  } else {
298
- parent->key = "";
299
- parent->klen = 0;
297
+ parent->key = "";
298
+ parent->klen = 0;
299
+ parent->kalloc = 0;
300
300
  }
301
301
  parent->k1 = *pi->rd.str;
302
302
  parent->next = NEXT_HASH_COLON;
@@ -906,7 +906,17 @@ CLEANUP:
906
906
  if (0 != pi->circ_array) {
907
907
  oj_circ_array_free(pi->circ_array);
908
908
  }
909
+ reader_cleanup(&pi->rd);
909
910
  stack_cleanup(&pi->stack);
911
+ // A :match_string option builds a chain of regexps for this call that has to
912
+ // be freed. Without the option the member aliases the chain owned by the
913
+ // defaults and must be left alone.
914
+ if (pi->options.str_rx.head != oj_default_options.str_rx.head) {
915
+ oj_rxclass_cleanup(&pi->options.str_rx);
916
+ pi->options.str_rx.head = NULL;
917
+ pi->options.str_rx.tail = NULL;
918
+ }
919
+ oj_free_call_options(&pi->options);
910
920
  if (0 != fd) {
911
921
  #ifdef _WIN32
912
922
  rb_w32_close(fd);
@@ -16,15 +16,29 @@ static void stream_writer_free(void *ptr) {
16
16
  return;
17
17
  }
18
18
  sw = (StreamWriter)ptr;
19
+ oj_options_release(&sw->sw.opts);
19
20
  OJ_R_FREE(sw->sw.out.buf);
20
21
  OJ_R_FREE(sw->sw.types);
21
22
  OJ_R_FREE(ptr);
22
23
  }
23
24
 
25
+ static void stream_writer_mark(void *ptr) {
26
+ if (NULL != ptr) {
27
+ StreamWriter sw = (StreamWriter)ptr;
28
+
29
+ oj_options_mark(&sw->sw.opts);
30
+ // The writer can be the only reference to the stream as in
31
+ // Oj::StreamWriter.new(StringIO.new) so it must be marked.
32
+ if (Qnil != sw->stream) {
33
+ rb_gc_mark(sw->stream);
34
+ }
35
+ }
36
+ }
37
+
24
38
  static const rb_data_type_t oj_stream_writer_type = {
25
39
  "Oj/stream_writer",
26
40
  {
27
- NULL,
41
+ stream_writer_mark,
28
42
  stream_writer_free,
29
43
  NULL,
30
44
  },
@@ -116,6 +130,7 @@ static VALUE stream_writer_new(int argc, VALUE *argv, VALUE self) {
116
130
  oj_str_writer_init(&sw->sw, 4096);
117
131
  sw->flush_limit = 0;
118
132
  }
133
+ oj_options_take_ownership(&sw->sw.opts);
119
134
  sw->sw.out.indent = sw->sw.opts.indent;
120
135
  sw->stream = stream;
121
136
  sw->type = type;
@@ -42,12 +42,18 @@ static void maybe_comma(StrWriter sw) {
42
42
 
43
43
  // Used by stream writer also.
44
44
  void oj_str_writer_init(StrWriter sw, int buf_size) {
45
- sw->opts = oj_default_options;
46
- sw->depth = 0;
47
- sw->types = OJ_R_ALLOC_N(char, 256);
48
- sw->types_end = sw->types + 256;
49
- *sw->types = '\0';
50
- sw->keyWritten = 0;
45
+ sw->opts = oj_default_options;
46
+ // Detach from the match_string regexps owned by the defaults before the
47
+ // options are parsed. A :match_string option then builds a chain of its own
48
+ // that is freed with the writer instead of being appended to the chain the
49
+ // defaults are using.
50
+ sw->opts.str_rx.head = NULL;
51
+ sw->opts.str_rx.tail = NULL;
52
+ sw->depth = 0;
53
+ sw->types = OJ_R_ALLOC_N(char, 256);
54
+ sw->types_end = sw->types + 256;
55
+ *sw->types = '\0';
56
+ sw->keyWritten = 0;
51
57
 
52
58
  if (0 == buf_size) {
53
59
  buf_size = 4096;
@@ -72,6 +78,8 @@ void oj_str_writer_init(StrWriter sw, int buf_size) {
72
78
  sw->out.argv = NULL;
73
79
  sw->out.ropts = NULL;
74
80
  sw->out.omit_nil = oj_default_options.dump_opts.omit_nil;
81
+ // oj_str_writer_init does not call oj_out_init, so clear this here too.
82
+ sw->out.key_filter_off = false;
75
83
  }
76
84
 
77
85
  void oj_str_writer_push_key(StrWriter sw, const char *key) {
@@ -234,15 +242,22 @@ static void string_writer_free(void *ptr) {
234
242
  sw = (StrWriter)ptr;
235
243
 
236
244
  oj_out_free(&sw->out);
245
+ oj_options_release(&sw->opts);
237
246
 
238
247
  OJ_R_FREE(sw->types);
239
248
  OJ_R_FREE(ptr);
240
249
  }
241
250
 
251
+ static void string_writer_mark(void *ptr) {
252
+ if (NULL != ptr) {
253
+ oj_options_mark(&((StrWriter)ptr)->opts);
254
+ }
255
+ }
256
+
242
257
  static const rb_data_type_t oj_string_writer_type = {
243
258
  "Oj/string_writer",
244
259
  {
245
- NULL,
260
+ string_writer_mark,
246
261
  string_writer_free,
247
262
  NULL,
248
263
  },
@@ -279,6 +294,7 @@ static VALUE str_writer_new(int argc, VALUE *argv, VALUE self) {
279
294
  if (1 == argc) {
280
295
  oj_parse_options(argv[0], &sw->opts);
281
296
  }
297
+ oj_options_take_ownership(&sw->opts);
282
298
  sw->out.argc = argc - 1;
283
299
  sw->out.argv = argv + 1;
284
300
  sw->out.indent = sw->opts.indent;
data/ext/oj/usual.c CHANGED
@@ -831,6 +831,7 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
831
831
  Usual d = (Usual)p->ctx;
832
832
 
833
833
  if (Qnil == value) {
834
+ OJ_R_FREE(d->create_id);
834
835
  d->create_id = NULL;
835
836
  d->create_id_len = 0;
836
837
  p->funcs[OBJECT_FUN].add_str = add_str_key;
@@ -850,12 +851,15 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
850
851
  if (1 << (8 * sizeof(d->create_id_len)) <= len) {
851
852
  rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << (8 * sizeof(d->create_id_len)));
852
853
  }
853
- d->create_id_len = (uint8_t)len;
854
+ char *prev = d->create_id;
855
+
854
856
  d->create_id = str_dup(RSTRING_PTR(value), len);
857
+ d->create_id_len = (uint8_t)len;
855
858
  p->funcs[OBJECT_FUN].add_str = add_str_key_create;
856
859
  p->funcs[TOP_FUN].close_object = close_object_create;
857
860
  p->funcs[ARRAY_FUN].close_object = close_object_create;
858
861
  p->funcs[OBJECT_FUN].close_object = close_object_create;
862
+ OJ_R_FREE(prev);
859
863
  }
860
864
  return opt_create_id(p, value);
861
865
  }
data/ext/oj/util.c CHANGED
@@ -134,3 +134,30 @@ void sec_as_time(int64_t secs, TimeInfo ti) {
134
134
  secs = secs - (int64_t)ti->min * 60LL;
135
135
  ti->sec = (int)secs;
136
136
  }
137
+
138
+ // The inverse of sec_as_time(). The days are counted with the civil calendar
139
+ // algorithm from Howard Hinnant's date library so that no libc call is needed.
140
+ // timegm() is not available on Windows and the mktime() based replacement that
141
+ // was used there returned the wrong value in any timezone east of UTC.
142
+ int64_t time_as_sec(TimeInfo ti) {
143
+ int64_t year = (int64_t)ti->year;
144
+ int64_t mon = (int64_t)ti->mon;
145
+ int64_t era;
146
+ int64_t yoe; // year of the era, [0, 399]
147
+ int64_t doy; // day of the year, [0, 365]
148
+ int64_t doe; // day of the era, [0, 146096]
149
+ int64_t days; // days since 1970-01-01
150
+
151
+ // Start the year on March 1st so that a leap day falls on the last day of
152
+ // the year and the day of the year is a simple expression.
153
+ if (mon <= 2) {
154
+ year--;
155
+ }
156
+ era = (0 <= year ? year : year - 399) / 400;
157
+ yoe = year - era * 400;
158
+ doy = (153 * (mon + (2 < mon ? -3 : 9)) + 2) / 5 + (int64_t)ti->day - 1;
159
+ doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
160
+ days = era * 146097 + doe - 719468;
161
+
162
+ return days * SECS_PER_DAY + (int64_t)ti->hour * 3600LL + (int64_t)ti->min * 60LL + (int64_t)ti->sec;
163
+ }
data/ext/oj/util.h CHANGED
@@ -15,6 +15,7 @@ typedef struct _timeInfo {
15
15
  int year;
16
16
  }* TimeInfo;
17
17
 
18
- extern void sec_as_time(int64_t secs, TimeInfo ti);
18
+ extern void sec_as_time(int64_t secs, TimeInfo ti);
19
+ extern int64_t time_as_sec(TimeInfo ti);
19
20
 
20
21
  #endif /* OJ_UTIL_H */
data/ext/oj/val_stack.h CHANGED
@@ -35,8 +35,8 @@ typedef struct _val {
35
35
  const char *classname;
36
36
  VALUE clas;
37
37
  OddArgs odd_args;
38
- uint16_t klen;
39
- uint16_t clen;
38
+ size_t klen;
39
+ size_t clen;
40
40
  char next; // ValNext
41
41
  char k1; // first original character in the key
42
42
  char kalloc;
@@ -62,6 +62,16 @@ inline static int stack_empty(ValStack stack) {
62
62
  }
63
63
 
64
64
  inline static void stack_cleanup(ValStack stack) {
65
+ Val v;
66
+
67
+ // end_hash() frees the args of a completed odd class. Anything still here
68
+ // belongs to a parse that was abandoned part way through.
69
+ for (v = stack->head; v < stack->tail; v++) {
70
+ if (NULL != v->odd_args) {
71
+ oj_odd_free(v->odd_args);
72
+ v->odd_args = NULL;
73
+ }
74
+ }
65
75
  if (stack->base != stack->head) {
66
76
  OJ_R_FREE(stack->head);
67
77
  stack->head = NULL;
@@ -70,9 +80,11 @@ inline static void stack_cleanup(ValStack stack) {
70
80
 
71
81
  inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
72
82
  if (stack->end <= stack->tail) {
73
- size_t len = stack->end - stack->head;
74
- size_t toff = stack->tail - stack->head;
75
- Val head = stack->head;
83
+ size_t len = stack->end - stack->head;
84
+ size_t toff = stack->tail - stack->head;
85
+ Val head = stack->head;
86
+ const char *old = (const char *)stack->head;
87
+ size_t i;
76
88
 
77
89
  // A realloc can trigger a GC so make sure it happens outside the lock
78
90
  // but lock before changing pointers.
@@ -82,6 +94,13 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
82
94
  } else {
83
95
  OJ_R_REALLOC_N(head, struct _val, len + STACK_INC);
84
96
  }
97
+ // A key short enough to be kept in the Val itself points into the
98
+ // block that just moved.
99
+ for (i = 0; i < toff; i++) {
100
+ if (old <= head[i].key && head[i].key < old + sizeof(struct _val) * len) {
101
+ head[i].key = head[i].karray;
102
+ }
103
+ }
85
104
  #ifdef HAVE_PTHREAD_MUTEX_INIT
86
105
  pthread_mutex_lock(&stack->mutex);
87
106
  #else
data/ext/oj/wab.c CHANGED
@@ -375,44 +375,41 @@ static const char *read_num(const char *s, int len, int *vp) {
375
375
  }
376
376
 
377
377
  static VALUE time_parse(const char *s, int len) {
378
- struct tm tm;
379
- bool neg = false;
380
- long nsecs = 0;
381
- int i;
382
- time_t secs;
378
+ struct _timeInfo ti;
379
+ bool neg = false;
380
+ long nsecs = 0;
381
+ int i;
383
382
 
384
- memset(&tm, 0, sizeof(tm));
383
+ memset(&ti, 0, sizeof(ti));
385
384
  if ('-' == *s) {
386
385
  s++;
387
386
  neg = true;
388
387
  }
389
- if (NULL == (s = read_num(s, 4, &tm.tm_year))) {
388
+ if (NULL == (s = read_num(s, 4, &ti.year))) {
390
389
  return Qnil;
391
390
  }
392
391
  if (neg) {
393
- tm.tm_year = -tm.tm_year;
394
- neg = false;
392
+ ti.year = -ti.year;
393
+ neg = false;
395
394
  }
396
- tm.tm_year -= 1900;
397
395
  s++;
398
- if (NULL == (s = read_num(s, 2, &tm.tm_mon))) {
396
+ if (NULL == (s = read_num(s, 2, &ti.mon))) {
399
397
  return Qnil;
400
398
  }
401
- tm.tm_mon--;
402
399
  s++;
403
- if (NULL == (s = read_num(s, 2, &tm.tm_mday))) {
400
+ if (NULL == (s = read_num(s, 2, &ti.day))) {
404
401
  return Qnil;
405
402
  }
406
403
  s++;
407
- if (NULL == (s = read_num(s, 2, &tm.tm_hour))) {
404
+ if (NULL == (s = read_num(s, 2, &ti.hour))) {
408
405
  return Qnil;
409
406
  }
410
407
  s++;
411
- if (NULL == (s = read_num(s, 2, &tm.tm_min))) {
408
+ if (NULL == (s = read_num(s, 2, &ti.min))) {
412
409
  return Qnil;
413
410
  }
414
411
  s++;
415
- if (NULL == (s = read_num(s, 2, &tm.tm_sec))) {
412
+ if (NULL == (s = read_num(s, 2, &ti.sec))) {
416
413
  return Qnil;
417
414
  }
418
415
  s++;
@@ -424,16 +421,7 @@ static VALUE time_parse(const char *s, int len) {
424
421
  return Qnil;
425
422
  }
426
423
  }
427
- #if IS_WINDOWS
428
- secs = (time_t)mktime(&tm);
429
- memset(&tm, 0, sizeof(tm));
430
- tm.tm_year = 70;
431
- tm.tm_mday = 1;
432
- secs -= (time_t)mktime(&tm);
433
- #else
434
- secs = (time_t)timegm(&tm);
435
- #endif
436
- return rb_funcall(rb_time_nano_new(secs, nsecs), oj_utc_id, 0);
424
+ return rb_funcall(rb_time_nano_new((time_t)time_as_sec(&ti), nsecs), oj_utc_id, 0);
437
425
  }
438
426
 
439
427
  static VALUE protect_uri(VALUE rstr) {
data/lib/oj/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Oj
2
2
  # Current version of the module.
3
- VERSION = '3.17.3'
3
+ VERSION = '3.17.6'
4
4
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Ruby**
4
4
 
5
- Oj is compatible with Ruby 2.4+ and RBX.
5
+ Oj is compatible with Ruby 2.7+ and RBX.
6
6
  Support for JRuby has been removed as JRuby no longer supports C extensions and
7
7
  there are bugs in the older versions that are not being fixed.
8
8
 
data/pages/Custom.md CHANGED
@@ -8,9 +8,8 @@ for object dumping and loading. The `:compat` mode mimic the json gem
8
8
  including methods called for encoding and inconsistencies between
9
9
  `JSON.dump()`, `JSON.generate()`, and `JSON()`.
10
10
 
11
- The `:custom` mode is the default mode. It can be configured either by passing
12
- options to the `Oj.dump()` and `Oj.load()` methods or by modifying the default
13
- options.
11
+ The `:custom` mode can be configured either by passing options to the
12
+ `Oj.dump()` and `Oj.load()` methods or by modifying the default options.
14
13
 
15
14
  The ability to create objects from JSON object elements is supported and
16
15
  considers the `:create_additions` option. Special treatment is given to the
@@ -10,11 +10,13 @@ To enable Oj trace feature, it uses `--enable-trace-log` option when installing
10
10
  Then, the trace logs will be displayed when `:trace` option is set to `true`.
11
11
 
12
12
 
13
- ### Enable SIMD instructions
13
+ ### SIMD instructions
14
14
 
15
- ```
16
- $ gem install oj -- --with-sse42
17
- ```
15
+ SIMD optimizations are enabled automatically, so no install option is
16
+ required. At runtime Oj detects the CPU features that are available and
17
+ selects the best implementation: SSE4.2 or SSE2 on x86 / x86_64, NEON on ARM,
18
+ and a scalar fallback when none is available.
18
19
 
19
- To enable the use of SIMD instructions in Oj, it uses the `--with-sse42` option when installing the gem.
20
- This will enable the use of the SSE4.2 instructions in the internal.
20
+ Earlier versions required a `--with-sse42` option at install time to turn on
21
+ SSE4.2. That option has been removed (#982); SSE4.2 is now selected
22
+ automatically when the CPU supports it.