oj 3.12.2 → 3.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/ext/oj/buf.h +9 -0
  4. data/ext/oj/cache.c +193 -0
  5. data/ext/oj/cache.h +20 -0
  6. data/ext/oj/compat.c +8 -22
  7. data/ext/oj/custom.c +15 -14
  8. data/ext/oj/debug.c +132 -0
  9. data/ext/oj/dump.c +12 -15
  10. data/ext/oj/dump_compat.c +3 -3
  11. data/ext/oj/dump_object.c +9 -9
  12. data/ext/oj/dump_strict.c +3 -3
  13. data/ext/oj/err.h +19 -0
  14. data/ext/oj/extconf.rb +4 -0
  15. data/ext/oj/fast.c +7 -18
  16. data/ext/oj/intern.c +398 -0
  17. data/ext/oj/intern.h +27 -0
  18. data/ext/oj/mimic_json.c +9 -9
  19. data/ext/oj/object.c +11 -59
  20. data/ext/oj/odd.c +1 -1
  21. data/ext/oj/oj.c +167 -109
  22. data/ext/oj/oj.h +2 -2
  23. data/ext/oj/parse.c +5 -5
  24. data/ext/oj/parser.c +1512 -0
  25. data/ext/oj/parser.h +90 -0
  26. data/ext/oj/rails.c +5 -5
  27. data/ext/oj/resolve.c +2 -20
  28. data/ext/oj/rxclass.c +1 -1
  29. data/ext/oj/saj.c +1 -1
  30. data/ext/oj/saj2.c +348 -0
  31. data/ext/oj/scp.c +1 -1
  32. data/ext/oj/sparse.c +2 -2
  33. data/ext/oj/stream_writer.c +4 -4
  34. data/ext/oj/strict.c +10 -27
  35. data/ext/oj/string_writer.c +2 -2
  36. data/ext/oj/usual.c +1228 -0
  37. data/ext/oj/validate.c +51 -0
  38. data/ext/oj/wab.c +9 -17
  39. data/lib/oj/error.rb +1 -1
  40. data/lib/oj/mimic.rb +1 -1
  41. data/lib/oj/version.rb +1 -1
  42. data/pages/Modes.md +2 -0
  43. data/pages/Options.md +17 -5
  44. data/pages/Parser.md +309 -0
  45. data/pages/Rails.md +2 -2
  46. data/test/json_gem/json_generator_test.rb +1 -1
  47. data/test/perf_parser.rb +184 -0
  48. data/test/test_hash.rb +1 -1
  49. data/test/test_parser.rb +27 -0
  50. data/test/test_parser_saj.rb +245 -0
  51. data/test/test_parser_usual.rb +213 -0
  52. metadata +22 -5
  53. data/ext/oj/hash.c +0 -168
  54. data/ext/oj/hash.h +0 -21
  55. data/ext/oj/hash_test.c +0 -491
