oj 3.16.11 → 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/dump.h CHANGED
@@ -62,15 +62,27 @@ extern size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d,
62
62
 
63
63
  extern time_t oj_sec_from_time_hard_way(VALUE obj);
64
64
 
65
+ extern bool oj_key_skip(VALUE key, const char *only, const char *except);
66
+
65
67
  inline static void assure_size(Out out, size_t len) {
66
68
  if (out->end - out->cur <= (long)len) {
67
69
  oj_grow_out(out, len);
68
70
  }
69
71
  }
70
72
 
73
+ // Bytes that fill_indent(), or the dump_opts equivalent used when :indent is a
74
+ // String, writes at this depth. nl_size is array_size or hash_size.
75
+ inline static size_t indent_len(Out out, int depth, size_t nl_size) {
76
+ if (out->opts->dump_opts.use) {
77
+ return depth * out->opts->dump_opts.indent_size + nl_size;
78
+ }
79
+ return depth * out->indent + 1;
80
+ }
81
+
71
82
  inline static void fill_indent(Out out, int cnt) {
72
83
  if (0 < out->indent) {
73
84
  cnt *= out->indent;
85
+ assure_size(out, cnt + 1);
74
86
  *out->cur++ = '\n';
75
87
  memset(out->cur, ' ', cnt);
76
88
  out->cur += cnt;
data/ext/oj/dump_compat.c CHANGED
@@ -148,10 +148,10 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
148
148
  } else {
149
149
  size = d2 * out->indent + 2;
150
150
  }
151
- assure_size(out, size * cnt);
152
151
  cnt--;
153
152
  for (i = 0; i <= cnt; i++) {
154
153
  if (out->opts->dump_opts.use) {
154
+ assure_size(out, size);
155
155
  if (0 < out->opts->dump_opts.array_size) {
156
156
  APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
157
157
  }
@@ -162,6 +162,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
162
162
  }
163
163
  }
164
164
  } else {
165
+ assure_size(out, size);
165
166
  fill_indent(out, d2);
166
167
  }
167
168
  oj_dump_compat_val(RARRAY_AREF(a, i), d2, out, true);
@@ -298,9 +299,10 @@ static ID message_id = 0;
298
299
  static ID backtrace_id = 0;
299
300
 
300
301
  static void exception_alt(VALUE obj, int depth, Out out) {
301
- int d3 = depth + 2;
302
- size_t size = d3 * out->indent + 2;
303
- size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
302
+ int d3 = depth + 2;
303
+ size_t size = d3 * out->indent + 2;
304
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
305
+ volatile VALUE bt;
304
306
 
305
307
  if (0 == message_id) {
306
308
  message_id = rb_intern("message");
@@ -331,7 +333,12 @@ static void exception_alt(VALUE obj, int depth, Out out) {
331
333
  if (0 < out->opts->dump_opts.after_size) {
332
334
  APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
333
335
  }
334
- dump_array(rb_funcall(obj, backtrace_id, 0), depth, out, false);
336
+ bt = rb_funcall(obj, backtrace_id, 0);
337
+ if (T_ARRAY == rb_type(bt)) {
338
+ dump_array(bt, depth, out, false);
339
+ } else {
340
+ oj_dump_nil(Qnil, depth, out, false);
341
+ }
335
342
  fill_indent(out, depth);
336
343
  *out->cur++ = '}';
337
344
  *out->cur = '\0';
@@ -605,6 +612,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
605
612
  if (out->omit_nil && Qnil == value) {
606
613
  return ST_CONTINUE;
607
614
  }
615
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
616
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
617
+ return ST_CONTINUE;
618
+ }
619
+ }
608
620
  if (!out->opts->dump_opts.use) {
609
621
  assure_size(out, depth * out->indent + 1);
610
622
  fill_indent(out, depth);
data/ext/oj/dump_object.c CHANGED
@@ -10,6 +10,24 @@ static const char hex_chars[17] = "0123456789abcdef";
10
10
 
11
11
  static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
12
12
 
13
+ // An odd class is dumped as {"^O":class,...} and is rebuilt with a create
14
+ // function when loaded so there is no place to put a "^i" circular id on it.
15
+ // Registering it in the circular cache would consume an id that is never
16
+ // written and any later occurrence would then be dumped as a "^r" that refers
17
+ // to nothing. Instead dump odd classes in full each time they are encountered
18
+ // just like Time, Rational, and Complex are.
19
+ static void dump_circular_obj(VALUE obj, VALUE clas, int depth, Out out) {
20
+ if (NULL != oj_get_odd(clas)) {
21
+ dump_obj_attrs(obj, clas, 0, depth, out);
22
+ } else {
23
+ long id = oj_check_circular(obj, out);
24
+
25
+ if (0 <= id) {
26
+ dump_obj_attrs(obj, clas, id, depth, out);
27
+ }
28
+ }
29
+ }
30
+
13
31
  static void dump_time(VALUE obj, Out out) {
14
32
  switch (out->opts->time_format) {
15
33
  case RubyTime:
@@ -47,11 +65,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
47
65
  oj_dump_cstr(str, len, 0, 0, out);
48
66
  }
49
67
  } else {
50
- long id = oj_check_circular(obj, out);
51
-
52
- if (0 <= id) {
53
- dump_obj_attrs(obj, clas, id, depth, out);
54
- }
68
+ dump_circular_obj(obj, clas, depth, out);
55
69
  }
56
70
  }
57
71
  }
