oj 3.17.6 → 3.17.8

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/saj2.c CHANGED
@@ -33,9 +33,30 @@ static void push_key(Saj d, VALUE key) {
33
33
  d->tail++;
34
34
  }
35
35
 
36
+ static VALUE pop_key(Saj d) {
37
+ if (d->tail <= d->keys) {
38
+ rb_raise(rb_eIndexError, "accessing key stack");
39
+ }
40
+ d->tail--;
41
+
42
+ return *d->tail;
43
+ }
44
+
36
45
  static void noop(ojParser p) {
37
46
  }
38
47
 
48
+ static void open_push_key(ojParser p) {
49
+ push_key((Saj)p->ctx, get_key(p));
50
+ }
51
+
52
+ static void close_pop_key(ojParser p) {
53
+ Saj d = (Saj)p->ctx;
54
+
55
+ if (d->keys < d->tail) {
56
+ d->tail--;
57
+ }
58
+ }
59
+
39
60
  static void open_object(ojParser p) {
40
61
  rb_funcall(((Saj)p->ctx)->handler, oj_hash_start_id, 1, Qnil);
41
62
  }
@@ -89,11 +110,7 @@ static void close_object(ojParser p) {
89
110
  VALUE key = Qnil;
90
111
 
91
112
  if (OBJECT_FUN == p->stack[p->depth]) {
92
- d->tail--;
93
- if (d->tail < d->keys) {
94
- rb_raise(rb_eIndexError, "accessing key stack");
95
- }
96
- key = *d->tail;
113
+ key = pop_key(d);
97
114
  }
98
115
  rb_funcall(d->handler, oj_hash_end_id, 1, key);
99
116
  }