data/ext/oj/parser.h ADDED
@@ -0,0 +1,90 @@
1
+ // Copyright (c) 2021 Peter Ohler, All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #ifndef OJ_PARSER_H
5
+ #define OJ_PARSER_H
6
+
7
+ #include <stdbool.h>
8
+ #include <ruby.h>
9
+
10
+ #include "buf.h"
11
+
12
+ #define TOP_FUN 0
13
+ #define ARRAY_FUN 1
14
+ #define OBJECT_FUN 2
15
+
16
+ typedef uint8_t byte;
17
+
18
+ typedef enum {
19
+ OJ_NONE = '\0',
20
+ OJ_NULL = 'n',
21
+ OJ_TRUE = 't',
22
+ OJ_FALSE = 'f',
23
+ OJ_INT = 'i',
24
+ OJ_DECIMAL = 'd',
25
+ OJ_BIG = 'b', // indicates parser buf is used
26
+ OJ_STRING = 's',
27
+ OJ_OBJECT = 'o',
28
+ OJ_ARRAY = 'a',
29
+ } ojType;
30
+
31
+ typedef struct _num {
32
+ long double dub;
33
+ int64_t fixnum; // holds all digits
34
+ uint32_t len;
35
+ int16_t div; // 10^div
36
+ int16_t exp;
37
+ uint8_t shift; // shift of fixnum to get decimal
38
+ bool neg;
39
+ bool exp_neg;
40
+ // for numbers as strings, reuse buf
41
+ } * Num;
42
+
43
+ struct _ojParser;
44
+
45
+ typedef struct _funcs {
46
+ void (*add_null)(struct _ojParser *p);
47
+ void (*add_true)(struct _ojParser *p);
48
+ void (*add_false)(struct _ojParser *p);
49
+ void (*add_int)(struct _ojParser *p);
50
+ void (*add_float)(struct _ojParser *p);
51
+ void (*add_big)(struct _ojParser *p);
52
+ void (*add_str)(struct _ojParser *p);
53
+ void (*open_array)(struct _ojParser *p);
54
+ void (*close_array)(struct _ojParser *p);
55
+ void (*open_object)(struct _ojParser *p);
56
+ void (*close_object)(struct _ojParser *p);
57
+ } * Funcs;
58
+
59
+ typedef struct _ojParser {
60
+ const char * map;
61
+ const char * next_map;
62
+ int depth;
63
+ unsigned char stack[1024];
64
+
65
+ // value data
66
+ struct _num num;
67
+ struct _buf key;
68
+ struct _buf buf;
69
+
70
+ struct _funcs funcs[3]; // indexed by XXX_FUN defines
71
+
72
+ void (*start)(struct _ojParser *p);
73
+ VALUE (*option)(struct _ojParser *p, const char *key, VALUE value);
74
+ VALUE (*result)(struct _ojParser *p);
75
+ void (*free)(struct _ojParser *p);
76
+ void (*mark)(struct _ojParser *p);
77
+
78
+ void *ctx;
79
+ VALUE reader;
80
+
81
+ char token[8];
82
+ long line;
83
+ long col;
84
+ int ri;
85
+ uint32_t ucode;
86
+ ojType type; // valType
87
+ bool just_one;
88
+ } * ojParser;
89
+
90
+ #endif /* OJ_PARSER_H */
data/ext/oj/rails.c CHANGED
@@ -157,9 +157,9 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
157
157
  assure_size(out, 2);
158
158
  *out->cur++ = '{';
