oj 3.17.3 → 3.17.5

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/reader.c CHANGED
@@ -88,8 +88,8 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
88
88
  }
89
89
 
90
90
  int oj_reader_read(Reader reader) {
91
- int err;
92
- size_t shift = 0;
91
+ int err;
92
+ ptrdiff_t shift = 0;
93
93
 
94
94
  if (0 == reader->read_func) {
95
95
  return -1;
@@ -164,8 +164,14 @@ static VALUE partial_io_cb(VALUE rbuf) {
164
164
  }
165
165
  str = StringValuePtr(rstr);
166
166
  cnt = RSTRING_LEN(rstr);
167
- strcpy(reader->tail, str);
168
- reader->read_end = reader->tail + cnt;
167
+ if (cnt > (size_t)(reader->end - reader->tail)) {
168
+ // A misbehaving IO returned more than the requested number of bytes.
169
+ // Copying it in with strcpy() would overflow the reader buffer.
170
+ rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
171
+ }
172
+ memcpy(reader->tail, str, cnt);
173
+ reader->tail[cnt] = '\0';
174
+ reader->read_end = reader->tail + cnt;
169
175
 
170
176
  return Qtrue;
171
177
  }
@@ -184,8 +190,14 @@ static VALUE io_cb(VALUE rbuf) {
184
190
  }
185
191
  str = StringValuePtr(rstr);
186
192
  cnt = RSTRING_LEN(rstr);
187
- strcpy(reader->tail, str);
188
- reader->read_end = reader->tail + cnt;
193
+ if (cnt > (size_t)(reader->end - reader->tail)) {
194
+ // A misbehaving IO returned more than the requested number of bytes.
195
+ // Copying it in with strcpy() would overflow the reader buffer.
196
+ rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
197
+ }
198
+ memcpy(reader->tail, str, cnt);
199
+ reader->tail[cnt] = '\0';
200
+ reader->read_end = reader->tail + cnt;
189
201
 
190
202
  return Qtrue;
191
203
  }
data/ext/oj/resolve.c CHANGED
@@ -19,6 +19,9 @@ inline static VALUE resolve_classname(VALUE mod, const char *classname, int auto
19
19
 
20
20
  if (rb_const_defined_at(mod, ci)) {
21
21
  clas = rb_const_get_at(mod, ci);
22
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
23
+ clas = Qundef;
24
+ }
22
25
  } else if (auto_define) {
23
26
  clas = rb_define_class_under(mod, classname, oj_bag_class);
24
27
  } else {
@@ -32,7 +35,8 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
32
35
  VALUE clas;
33
36
  char *end = class_name + sizeof(class_name) - 1;
34
37
  char *s;
35
- const char *n = name;
38
+ const char *n = name;
39
+ size_t nlen = len;
36
40
 
37
41
  clas = rb_cObject;
38
42
  for (s = class_name; 0 < len; n++, len--) {
@@ -55,7 +59,12 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
55
59
  }
56
60
  *s = '\0';
57
61
  if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
58
- oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
62
+ if (sizeof(class_name) <= nlen) {
63
+ nlen = sizeof(class_name) - 1;
64
+ }
65
+ memcpy(class_name, name, nlen);
66
+ class_name[nlen] = '\0';
67
+ oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class '%s' is not defined", class_name);
59
68
  if (Qnil != error_class) {
60
69
  pi->err_class = error_class;
61
70
  }
data/ext/oj/rxclass.c CHANGED
@@ -142,3 +142,19 @@ void oj_rxclass_copy(RxClass src, RxClass dest) {
142
142
  }
143
143
  }
144
144
  }
145
+
146
+ // Add the Ruby objects in the chain to an array. The regexps and the classes
147
+ // can be referenced from nowhere else, as with a default match_string option,
148
+ // and are collected unless something holds onto them.
149
+ void oj_rxclass_keep(RxClass rc, VALUE keep) {
150
+ RxC rxc;
151
+
152
+ for (rxc = rc->head; NULL != rxc; rxc = rxc->next) {
153
+ if (Qnil != rxc->rrx) {
154
+ rb_ary_push(keep, rxc->rrx);
155
+ }
156
+ if (Qnil != rxc->clas) {
157
+ rb_ary_push(keep, rxc->clas);
158
+ }
159
+ }
160
+ }
data/ext/oj/rxclass.h CHANGED
@@ -22,5 +22,6 @@ extern int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas);
22
22
  extern VALUE oj_rxclass_match(RxClass rc, const char *str, size_t len);
23
23
  extern void oj_rxclass_copy(RxClass src, RxClass dest);
24
24
  extern void oj_rxclass_rappend(RxClass rc, VALUE rx, VALUE clas);
25
+ extern void oj_rxclass_keep(RxClass rc, VALUE keep);
25
26
 
26
27
  #endif /* OJ_RXCLASS_H */
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