@@ -74,11 +88,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
74
88
  oj_dump_raw(str, len, out);
75
89
  }
76
90
  } else {
77
- long id = oj_check_circular(obj, out);
78
-
79
- if (0 <= id) {
80
- dump_obj_attrs(obj, clas, id, depth, out);
81
- }
91
+ dump_circular_obj(obj, clas, depth, out);
82
92
  }
83
93
  }
84
94
 
@@ -210,6 +220,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
210
220
  if (out->omit_nil && Qnil == value) {
211
221
  return ST_CONTINUE;
212
222
  }
223
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
224
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
225
+ return ST_CONTINUE;
226
+ }
227
+ }
213
228
  assure_size(out, size);
214
229
  fill_indent(out, depth);
215
230
  switch (rb_type(key)) {
data/ext/oj/dump_strict.c CHANGED
@@ -193,14 +193,19 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
193
193
  if (out->omit_nil && Qnil == value) {
194
194
  return ST_CONTINUE;
195
195
  }
196
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
197
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
198
+ return ST_CONTINUE;
199
+ }
200
+ }
196
201
  if (!out->opts->dump_opts.use) {
197
202
  size = depth * out->indent + 1;
198
203
  assure_size(out, size);
199
204
  fill_indent(out, depth);
200
- if (rtype == T_STRING) {
201
- oj_dump_str(key, 0, out, false);
202
- } else {
203
- oj_dump_sym(key, 0, out, false);
205
+ switch (rtype) {
206
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
207
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
208
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
204
209
  }
205
210
  *out->cur++ = ':';
206
211
  } else {
@@ -215,10 +220,10 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
215
220
  APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
216
221
  }
217
222
  }