159
159
  for (i = 0; i < cnt; i++) {
160
- volatile VALUE s = rb_sym_to_s(rb_ary_entry(ma, i));
160
+ volatile VALUE s = rb_sym2str(rb_ary_entry(ma, i));
161
161
 
162
- name = rb_string_value_ptr((VALUE *)&s);
162
+ name = RSTRING_PTR(s);
163
163
  len = (int)RSTRING_LEN(s);
164
164
  assure_size(out, size + sep_len + 6);
165
165
  if (0 < i) {
@@ -202,7 +202,7 @@ static void dump_enumerable(VALUE obj, int depth, Out out, bool as_ok) {
202
202
 
203
203
  static void dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
204
204
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
205
- const char * str = rb_string_value_ptr((VALUE *)&rstr);
205
+ const char * str = RSTRING_PTR(rstr);
206
206
 
207
207
  if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
208
208
  oj_dump_nil(Qnil, depth, out, false);
@@ -355,7 +355,7 @@ static void dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
355
355
  static void dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
356
356
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
357
357
 
358
- oj_dump_cstr(rb_string_value_ptr((VALUE *)&rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
358
+ oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
359
359
  }
360
360
 
361
361
  static ID parameters_id = 0;
@@ -1224,7 +1224,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1224
1224
  } else {
1225
1225
  volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1226
1226
 
1227
- strcpy(buf, rb_string_value_ptr((VALUE *)&rstr));
1227
+ strcpy(buf, RSTRING_PTR(rstr));
1228
1228
  cnt = (int)RSTRING_LEN(rstr);
1229
1229
  }
1230
1230
  }
data/ext/oj/resolve.c CHANGED
@@ -9,7 +9,7 @@
9
9
  #endif
10
10
 
11
11
  #include "err.h"
12
- #include "hash.h"
12
+ #include "intern.h"
13
13
  #include "oj.h"
14
14
  #include "parse.h"
15
15
 
@@ -66,28 +66,10 @@ resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, V
66
66
 
67
67
  VALUE
68
68
  oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
69
- VALUE clas;
70
- VALUE *slot;
71
-
72
69
  if (No == pi->options.class_cache) {
73
70
  return resolve_classpath(pi, name, len, auto_define, error_class);
74
71
  }
75
- #ifdef HAVE_PTHREAD_MUTEX_INIT
76
- pthread_mutex_lock(&oj_cache_mutex);
77
- #else
78
- rb_mutex_lock(oj_cache_mutex);
79
- #endif
80
- if (Qnil == (clas = oj_class_hash_get(name, len, &slot))) {
81
- if (Qundef != (clas = resolve_classpath(pi, name, len, auto_define, error_class))) {
82
- *slot = clas;
83
- }
84
- }
85
- #ifdef HAVE_PTHREAD_MUTEX_INIT
86
- pthread_mutex_unlock(&oj_cache_mutex);
87
- #else
88
- rb_mutex_unlock(oj_cache_mutex);
89
- #endif
90
- return clas;
72
+ return oj_class_intern(name, len, true, pi, auto_define, error_class);
91
73
  }
92
74
 
93
75
  VALUE
data/ext/oj/rxclass.c CHANGED
@@ -110,7 +110,7 @@ oj_rxclass_match(RxClass rc, const char *str, int len) {
110
110
  }
111
111
  } else if (len < (int)sizeof(buf)) {
112
112
  #if !IS_WINDOWS
113
- // string is not \0 terminated so copy and atempt a match
113
+ // string is not \0 terminated so copy and attempt a match
114
114
  memcpy(buf, str, len);
115
115
  buf[len] = '\0';
116
116
  if (0 == regexec(&rxc->rx, buf, 0, NULL, 0)) { // match
data/ext/oj/saj.c CHANGED
@@ -628,7 +628,7 @@ static void saj_parse(VALUE handler, char *json) {
628
628
  * @param [IO|String] io IO Object to read from
629
629
  * @deprecated The sc_parse() method along with the ScHandler is the preferred
630
630
  * callback parser. It is slightly faster and handles streams while the
631
- * saj_parse() methos requires a complete read before parsing.
631
+ * saj_parse() method requires a complete read before parsing.
632
632
  * @see sc_parse
633
633
  */
634
634
  VALUE
data/ext/oj/saj2.c ADDED
@@ -0,0 +1,348 @@
1
+ // Copyright (c) 2021, Peter Ohler, All rights reserved.
2
+
3
+ #include "cache.h"
4
+ #include "oj.h"
5
+ #include "parser.h"
6
+
7
+ typedef struct _delegate {
8
+ VALUE handler;
9
+ VALUE * keys;
10
+ VALUE * tail;
11
+ size_t klen;
12
+ struct _cache *str_cache;
13
+ uint8_t cache_str;
14
+ bool cache_keys;
15
+ bool thread_safe;
16
+ } * Delegate;
17
+
18
+ static VALUE get_key(ojParser p) {
19
+ Delegate d = (Delegate)p->ctx;
20
+ const char * key = buf_str(&p->key);
21
+ size_t len = buf_len(&p->key);
22
+ volatile VALUE rkey;
23
+
24
+ if (d->cache_keys) {
25
+ rkey = cache_intern(d->str_cache, key, len);
26
+ } else {
27
+ rkey = rb_utf8_str_new(key, len);
28
+ }
29
+ return rkey;
30
+ }
31
+
32
+ static void push_key(Delegate d, VALUE key) {
33
+ if (d->klen <= (size_t)(d->tail - d->keys)) {
34
+ size_t off = d->tail - d->keys;
35
+
36
+ d->klen += d->klen / 2;
37
+ REALLOC_N(d->keys, VALUE, d->klen);
38
+ d->tail = d->keys + off;
39
+ }
40
+ *d->tail = key;
41
+ d->tail++;
42
+ }
43
+
44
+ static void noop(ojParser p) {
45
+ }
46
+
47
+ static void open_object(ojParser p) {
48
+ rb_funcall(((Delegate)p->ctx)->handler, oj_hash_start_id, 1, Qnil);
49
+ }
50
+
51
+ static void open_object_key(ojParser p) {
52
+ Delegate d = (Delegate)p->ctx;
53
+ volatile VALUE key = get_key(p);
54
+
55
+ push_key(d, key);
56
+ rb_funcall(d->handler, oj_hash_start_id, 1, key);
57
+ }
58
+
59
+ static void open_array(ojParser p) {
60
+ rb_funcall(((Delegate)p->ctx)->handler, oj_array_start_id, 1, Qnil);
61
+ }
62
+
63
+ static void open_array_key(ojParser p) {
64
+ Delegate d = (Delegate)p->ctx;
65
+ volatile VALUE key = get_key(p);
66
+
67
+ push_key(d, key);
68
+ rb_funcall(d->handler, oj_array_start_id, 1, key);
69
+ }
70
+
71
+ static void close_object(ojParser p) {
72
+ Delegate d = (Delegate)p->ctx;
73
+ VALUE key = Qnil;
74
+
75
+ if (OBJECT_FUN == p->stack[p->depth]) {
76
+ d->tail--;
77
+ if (d->tail < d->keys) {
78
+ rb_raise(rb_eIndexError, "accessing key stack");
79
+ }
80
+ key = *d->tail;
81
+ }
82
+ rb_funcall(d->handler, oj_hash_end_id, 1, key);
83
+ }
84
+
85
+ static void close_array(ojParser p) {
86
+ Delegate d = (Delegate)p->ctx;
87
+ VALUE key = Qnil;
88
+
89
+ if (OBJECT_FUN == p->stack[p->depth]) {
90
+ d->tail--;
91
+ if (d->tail < d->keys) {
92
+ rb_raise(rb_eIndexError, "accessing key stack");
93
+ }
94
+ key = *d->tail;
95
+ }
96
+ rb_funcall(d->handler, oj_array_end_id, 1, key);
97
+ }
98
+
99
+ static void add_null(ojParser p) {
100
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qnil, Qnil);
101
+ }
102
+
103
+ static void add_null_key(ojParser p) {
104
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qnil, get_key(p));
105
+ }
106
+
107
+ static void add_true(ojParser p) {
108
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qtrue, Qnil);
109
+ }
110
+
111
+ static void add_true_key(ojParser p) {
112
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qtrue, get_key(p));
113
+ }
114
+
115
+ static void add_false(ojParser p) {
116
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qfalse, Qnil);
117
+ }
118
+
119
+ static void add_false_key(ojParser p) {
120
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, Qfalse, get_key(p));
121
+ }
122
+
123
+ static void add_int(ojParser p) {
124
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, LONG2NUM(p->num.fixnum), Qnil);
125
+ }
126
+
127
+ static void add_int_key(ojParser p) {
128
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, LONG2NUM(p->num.fixnum), get_key(p));
129
+ }
130
+
131
+ static void add_float(ojParser p) {
132
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, rb_float_new(p->num.dub), Qnil);
133
+ }
134
+
135
+ static void add_float_key(ojParser p) {
136
+ rb_funcall(((Delegate)p->ctx)->handler, oj_add_value_id, 2, rb_float_new(p->num.dub), get_key(p));
137
+ }
138
+
139
+ static void add_big(ojParser p) {
140
+ rb_funcall((VALUE)p->ctx,
141
+ oj_add_value_id,
142
+ 2,
143
+ rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
144
+ Qnil);
145
+ }
146
+
147
+ static void add_big_key(ojParser p) {
148
+ rb_funcall((VALUE)p->ctx,
149
+ oj_add_value_id,
150
+ 2,
151
+ rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
152
+ get_key(p));
153
+ }
154
+
155
+ static void add_str(ojParser p) {
156
+ Delegate d = (Delegate)p->ctx;
157
+ volatile VALUE rstr;
158
+ const char * str = buf_str(&p->buf);
159
+ size_t len = buf_len(&p->buf);
160
+
161
+ if (d->cache_str <= len) {
162
+ rstr = cache_intern(d->str_cache, str, len);
163
+ } else {
164
+ rstr = rb_utf8_str_new(str, len);
165
+ }
166
+ rb_funcall(d->handler, oj_add_value_id, 2, rstr, Qnil);
167
+ }
168
+
169
+ static void add_str_key(ojParser p) {
170
+ Delegate d = (Delegate)p->ctx;
171
+ volatile VALUE rstr;
172
+ const char * str = buf_str(&p->buf);
173
+ size_t len = buf_len(&p->buf);
174
+
175
+ if (d->cache_str <= len) {
176
+ rstr = cache_intern(d->str_cache, str, len);
177
+ } else {
178
+ rstr = rb_utf8_str_new(str, len);
179
+ }
180
+ rb_funcall(d->handler, oj_add_value_id, 2, rstr, get_key(p));
181
+ }
182
+
183
+ static void reset(ojParser p) {
184
+ Funcs end = p->funcs + 3;
185
+ Funcs f;
186
+
187
+ for (f = p->funcs; f < end; f++) {
188
+ f->add_null = noop;
189
+ f->add_true = noop;
190
+ f->add_false = noop;
191
+ f->add_int = noop;
192
+ f->add_float = noop;
193
+ f->add_big = noop;
194
+ f->add_str = noop;
195
+ f->open_array = noop;
196
+ f->close_array = noop;
197
+ f->open_object = noop;
198
+ f->close_object = noop;
199
+ }
200
+ }
201
+
202
+ static VALUE option(ojParser p, const char *key, VALUE value) {
203
+ Delegate d = (Delegate)p->ctx;
204
+
205
+ if (0 == strcmp(key, "handler")) {
206
+ return d->handler;
207
+ }
208
+ if (0 == strcmp(key, "handler=")) {
209
+ d->tail = d->keys;
210
+ d->handler = value;
211
+ reset(p);
212
+ if (rb_respond_to(value, oj_hash_start_id)) {
213
+ p->funcs[TOP_FUN].open_object = open_object;
214
+ p->funcs[ARRAY_FUN].open_object = open_object;
215
+ p->funcs[OBJECT_FUN].open_object = open_object_key;
216
+ }
217
+ if (rb_respond_to(value, oj_array_start_id)) {
218
+ p->funcs[TOP_FUN].open_array = open_array;
219
+ p->funcs[ARRAY_FUN].open_array = open_array;
220
+ p->funcs[OBJECT_FUN].open_array = open_array_key;
221
+ }
222
+ if (rb_respond_to(value, oj_hash_end_id)) {
223
+ p->funcs[TOP_FUN].close_object = close_object;
224
+ p->funcs[ARRAY_FUN].close_object = close_object;
225
+ p->funcs[OBJECT_FUN].close_object = close_object;
226
+ }
227
+ if (rb_respond_to(value, oj_array_end_id)) {
228
+ p->funcs[TOP_FUN].close_array = close_array;
229
+ p->funcs[ARRAY_FUN].close_array = close_array;
230
+ p->funcs[OBJECT_FUN].close_array = close_array;
231
+ }
232
+ if (rb_respond_to(value, oj_add_value_id)) {
233
+ p->funcs[TOP_FUN].add_null = add_null;
234
+ p->funcs[ARRAY_FUN].add_null = add_null;
235
+ p->funcs[OBJECT_FUN].add_null = add_null_key;
236
+
237
+ p->funcs[TOP_FUN].add_true = add_true;
238
+ p->funcs[ARRAY_FUN].add_true = add_true;
239
+ p->funcs[OBJECT_FUN].add_true = add_true_key;
240
+
241
+ p->funcs[TOP_FUN].add_false = add_false;
242
+ p->funcs[ARRAY_FUN].add_false = add_false;
243
+ p->funcs[OBJECT_FUN].add_false = add_false_key;
244
+
245
+ p->funcs[TOP_FUN].add_int = add_int;
246
+ p->funcs[ARRAY_FUN].add_int = add_int;
247
+ p->funcs[OBJECT_FUN].add_int = add_int_key;
248
+
249
+ p->funcs[TOP_FUN].add_float = add_float;
250
+ p->funcs[ARRAY_FUN].add_float = add_float;
251
+ p->funcs[OBJECT_FUN].add_float = add_float_key;
252
+
253
+ p->funcs[TOP_FUN].add_big = add_big;
254
+ p->funcs[ARRAY_FUN].add_big = add_big;
255
+ p->funcs[OBJECT_FUN].add_big = add_big_key;
256
+
257
+ p->funcs[TOP_FUN].add_str = add_str;
258
+ p->funcs[ARRAY_FUN].add_str = add_str;
259
+ p->funcs[OBJECT_FUN].add_str = add_str_key;
260
+ }
261
+ return Qnil;
262
+ }
263
+ if (0 == strcmp(key, "cache_keys")) {
264
+ return d->cache_keys ? Qtrue : Qfalse;
265
+ }
266
+ if (0 == strcmp(key, "cache_keys=")) {
267
+ d->cache_keys = (Qtrue == value);
268
+
269
+ return d->cache_keys ? Qtrue : Qfalse;
270
+ }
271
+ if (0 == strcmp(key, "cache_strings")) {
272
+ return INT2NUM((int)d->cache_str);
273
+ }
274
+ if (0 == strcmp(key, "cache_strings=")) {
275
+ int limit = NUM2INT(value);
276
+
277
+ if (CACHE_MAX_KEY < limit) {
278
+ limit = CACHE_MAX_KEY;
279
+ } else if (limit < 0) {
280
+ limit = 0;
281
+ }
282
+ d->cache_str = limit;
283
+
284
+ return INT2NUM((int)d->cache_str);
285
+ }
286
+ rb_raise(rb_eArgError, "%s is not an option for the SAJ (Simple API for JSON) delegate", key);
287
+
288
+ return Qnil; // Never reached due to the raise but required by the compiler.
289
+ }
290
+
291
+ static VALUE result(ojParser p) {
292
+ return Qnil;
293
+ }
294
+
295
+ static void start(ojParser p) {
296
+ Delegate d = (Delegate)p->ctx;
297
+
298
+ d->tail = d->keys;
299
+ }
300
+
301
+ static void dfree(ojParser p) {
302
+ Delegate d = (Delegate)p->ctx;
303
+
304
+ if (NULL != d->keys) {
305
+ xfree(d->keys);
306
+ }
307
+ cache_free(d->str_cache);
308
+ xfree(p->ctx);
309
+ }
310
+
311
+ static void mark(ojParser p) {
312
+ if (NULL == p->ctx) {
313
+ return;
314
+ }
315
+ Delegate d = (Delegate)p->ctx;
316
+ VALUE *kp;
317
+
318
+ cache_mark(d->str_cache);
319
+ if (Qnil != d->handler) {
320
+ rb_gc_mark(d->handler);
321
+ }
322
+ if (!d->cache_keys) {
323
+ for (kp = d->keys; kp < d->tail; kp++) {
324
+ rb_gc_mark(*kp);
325
+ }
326
+ }
327
+ }
328
+
329
+ static VALUE form_str(const char *str, size_t len) {
330
+ return rb_str_freeze(rb_utf8_str_new(str, len));
331
+ }
332
+
333
+ void oj_set_parser_saj(ojParser p) {
334
+ Delegate d = ALLOC(struct _delegate);
335
+
336
+ d->klen = 256;
337
+ d->keys = ALLOC_N(VALUE, d->klen);
338
+ d->tail = d->keys;
339
+ d->str_cache = cache_create(0, form_str, true);
340
+
341
+ p->ctx = (void *)d;
342
+ reset(p);
343
+ p->option = option;
344
+ p->result = result;
345
+ p->free = dfree;
346
+ p->mark = mark;
347
+ p->start = start;
348
+ }