@@ -103,11 +120,7 @@ static void close_object_loc(ojParser p) {
103
120
  VALUE key = Qnil;
104
121
 
105
122
  if (OBJECT_FUN == p->stack[p->depth]) {
106
- d->tail--;
107
- if (d->tail < d->keys) {
108
- rb_raise(rb_eIndexError, "accessing key stack");
109
- }
110
- key = *d->tail;
123
+ key = pop_key(d);
111
124
  }
112
125
  rb_funcall(d->handler, oj_hash_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
113
126
  }
@@ -117,11 +130,7 @@ static void close_array(ojParser p) {
117
130
  VALUE key = Qnil;
118
131
 
119
132
  if (OBJECT_FUN == p->stack[p->depth]) {
120
- d->tail--;
121
- if (d->tail < d->keys) {
122
- rb_raise(rb_eIndexError, "accessing key stack");
123
- }
124
- key = *d->tail;
133
+ key = pop_key(d);
125
134
  }
126
135
  rb_funcall(d->handler, oj_array_end_id, 1, key);
127
136
  }
@@ -131,11 +140,7 @@ static void close_array_loc(ojParser p) {
131
140
  VALUE key = Qnil;
132
141
 
133
142
  if (OBJECT_FUN == p->stack[p->depth]) {
134
- d->tail--;
135
- if (d->tail < d->keys) {
136
- rb_raise(rb_eIndexError, "accessing key stack");
137
- }
138
- key = *d->tail;
143
+ key = pop_key(d);
139
144
  }
140
145
  rb_funcall(d->handler, oj_array_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
141
146
  }
@@ -380,10 +385,15 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
380
385
  return d->handler;
381
386
  }
382
387
  if (0 == strcmp(key, "handler=")) {
388
+ int hash_start = rb_respond_to(value, oj_hash_start_id);
389
+ int hash_end = rb_respond_to(value, oj_hash_end_id);
390
+ int array_start = rb_respond_to(value, oj_array_start_id);
391
+ int array_end = rb_respond_to(value, oj_array_end_id);
392
+
383
393
  d->tail = d->keys;
384
394
  d->handler = value;
385
395
  reset(p);
386
- if (rb_respond_to(value, oj_hash_start_id)) {
396
+ if (hash_start) {
387
397
  if (1 == rb_obj_method_arity(value, oj_hash_start_id)) {
388
398
  p->funcs[TOP_FUN].open_object = open_object;
389
399
  p->funcs[ARRAY_FUN].open_object = open_object;
@@ -393,8 +403,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
393
403
  p->funcs[ARRAY_FUN].open_object = open_object_loc;
394
404
  p->funcs[OBJECT_FUN].open_object = open_object_loc_key;
395
405
  }
406
+ } else if (hash_end) {
407
+ p->funcs[OBJECT_FUN].open_object = open_push_key;
396
408
  }
397
- if (rb_respond_to(value, oj_array_start_id)) {
409
+ if (array_start) {
398
410
  if (1 == rb_obj_method_arity(value, oj_array_start_id)) {
399
411
  p->funcs[TOP_FUN].open_array = open_array;
400
412
  p->funcs[ARRAY_FUN].open_array = open_array;
@@ -404,8 +416,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
404
416
  p->funcs[ARRAY_FUN].open_array = open_array_loc;
405
417
  p->funcs[OBJECT_FUN].open_array = open_array_loc_key;
406
418
  }
419
+ } else if (array_end) {
420
+ p->funcs[OBJECT_FUN].open_array = open_push_key;
407
421
  }
408
- if (rb_respond_to(value, oj_hash_end_id)) {
422
+ if (hash_end) {
409
423
  if (1 == rb_obj_method_arity(value, oj_hash_end_id)) {
410
424
  p->funcs[TOP_FUN].close_object = close_object;
411
425
  p->funcs[ARRAY_FUN].close_object = close_object;
@@ -415,8 +429,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
415
429
  p->funcs[ARRAY_FUN].close_object = close_object_loc;
416
430
  p->funcs[OBJECT_FUN].close_object = close_object_loc;
417
431
  }
432
+ } else if (hash_start) {
433
+ p->funcs[OBJECT_FUN].close_object = close_pop_key;
418
434
  }
419
- if (rb_respond_to(value, oj_array_end_id)) {
435
+ if (array_end) {
420
436
  if (1 == rb_obj_method_arity(value, oj_array_end_id)) {
421
437
  p->funcs[TOP_FUN].close_array = close_array;
422
438
  p->funcs[ARRAY_FUN].close_array = close_array;
@@ -426,6 +442,8 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
426
442
  p->funcs[ARRAY_FUN].close_array = close_array_loc;
427
443
  p->funcs[OBJECT_FUN].close_array = close_array_loc;
428
444
  }
445
+ } else if (array_start) {
446
+ p->funcs[OBJECT_FUN].close_array = close_pop_key;
429
447
  }
430
448
  if (rb_respond_to(value, oj_add_value_id)) {
431
449
  if (2 == rb_obj_method_arity(value, oj_add_value_id)) {
@@ -547,10 +565,8 @@ static void mark(ojParser p) {
547
565
  if (Qnil != d->handler) {
548
566
  rb_gc_mark(d->handler);
549
567
  }
550
- if (!d->cache_keys) {
551
- for (kp = d->keys; kp < d->tail; kp++) {
552
- rb_gc_mark(*kp);
553
- }
568
+ for (kp = d->keys; kp < d->tail; kp++) {
569
+ rb_gc_mark(*kp);
554
570
  }
555
571
  }
556
572
 
data/ext/oj/simd.h CHANGED
@@ -98,10 +98,10 @@ static inline int oj_ctz64_fallback(uint64_t x) {
98
98
  #elif defined(__GNUC__) || defined(__clang__)
99
99
  // GCC/Clang: check for header availability and include them
100
100
  // We include headers but use target attributes to enable instructions per-function
101
- // Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable
102
- #if __has_include(<cpuid.h>)
101
+ // Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable.
102
+ // cpuid.h is available on all GCC since 4.3 and all Clang versions, so include
103
+ // it unconditionally rather than gating on __has_include (which GCC < 5 lacks).
103
104
  #include <cpuid.h>
104
- #endif
105
105
  #if defined(__SSE4_2__) || defined(__SSE2__)
106
106
  // If any SSE is enabled globally, x86intrin.h should be available
107
107
  #include <x86intrin.h>
@@ -109,6 +109,7 @@ static inline int oj_ctz64_fallback(uint64_t x) {
109
109
  #define HAVE_SIMD_SSE2 1
110
110
  #else
111
111
  // Try to include headers anyway for target attribute functions
112
+ #ifdef __has_include
112
113
  #if __has_include(<x86intrin.h>)
113
114
  #include <x86intrin.h>
114
115
  #define HAVE_SIMD_SSE4_2 1
@@ -123,6 +124,7 @@ static inline int oj_ctz64_fallback(uint64_t x) {
123
124
  #endif
124
125
  #endif
125
126
  #endif
127
+ #endif
126
128
 
127
129
  // Target attribute macros for function-level SIMD enabling
128
130
  #if defined(__clang__) || defined(__GNUC__)
data/ext/oj/sparse.c CHANGED
@@ -274,6 +274,10 @@ static void read_escaped_str(ParseInfo pi) {
274
274
  buf_cleanup(&buf);
275
275
  return;
276
276
  }
277
+ } else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
278
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
279
+ buf_cleanup(&buf);
280
+ return;
277
281
  } else {
278
282
  buf_append(&buf, c);
279
283
  }
@@ -341,6 +345,9 @@ static void read_str(ParseInfo pi) {
341
345
  read_escaped_str(pi);
342
346
  reader_release(&pi->rd);
343
347
  return;
348
+ } else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
349
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
350
+ return;
344
351
  }
345
352
  }
346
353
  if (0 == parent) { // simple add
@@ -16,6 +16,9 @@ static void stream_writer_free(void *ptr) {
16
16
  return;
17
17
  }
18
18
  sw = (StreamWriter)ptr;
19
+ if (NULL != sw->sw.out.circ_cache) {
20
+ oj_cache8_delete(sw->sw.out.circ_cache);
21
+ }
19
22
  oj_options_release(&sw->sw.opts);
20
23
  OJ_R_FREE(sw->sw.out.buf);
21
24
  OJ_R_FREE(sw->sw.types);
data/ext/oj/strict.c CHANGED
@@ -47,7 +47,29 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
47
47
  return rkey;
48
48
  }
49
49
 
50
+ // With the default Hash class the pairs of the just closed hash sit on top
51
+ // of the pair buffer; build the Hash in one bulk insert with the final size
52
+ // known instead of one rb_hash_aset per pair (which converts the small
53
+ // ar_table to an st_table mid-insertion for hashes above 8 entries).
50
54
  static void hash_end(ParseInfo pi) {
55
+ if (Qnil == pi->options.hash_class) {
56
+ ValStack stack = &pi->stack;
57
+ Val hash = stack_peek(stack);
58
+ size_t cnt = hash->pcnt;
59
+ volatile VALUE h;
60
+
61
+ #if HAVE_RB_HASH_NEW_CAPA
62
+ h = rb_hash_new_capa((long)(cnt / 2));
63
+ #else
64
+ h = rb_hash_new();
65
+ #endif
66
+ if (0 < cnt) {
67
+ rb_hash_bulk_insert((long)cnt, stack->pairs + stack->pcnt - cnt, h);
68
+ stack->pcnt -= cnt;
69
+ hash->pcnt = 0;
70
+ }
71
+ hash->val = h;
72
+ }
51
73
  TRACE_PARSE_HASH_END(pi->options.trace, pi);
52
74
  }
53
75
 
@@ -84,13 +106,19 @@ static VALUE start_hash(ParseInfo pi) {
84
106
  return rb_class_new_instance(0, NULL, pi->options.hash_class);
85
107
  }
86
108
  TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
87
- return rb_hash_new();
109
+ // The Hash is built at hash_end from the buffered pairs.
110
+ return Qundef;
88
111
  }
89
112
 
90
113
  static void hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
91
114
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
92
115
 
93
- rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), rstr);
116
+ if (Qnil == pi->options.hash_class) {
117
+ stack_pair_push(&pi->stack, oj_calc_hash_key(pi, parent), rstr);
118
+ parent->pcnt += 2;
119
+ } else {
120
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), rstr);
121
+ }
94
122
  TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rstr);
95
123
  }
96
124
 
@@ -101,12 +129,22 @@ static void hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
101
129
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
102
130
  }