218
- if (rtype == T_STRING) {
219
- oj_dump_str(key, 0, out, false);
220
- } else {
221
- oj_dump_sym(key, 0, out, false);
223
+ switch (rtype) {
224
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
225
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
226
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
222
227
  }
223
228
  size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
224
229
  assure_size(out, size);
data/ext/oj/extconf.rb CHANGED
@@ -32,16 +32,20 @@ have_func('pthread_mutex_init')
32
32
  have_func('getrlimit', 'sys/resource.h')
33
33
  have_func('rb_enc_interned_str')
34
34
  have_func('rb_ext_ractor_safe', 'ruby.h')
35
+ have_func('rb_hash_start', 'ruby.h')
35
36
 
36
37
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
37
38
 
38
- if with_config('--with-sse42')
39
- if try_cflags('-msse4.2')
40
- $CPPFLAGS += ' -msse4.2'
41
- dflags['OJ_USE_SSE4_2'] = 1
42
- else
43
- warn 'SSE 4.2 is not supported on this platform.'
44
- end
39
+ # SIMD optimizations use runtime CPU detection and function-level target attributes
40
+ # We do NOT add global -msse4.2/-msse2 flags here because:
41
+ # 1. It would cause illegal instruction errors on CPUs without SSE4.2
42
+ # 2. The code uses __attribute__((target("sse4.2"))) for SSE4.2 functions
43
+ # 3. Runtime detection in oj_get_simd_implementation() selects the right path
44
+ #
45
+ # We only add -msse2 if available, since SSE2 is baseline for all x86_64 CPUs
46
+ # and needed for compiling the SSE2 fallback code on 32-bit x86
47
+ if try_cflags('-msse2')
48
+ $CPPFLAGS += ' -msse2'
45
49
  end
46
50
 
47
51
  if enable_config('trace-log', false)
data/ext/oj/fast.c CHANGED
@@ -80,21 +80,10 @@ static void each_leaf(Doc doc, VALUE self);
80
80
  static int move_step(Doc doc, const char *path, int loc);
81
81
  static Leaf get_doc_leaf(Doc doc, const char *path);
82
82
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
- static void each_value(Doc doc, Leaf leaf);
83
+ static void each_value(Doc doc, Leaf leaf, VALUE self);
84
84
 
85
85
  VALUE oj_doc_class = Qundef;
86
86
 
87
- // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
88
- #ifndef HAVE_STPCPY
89
- char *stpcpy(char *dest, const char *src) {
90
- size_t cnt = strlen(src);
91
-
92
- strcpy(dest, src);
93
-
94
- return dest + cnt;
95
- }
96
- #endif
97
-
98
87
  inline static void next_non_white(ParseInfo pi) {
99
88
  for (; 1; pi->s++) {
100
89
  switch (*pi->s) {
@@ -103,12 +92,35 @@ inline static void next_non_white(ParseInfo pi) {
103
92
  case '\f':
104
93
  case '\n':
105
94
  case '\r': break;
106
- case '/': skip_comment(pi); break;
95
+ case '/':
96
+ skip_comment(pi);
97
+ // A comment that is not terminated by a newline ends on the null
98
+ // terminator. Stop here so the loop does not step past it.
99
+ if ('\0' == *pi->s) {
100
+ return;
101
+ }
102
+ break;
107
103
  default: return;
108
104
  }
109
105
  }
110
106
  }
111
107
 
108
+ inline static bool is_value_end(char c) {
109
+ switch (c) {
110
+ case '\0':
111
+ case ' ':
112
+ case '\t':
113
+ case '\f':
114
+ case '\n':
115
+ case '\r':
116
+ case '/':
117
+ case ',':
118
+ case ']':
119
+ case '}': return true;
120
+ default: return false;
121
+ }
122
+ }
123
+
112
124
  inline static char *ulong_fill(char *s, size_t num) {
113
125
  char buf[32];
114
126
  char *b = buf + sizeof(buf) - 1;
@@ -221,10 +233,11 @@ static void skip_comment(ParseInfo pi) {
221
233
  if ('*' == *pi->s && '/' == *(pi->s + 1)) {
222
234
  pi->s++;
223
235
  return;
224
- } else if ('\0' == *pi->s) {
225
- raise_error("comment not terminated", pi->str, pi->s);
226
236
  }
227
237
  }
238
+ // The loop only ends on the null terminator so the comment was never
239
+ // closed.
240
+ raise_error("comment not terminated", pi->str, pi->s);
228
241
  } else if ('/' == *pi->s) {
229
242
  for (; 1; pi->s++) {
230
243
  switch (*pi->s) {
@@ -246,6 +259,19 @@ static void skip_comment(ParseInfo pi) {
246
259
  #define NUM_MAX (FIXNUM_MAX >> 8)
247
260
  #endif
248
261
 
262
+ static void validate_integer_size(size_t limit, char *head, char *tail) {
263
+ size_t total = (size_t)(tail - head);
264
+ bool has_sign = (head[0] == '-' || head[0] == '+');
265
+ size_t digit_count = total - (has_sign ? 1 : 0);
266
+
267
+ if (digit_count > limit) {
268
+ rb_raise(oj_parse_error_class,
269
+ "integer exceeds :max_integer_digits (%lu > %lu)",
270
+ (unsigned long)digit_count,
271
+ (unsigned long)limit);
272
+ }
273
+ }
274
+
249
275
  static void leaf_fixnum_value(Leaf leaf) {
250
276
  char *s = leaf->str;
251
277
  int64_t n = 0;
@@ -265,7 +291,12 @@ static void leaf_fixnum_value(Leaf leaf) {
265
291
  }
266
292
  }
267
293
  if (big) {
268
- char c = *s;
294
+ size_t limit = oj_default_options.max_integer_digits;
295
+ char c = *s;
296
+
297
+ if (0 < limit) {
298
+ validate_integer_size(limit, leaf->str, s);
299
+ }
269
300
 
270
301
  *s = '\0';
271
302
  leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
@@ -448,27 +479,42 @@ static Leaf read_num(ParseInfo pi) {
448
479
  char *start = pi->s;
449
480
  int type = T_FIXNUM;
450
481
  Leaf leaf;
482
+ int dcnt = 0;
483
+ int ecnt = 1;
451
484
 
452
- if ('-' == *pi->s) {
485
+ // read_next() sends a leading '+' here as well, and Oj.load() takes one.
486
+ if ('-' == *pi->s || '+' == *pi->s) {
453
487
  pi->s++;
454
488
  }
455
489
  // digits
456
490
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
491
+ dcnt++;
457
492
  }
458
493
  if ('.' == *pi->s) {
459
494
  type = T_FLOAT;
460
495
  pi->s++;
461
496
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
497
+ dcnt++;
462
498
  }
463
499
  }
464
500
  if ('e' == *pi->s || 'E' == *pi->s) {
501
+ // An exponent makes it a float whether or not a '.' was seen. Without
502
+ // this leaf_fixnum_value() stops reading at the 'e' and the exponent is
503
+ // dropped.
504
+ type = T_FLOAT;
465
505
  pi->s++;
466
506
  if ('-' == *pi->s || '+' == *pi->s) {
467
507
  pi->s++;
468
508
  }
469
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
509
+ for (ecnt = 0; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
510
+ ecnt++;
470
511
  }
471
512
  }
513
+ // A float is converted with rb_cstr_to_dbl(), which has to be given
514
+ // something it can convert.
515
+ if (T_FLOAT == type && (0 == dcnt || 0 == ecnt || !is_value_end(*pi->s))) {
516
+ raise_error("invalid number", pi->str, pi->s);
517
+ }
472
518
  leaf = leaf_new(pi->doc, type);
473
519
  leaf->str = start;
474
520
 
@@ -655,7 +701,11 @@ static void doc_free(Doc doc) {
655
701
  static VALUE protect_open_proc(VALUE x) {
656
702
  ParseInfo pi = (ParseInfo)x;
657
703
 
658
- pi->doc->data = read_next(pi); // parse
704
+ pi->doc->data = read_next(pi); // parse
705
+ // read_obj() and read_array() terminate each value where it ends. The top
706
+ // level value has no closing delimiter to do that on, so a number leaf runs
707
+ // to the end of the document unless it is terminated here.
708
+ *pi->s = '\0';
659
709
  *pi->doc->where = pi->doc->data;
660
710
  pi->doc->where = pi->doc->where_path;
661
711
  if (rb_block_given_p()) {
@@ -703,21 +753,23 @@ static void mark_doc(void *ptr) {
703
753
  }
704
754
  #ifdef HAVE_RB_GC_MARK_MOVABLE
705
755
  static void compact_leaf(Leaf leaf) {
706
- switch (leaf->value_type) {
707
- case COL_VAL:
708
- if (NULL != leaf->elements) {
709
- Leaf first = leaf->elements->next;
710
- Leaf e = first;
756
+ if (NULL != leaf) {
757
+ switch (leaf->value_type) {
758
+ case COL_VAL:
759
+ if (NULL != leaf->elements) {
760
+ Leaf first = leaf->elements->next;
761
+ Leaf e = first;
711
762
 
712
- do {
713
- compact_leaf(e);
714
- e = e->next;
715
- } while (e != first);
716
- }
717
- break;
718
- case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
763
+ do {
764
+ compact_leaf(e);
765
+ e = e->next;
766
+ } while (e != first);
767
+ }
768
+ break;
769
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
719
770
 
720
- default: break;
771
+ default: break;
772
+ }
721
773
  }
722
774
  }
723
775
 
@@ -783,14 +835,11 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
783
835
  doc->self = self;
784
836
  result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
785
837
  if (given || 0 != ex) {
838
+ // The doc is detached from its wrapper so the GC will not free it.
839
+ // doc_free() releases both the doc and its json buffer (doc->json), so
840
+ // the caller must not free json separately.
786
841
  DATA_PTR(doc->self) = NULL;
787
- // TBD is this needed?
788
- /*
789
842
  doc_free(pi.doc);
790
- if (0 != ex) { // will jump so caller will not free
791
- OJ_R_FREE(json);
792
- }
793
- */
794
843
  } else {
795
844
  result = doc->self;
796
845
  }
@@ -858,7 +907,7 @@ static bool key_match(const char *pat, const char *key, int plen) {
858
907
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
859
908
  Leaf leaf = *lp;
860
909
 
861
- if (MAX_STACK <= lp - stack) {
910
+ if (MAX_STACK <= (lp + 1) - stack) {
862
911
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
863
912
  }
864
913
  if ('\0' != *path) {
@@ -939,6 +988,7 @@ static void each_leaf(Doc doc, VALUE self) {
939
988
 
940
989
  doc->where++;
941
990
  if (MAX_STACK <= doc->where - doc->where_path) {
991
+ doc->where--;
942
992
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
943
993
  }
944
994
  do {
@@ -950,11 +1000,14 @@ static void each_leaf(Doc doc, VALUE self) {
950
1000
  }
951
1001
  } else {
952
1002
  rb_yield(self);
1003
+ if (NULL == DATA_PTR(self)) {
1004
+ rb_raise(rb_eIOError, "Document closed.");
1005
+ }
953
1006
  }
954
1007
  }
955
1008
 
956
1009
  static int move_step(Doc doc, const char *path, int loc) {
957
- if (MAX_STACK <= doc->where - doc->where_path) {
1010
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
958
1011
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
959
1012
  }
960
1013
  if ('\0' == *path) {
@@ -962,8 +1015,9 @@ static int move_step(Doc doc, const char *path, int loc) {
962
1015
  } else {
963
1016
  Leaf leaf;
964
1017
 
1018
+ // A document with no root (empty or comment only JSON) has no leaf to
1019
+ // step from so the path can not be located.
965
1020
  if (0 == doc->where || 0 == (leaf = *doc->where)) {
966
- printf("*** Internal error at %s\n", path);
967
1021
  return loc;
968
1022
  }
969
1023
  if ('.' == *path && '.' == *(path + 1)) {
@@ -1043,19 +1097,22 @@ static int move_step(Doc doc, const char *path, int loc) {
1043
1097
  return loc;
1044
1098
  }
1045
1099
 
1046
- static void each_value(Doc doc, Leaf leaf) {
1100
+ static void each_value(Doc doc, Leaf leaf, VALUE self) {
1047
1101
  if (COL_VAL == leaf->value_type) {
1048
1102
  if (0 != leaf->elements) {
1049
1103
  Leaf first = leaf->elements->next;
1050
1104
  Leaf e = first;
1051
1105
 
1052
1106
  do {
1053
- each_value(doc, e);
1107
+ each_value(doc, e, self);
1054
1108
  e = e->next;
1055
1109
  } while (e != first);
1056
1110
  }
1057
1111
  } else {
1058
1112
  rb_yield(leaf_value(doc, leaf));
1113
+ if (NULL == DATA_PTR(self)) {
1114
+ rb_raise(rb_eIOError, "Document closed.");
1115
+ }
1059
1116
  }
1060
1117
  }
1061
1118
 
@@ -1091,12 +1148,7 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1091
1148
 
1092
1149
  memcpy(json, StringValuePtr(str), len);
1093
1150
  obj = parse_json(clas, json, given);
1094
- // TBD is this needed
1095
- /*
1096
- if (given) {
1097
- OJ_R_FREE(json);
1098
- }
1099
- */
1151
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1100
1152
  return obj;
1101
1153
  }
1102
1154
 
@@ -1128,7 +1180,9 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1128
1180
  int given = rb_block_given_p();
1129
1181
 
1130
1182
  path = StringValuePtr(filename);
1131
- if (0 == (f = fopen(path, "r"))) {
1183
+ // Open in binary mode. On Windows a text mode read translates CRLF into LF
1184
+ // so fewer bytes are read than the file size reported by ftell().
1185
+ if (0 == (f = fopen(path, "rb"))) {
1132
1186
  rb_raise(rb_eIOError, "%s", strerror(errno));
1133
1187
  }
1134
1188
  fseek(f, 0, SEEK_END);
@@ -1146,12 +1200,7 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1146
1200
  fclose(f);
1147
1201
  json[len] = '\0';
1148
1202
  obj = parse_json(clas, json, given);
1149
- // TBD is this needed
1150
- /*
1151
- if (given) {
1152
- OJ_R_FREE(json);
1153
- }
1154
- */
1203
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1155
1204
  return obj;
1156
1205
  }
1157
1206
 
@@ -1250,10 +1299,14 @@ static VALUE doc_path(VALUE self) {
1250
1299
  * #=> nil
1251
1300
  */
1252
1301
  static VALUE doc_local_key(VALUE self) {
1253
- Doc doc = self_doc(self);
1254
- Leaf leaf = *doc->where;
1255
- volatile VALUE key = Qnil;
1302
+ Doc doc = self_doc(self);
1303
+ Leaf leaf;
1304
+ volatile VALUE key = Qnil;
1256
1305
 
1306
+ if (NULL == doc->where || NULL == *doc->where) {
1307
+ return Qnil;
1308
+ }
1309
+ leaf = *doc->where;
1257
1310
  if (T_HASH == leaf->parent_type) {
1258
1311
  key = rb_utf8_str_new_cstr(leaf->key);
1259
1312
  } else if (T_ARRAY == leaf->parent_type) {
@@ -1408,6 +1461,9 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1408
1461
  return Qnil;
1409
1462
  }
1410
1463
  }
1464
+ if (NULL == doc->where || NULL == *doc->where) {
1465
+ return Qnil;
1466
+ }
1411
1467
  each_leaf(doc, self);
1412
1468
  if (0 < wlen) {
1413
1469
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1491,12 +1547,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1491
1547
  Leaf first = (*doc->where)->elements->next;
1492
1548
  Leaf e = first;
1493
1549
 
1550
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
1551
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1552
+ }
1494
1553
  doc->where++;
1495
1554
  do {
1496
1555
  *doc->where = e;
1497
1556
  rb_yield(self);
1557
+ if (NULL == DATA_PTR(self)) {
1558
+ rb_raise(rb_eIOError, "Document closed.");
1559
+ }
1498
1560
  e = e->next;
1499
1561
  } while (e != first);
1562
+ doc->where--;
1500
1563
  }
1501
1564
  if (0 < wlen) {
1502
1565
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1540,7 +1603,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1540
1603
  path = StringValuePtr(*argv);
1541
1604
  }
1542
1605
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1543
- each_value(doc, leaf);
1606
+ each_value(doc, leaf, self);
1544
1607
  }
1545
1608
  }
1546
1609
  return Qnil;
data/ext/oj/intern.c CHANGED
@@ -69,7 +69,7 @@ static VALUE form_attr(const char *str, size_t len) {
69
69
  memcpy(b + 1, str, len);
70
70
  b[len + 1] = '\0';
71
71
  }
72
- id = rb_intern3(buf, len + 1, oj_utf8_encoding);
72
+ id = rb_intern3(b, len + 1, oj_utf8_encoding);
73
73
  OJ_R_FREE(b);
74
74
  return id;
75
75
  }
@@ -97,6 +97,8 @@ static const rb_data_type_t oj_cache_type = {
97
97
  };
98
98
 
99
99
  void oj_hash_init(void) {
100
+ oj_hash_seed_init();
101
+
100
102
  VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
101
103
  rb_undef_alloc_func(cache_class);
102
104
 
@@ -151,7 +153,7 @@ ID oj_attr_intern(const char *key, size_t len) {
151
153
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
152
154
  const uint8_t *end = key + len;
153
155
  const uint8_t *endless = key + (len & 0xFFFFFFFC);
154
- uint64_t h = (uint64_t)len;
156
+ uint64_t h = (uint64_t)len ^ oj_hash_seed;
155
157
  uint64_t k;
156
158
 
157
159
  while (key < endless) {
@@ -188,6 +190,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define
188
190
 
189
191
  if (rb_const_defined_at(mod, ci)) {
190
192
  clas = rb_const_get_at(mod, ci);
193
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
194
+ clas = Qundef;
195
+ }
191
196
  } else if (auto_define) {
192
197
  clas = rb_define_class_under(mod, classname, oj_bag_class);
193
198
  } else {
data/ext/oj/mimic_json.c CHANGED
@@ -711,6 +711,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
711
711
  0, // cache_str
712
712
  0, // int_range_min
713
713
  0, // int_range_max
714
+ 0, // max_integer_digits
714
715
  oj_json_class, // create_id
715
716
  10, // create_id_len
716
717
  3, // sec_prec
@@ -735,6 +736,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
735
736
  false, // omit_nil
736
737
  false, // omit_null_byte
737
738
  100, // max_depth
739
+ NULL, // only
740
+ NULL, // except
738
741
  },
739
742
  {
740
743
  // str_rx