103
131
  v = oj_num_as_value(ni);
104
- rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), v);
132
+ if (Qnil == pi->options.hash_class) {
133
+ stack_pair_push(&pi->stack, oj_calc_hash_key(pi, parent), v);
134
+ parent->pcnt += 2;
135
+ } else {
136
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), v);
137
+ }
105
138
  TRACE_PARSE_CALL(pi->options.trace, "set_number", pi, v);
106
139
  }
107
140
 
108
141
  static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
109
- rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), value);
142
+ if (Qnil == pi->options.hash_class) {
143
+ stack_pair_push(&pi->stack, oj_calc_hash_key(pi, parent), value);
144
+ parent->pcnt += 2;
145
+ } else {
146
+ rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), value);
147
+ }
110
148
  TRACE_PARSE_CALL(pi->options.trace, "set_value", pi, value);
111
149
  }
112
150
 
@@ -169,6 +169,11 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
169
169
  *out->cur++ = ':';
170
170
  }
171
171
  }
172
+ if (NULL != out->circ_cache) {
173
+ oj_cache8_delete(out->circ_cache);
174
+ out->circ_cache = NULL;
175
+ }
176
+ out->circ_cnt = 0;
172
177
  switch (out->opts->mode) {
173
178
  case StrictMode: oj_dump_strict_val(val, sw->depth, out); break;
174
179
  case NullMode: oj_dump_null_val(val, sw->depth, out); break;
@@ -178,6 +183,10 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
178
183
  case CustomMode: oj_dump_custom_val(val, sw->depth, out, true); break;
179
184
  default: oj_dump_custom_val(val, sw->depth, out, true); break;
180
185
  }
186
+ if (NULL != out->circ_cache) {
187
+ oj_cache8_delete(out->circ_cache);
188
+ out->circ_cache = NULL;
189
+ }
181
190
  }
182
191
 
183
192
  void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
@@ -203,16 +212,17 @@ void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
203
212
 
204
213
  void oj_str_writer_pop(StrWriter sw) {
205
214
  long size;
206
- DumpType type = sw->types[sw->depth];
215
+ DumpType type;
207
216
 
208
217
  if (sw->keyWritten) {
209
218
  sw->keyWritten = 0;
210
219
  rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
211
220
  }
212
- sw->depth--;
213
- if (0 > sw->depth) {
221
+ if (0 >= sw->depth) {
214
222
  rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
215
223
  }
224
+ type = sw->types[sw->depth];
225
+ sw->depth--;
216
226
  size = sw->depth * sw->out.indent + 2;
217
227
  assure_size(&sw->out, size);
218
228
  fill_indent(&sw->out, sw->depth);
@@ -241,6 +251,10 @@ static void string_writer_free(void *ptr) {
241
251
  }
242
252
  sw = (StrWriter)ptr;
243
253
 
254
+ if (NULL != sw->out.circ_cache) {
255
+ oj_cache8_delete(sw->out.circ_cache);
256
+ sw->out.circ_cache = NULL;
257
+ }
244
258
  oj_out_free(&sw->out);
245
259
  oj_options_release(&sw->opts);
246
260
 
data/ext/oj/usual.c CHANGED
@@ -49,7 +49,12 @@ static VALUE form_str(const char *str, size_t len) {
49
49
  }
50
50
 
51
51
  static VALUE form_sym(const char *str, size_t len) {
52
- return rb_str_intern(rb_utf8_str_new(str, len));
52
+ VALUE sym = rb_check_symbol_cstr(str, (long)len, oj_utf8_encoding);
53
+
54
+ if (Qnil == sym) {
55
+ sym = rb_str_intern(rb_utf8_str_new(str, len));
56
+ }
57
+ return sym;
53
58
  }
54
59
 
55
60
  static VALUE form_attr(const char *str, size_t len) {
@@ -80,6 +85,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, bool auto_defin
80
85
 
81
86
  if (rb_const_defined_at(mod, ci)) {
82
87
  clas = rb_const_get_at(mod, ci);
88
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
89
+ clas = Qundef;
90
+ }
83
91
  } else if (auto_define) {
84
92
  clas = rb_define_class_under(mod, classname, oj_bag_class);
85
93
  } else {
@@ -172,9 +180,9 @@ static VALUE str_key(ojParser p, Key kp) {
172
180
 
173
181
  static VALUE sym_key(ojParser p, Key kp) {
174
182
  if ((size_t)kp->len < sizeof(kp->buf)) {
175
- return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->buf, kp->len)));
183
+ return form_sym(kp->buf, kp->len);
176
184
  }
177
- return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->key, kp->len)));
185
+ return form_sym(kp->key, kp->len);
178
186
  }
179
187
 
180
188
  static ID get_attr_id(ojParser p, Key kp) {
@@ -809,7 +817,7 @@ static VALUE opt_class_cache_set(ojParser p, VALUE value) {
809
817
 
810
818
  if (Qtrue == value) {
811
819
  if (NULL == d->class_cache) {
812
- d->class_cache = cache_create(0, form_class_auto, MISS_AUTO == d->miss_class, false);
820
+ d->class_cache = cache_create(0, form_class_auto, true, false);
813
821
  }
814
822
  } else if (NULL != d->class_cache) {
815
823
  cache_free(d->class_cache);
data/ext/oj/val_stack.c CHANGED
@@ -3,8 +3,6 @@
3
3
 
4
4
  #include "val_stack.h"
5
5
 
6
- #include <string.h>
7
-
8
6
  #include "odd.h"
9
7
  #include "oj.h"
10
8
 
@@ -15,12 +13,13 @@ static void stack_mark(void *ptr) {
15
13
  if (NULL == ptr) {
16
14
  return;
17
15
  }
18
- #ifdef HAVE_PTHREAD_MUTEX_INIT
19
- pthread_mutex_lock(&stack->mutex);
20
- #else
21
- rb_mutex_lock(stack->mutex);
22
- rb_gc_mark(stack->mutex);
23
- #endif
16
+ {
17
+ size_t i;
18
+
19
+ for (i = 0; i < stack->pcnt; i++) {
20
+ rb_gc_mark(stack->pairs[i]);
21
+ }
22
+ }
24
23
  for (v = stack->head; v < stack->tail; v++) {
25
24
  if (Qnil != v->val && Qundef != v->val) {
26
25
  rb_gc_mark(v->val);
@@ -39,11 +38,6 @@ static void stack_mark(void *ptr) {
39
38
  }
40
39
  }
41
40
  }
42
- #ifdef HAVE_PTHREAD_MUTEX_INIT
43
- pthread_mutex_unlock(&stack->mutex);
44
- #else
45
- rb_mutex_unlock(stack->mutex);
46
- #endif
47
41
  }
48
42
 
49
43
  static const rb_data_type_t oj_stack_type = {
@@ -59,18 +53,12 @@ static const rb_data_type_t oj_stack_type = {
59
53
 
60
54
  VALUE
61
55
  oj_stack_init(ValStack stack) {
62
- #ifdef HAVE_PTHREAD_MUTEX_INIT
63
- int err;
64
-
65
- if (0 != (err = pthread_mutex_init(&stack->mutex, 0))) {
66
- rb_raise(rb_eException, "failed to initialize a mutex. %s", strerror(err));
67
- }
68
- #else
69
- stack->mutex = rb_mutex_new();
70
- #endif
71
56
  stack->head = stack->base;
72
57
  stack->end = stack->base + sizeof(stack->base) / sizeof(struct _val);
73
58
  stack->tail = stack->head;
59
+ stack->pairs = stack->pbase;
60
+ stack->pcnt = 0;
61
+ stack->pend = sizeof(stack->pbase) / sizeof(VALUE);
74
62
  stack->head->val = Qundef;
75
63
  stack->head->key = NULL;
76
64
  stack->head->key_val = Qundef;
@@ -79,6 +67,7 @@ oj_stack_init(ValStack stack) {
79
67
  stack->head->clas = Qundef;
80
68
  stack->head->klen = 0;
81
69
  stack->head->clen = 0;
70
+ stack->head->pcnt = 0;
82
71
  stack->head->next = NEXT_NONE;
83
72
 
84
73
  return TypedData_Wrap_Struct(oj_cstack_class, &oj_stack_type, stack);
data/ext/oj/val_stack.h CHANGED
@@ -9,9 +9,6 @@
9
9
  #include "mem.h"
10
10
  #include "odd.h"
11
11
  #include "ruby.h"
12
- #ifdef HAVE_PTHREAD_MUTEX_INIT
13
- #include <pthread.h>
14
- #endif
15
12
 
16
13
  #define STACK_INC 64
17
14
 
@@ -37,22 +34,27 @@ typedef struct _val {
37
34
  OddArgs odd_args;
38
35
  size_t klen;
39
36
  size_t clen;
37
+ size_t pcnt; // VALUEs this open hash holds in the stack pair buffer
40
38
  char next; // ValNext
41
39
  char k1; // first original character in the key
42
40
  char kalloc;
43
41
  } *Val;
44
42
 
43
+ #define PAIR_BASE_CNT 64
44
+
45
45
  typedef struct _valStack {
46
46
  struct _val base[STACK_INC];
47
- Val head; // current stack
48
- Val end; // stack end
49
- Val tail; // pointer to one past last element name on stack
50
- #ifdef HAVE_PTHREAD_MUTEX_INIT
51
- pthread_mutex_t mutex;
52
- #else
53
- VALUE mutex;
54
- #endif
55
-
47
+ VALUE pbase[PAIR_BASE_CNT]; // initial buffer for pairs
48
+ // Flat key/value pair buffer for hash modes that build each Hash in one
49
+ // rb_hash_bulk_insert at the closing brace instead of one rb_hash_aset
50
+ // per pair. Nesting is strictly LIFO so each open hash's pairs occupy
51
+ // the top of the buffer; the per-hash count lives in its Val's clen.
52
+ VALUE *pairs;
53
+ size_t pcnt; // number of live VALUEs in pairs
54
+ size_t pend; // capacity of pairs in VALUEs
55
+ Val head; // current stack
56
+ Val end; // stack end
57
+ Val tail; // pointer to one past last element name on stack
56
58
  } *ValStack;
57
59
 
58
60
  extern VALUE oj_stack_init(ValStack stack);
@@ -76,6 +78,11 @@ inline static void stack_cleanup(ValStack stack) {
76
78
  OJ_R_FREE(stack->head);
77
79
  stack->head = NULL;
78
80
  }
81
+ if (stack->pbase != stack->pairs) {
82
+ OJ_R_FREE(stack->pairs);
83
+ stack->pairs = NULL;
84
+ }
85
+ stack->pcnt = 0;
79
86
  }
80
87
 
81
88
  inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
@@ -86,8 +93,8 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
86
93
  const char *old = (const char *)stack->head;
87
94
  size_t i;
88
95
 
89
- // A realloc can trigger a GC so make sure it happens outside the lock
90
- // but lock before changing pointers.
96
+ // A realloc can trigger a GC that marks the stack, so change the
97
+ // pointers only after it returns.
91
98
  if (stack->base == stack->head) {
92
99
  head = OJ_R_ALLOC_N(struct _val, len + STACK_INC);
93
100
  memcpy(head, stack->base, sizeof(struct _val) * len);
@@ -101,19 +108,9 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
101
108
  head[i].key = head[i].karray;
102
109
  }
103
110
  }
104
- #ifdef HAVE_PTHREAD_MUTEX_INIT
105
- pthread_mutex_lock(&stack->mutex);
106
- #else
107
- rb_mutex_lock(stack->mutex);
108
- #endif
109
111
  stack->head = head;
110
112
  stack->tail = stack->head + toff;
111
113
  stack->end = stack->head + len + STACK_INC;
112
- #ifdef HAVE_PTHREAD_MUTEX_INIT
113
- pthread_mutex_unlock(&stack->mutex);
114
- #else
115
- rb_mutex_unlock(stack->mutex);
116
- #endif
117
114
  }
118
115
  stack->tail->val = val;
119
116
  stack->tail->next = next;
@@ -124,6 +121,7 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
124
121
  stack->tail->key_val = Qundef;
125
122
  stack->tail->clen = 0;
126
123
  stack->tail->klen = 0;
124
+ stack->tail->pcnt = 0;
127
125
  stack->tail->kalloc = 0;
128
126
  stack->tail++;
129
127
  }
@@ -165,6 +163,29 @@ inline static Val stack_pop(ValStack stack) {
165
163
  return 0;
166
164
  }
167
165
 
166
+ // Append one key/value pair to the pair buffer. The same GC discipline as
167
+ // stack_push: the allocation can trigger a GC that marks the pairs, so the
168
+ // buffer pointer only changes after it returns.
169
+ inline static void stack_pair_push(ValStack stack, VALUE key, VALUE value) {
170
+ if (stack->pend <= stack->pcnt + 2) {
171
+ size_t cnt = stack->pend * 2;
172
+ VALUE *pairs;
173
+
174
+ if (stack->pbase == stack->pairs) {
175
+ pairs = OJ_R_ALLOC_N(VALUE, cnt);
176
+ memcpy(pairs, stack->pairs, sizeof(VALUE) * stack->pcnt);
177
+ } else {
178
+ pairs = stack->pairs;
179
+ OJ_R_REALLOC_N(pairs, VALUE, cnt);
180
+ }
181
+ stack->pairs = pairs;
182
+ stack->pend = cnt;
183
+ }
184
+ stack->pairs[stack->pcnt] = key;
185
+ stack->pairs[stack->pcnt + 1] = value;
186
+ stack->pcnt += 2;
187
+ }
188
+
168
189
  extern const char *oj_stack_next_string(ValNext n);
169
190
 
170
191
  #endif /* OJ_VAL_STACK_H */
data/ext/oj/wab.c CHANGED
@@ -40,6 +40,7 @@ static VALUE resolve_wab_uuid_class(void) {
40
40
  volatile VALUE wab_module;
41
41
 
42
42
  wab_uuid_clas = Qnil;
43
+ rb_gc_register_address(&wab_uuid_clas);
43
44
  if (rb_const_defined_at(rb_cObject, rb_intern("WAB"))) {
44
45
  wab_module = rb_const_get_at(rb_cObject, rb_intern("WAB"));
45
46
  if (rb_const_defined_at(wab_module, rb_intern("UUID"))) {
@@ -53,6 +54,7 @@ static VALUE resolve_wab_uuid_class(void) {
53
54
  static VALUE resolve_uri_class(void) {
54
55
  if (Qundef == uri_clas) {
55
56
  uri_clas = Qnil;
57
+ rb_gc_register_address(&uri_clas);
56
58
  if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
57
59
  uri_clas = rb_const_get_at(rb_cObject, rb_intern("URI"));
58
60
  }
@@ -65,6 +67,7 @@ static VALUE resolve_uri_http_class(void) {
65
67
  volatile VALUE uri_module;
66
68
 
67
69
  uri_http_clas = Qnil;
70
+ rb_gc_register_address(&uri_http_clas);
68
71
  if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
69
72
  uri_module = rb_const_get_at(rb_cObject, rb_intern("URI"));
70
73
  if (rb_const_defined_at(uri_module, rb_intern("HTTP"))) {