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/scp.c CHANGED
@@ -9,7 +9,7 @@
9
9
  #include <unistd.h>
10
10
 
11
11
  #include "encode.h"
12
- #include "hash.h"
12
+ #include "intern.h"
13
13
  #include "oj.h"
14
14
  #include "parse.h"
15
15
 
data/ext/oj/sparse.c CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  #include "buf.h"
11
11
  #include "encode.h"
12
- #include "hash.h" // for oj_strndup()
12
+ #include "intern.h" // for oj_strndup()
13
13
  #include "oj.h"
14
14
  #include "parse.h"
15
15
  #include "val_stack.h"
@@ -494,7 +494,7 @@ static void read_num(ParseInfo pi) {
494
494
  if ('.' == c) {
495
495
  c = reader_get(&pi->rd);
496
496
  // A trailing . is not a valid decimal but if encountered allow it
497
- // except when mimicing the JSON gem.
497
+ // except when mimicking the JSON gem.
498
498
  if (CompatMode == pi->options.mode) {
499
499
  if (c < '0' || '9' < c) {
500
500
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
@@ -56,9 +56,9 @@ static VALUE buffer_size_sym = Qundef;
56
56
  /* Document-method: new
57
57
  * call-seq: new(io, options)
58
58
  *
59
- * Creates a new StreamWriter. Options are supported according the the
60
- * specified mode or the mode in the default options. Note that if mimic_JSON
61
- * or Oj.optimize_rails has not been called then the behavior of the modes may
59
+ * Creates a new StreamWriter. Options are supported according the specified
60
+ * mode or the mode in the default options. Note that if mimic_JSON or
61
+ * Oj.optimize_rails has not been called then the behavior of the modes may
62
62
  * not be the same as if they were.
63
63
  *
64
64
  * In addition to the regular dump options for the various modes a
@@ -67,7 +67,7 @@ static VALUE buffer_size_sym = Qundef;
67
67
  * should be and also a hint on when to flush.
68
68
  *
69
69
  * - *io* [_IO_] stream to write to
70
- * - *options* [_Hash_] formating options
70
+ * - *options* [_Hash_] formatting options
71
71
  */
72
72
  static VALUE stream_writer_new(int argc, VALUE *argv, VALUE self) {
73
73
  StreamWriterType type = STREAM_IO;
data/ext/oj/strict.c CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  #include "encode.h"
10
10
  #include "err.h"
11
- #include "hash.h"
11
+ #include "intern.h"
12
12
  #include "oj.h"
13
13
  #include "parse.h"
14
14
  #include "trace.h"
@@ -17,14 +17,7 @@ VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str) {
17
17
  volatile VALUE rstr = Qnil;
18
18
 
19
19
  if (len <= cache_str) {
20
- VALUE *slot;
21
-
22
- if (Qnil == (rstr = oj_str_hash_get(str, len, &slot))) {
23
- rstr = rb_str_new(str, len);
24
- rstr = oj_encode(rstr);
25
- *slot = rstr;
26
- rb_gc_register_address(slot);
27
- }
20
+ rstr = oj_str_intern(str, len);
28
21
  } else {
29
22
  rstr = rb_str_new(str, len);
30
23
  rstr = oj_encode(rstr);
@@ -39,31 +32,21 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
39
32
  return rkey;
40
33
  }
41
34
  if (Yes != pi->options.cache_keys) {
42
- rkey = rb_str_new(parent->key, parent->klen);
43
- rkey = oj_encode(rkey);
44
35
  if (Yes == pi->options.sym_key) {
45
- rkey = rb_str_intern(rkey);
36
+ rkey = ID2SYM(rb_intern3(parent->key, parent->klen, oj_utf8_encoding));
37
+ } else {
38
+ rkey = rb_str_new(parent->key, parent->klen);
39
+ rkey = oj_encode(rkey);
46
40
  }
41
+ OBJ_FREEZE(rkey);
47
42
  return rkey;
48
43
  }
49
- VALUE *slot;
50
-
51
44
  if (Yes == pi->options.sym_key) {
52
- if (Qnil == (rkey = oj_sym_hash_get(parent->key, parent->klen, &slot))) {
53
- rkey = rb_str_new(parent->key, parent->klen);
54
- rkey = oj_encode(rkey);
55
- rkey = rb_str_intern(rkey);
56
- *slot = rkey;
57
- rb_gc_register_address(slot);
58
- }
45
+ rkey = oj_sym_intern(parent->key, parent->klen);
59
46
  } else {
60
- if (Qnil == (rkey = oj_str_hash_get(parent->key, parent->klen, &slot))) {
61
- rkey = rb_str_new(parent->key, parent->klen);
62
- rkey = oj_encode(rkey);
63
- *slot = rkey;
64
- rb_gc_register_address(slot);
65
- }
47
+ rkey = oj_str_intern(parent->key, parent->klen);
66
48
  }
49
+ OBJ_FREEZE(rkey);
67
50
  return rkey;
68
51
  }
69
52
 
@@ -248,7 +248,7 @@ static void str_writer_free(void *ptr) {
248
248
  * should be.
249
249
  *
250
250
  * - *io* [_IO_] stream to write to
251
- * - *options* [_Hash_] formating options
251
+ * - *options* [_Hash_] formatting options
252
252
  */
253
253
  static VALUE str_writer_new(int argc, VALUE *argv, VALUE self) {
254
254
  StrWriter sw = ALLOC(struct _strWriter);
@@ -466,7 +466,7 @@ static VALUE str_writer_as_json(VALUE self) {
466
466
  * by pushing values into the document. Pushing an array or an object will
467
467
  * create that element in the JSON document and subsequent pushes will add the
468
468
  * elements to that array or object until a pop() is called. When complete
469
- * calling to_s() will return the JSON document. Note tha calling to_s() before
469
+ * calling to_s() will return the JSON document. Note that calling to_s() before
470
470
  * construction is complete will return the document in it's current state.
471
471
  */
472
472
  void oj_string_writer_init() {
data/ext/oj/usual.c ADDED
@@ -0,0 +1,1228 @@
1
+ // Copyright (c) 2021, Peter Ohler, All rights reserved.
2
+
3
+ #include "cache.h"
4
+ #include "oj.h"
5
+ #include "parser.h"
6
+
7
+ // The Usual delegate builds Ruby objects during parsing. It makes use of
8
+ // three stacks. The first is the value stack. This is where parsed values are
9
+ // placed. With the value stack the bulk creation and setting can be used
10
+ // which is significantly faster than setting Array (15x) or Hash (3x)
11
+ // elements one at a time.
12
+ //
13
+ // The second stack is the collection stack. Each element on the collection
14
+ // stack marks the start of a Hash, Array, or Object.
15
+ //
16
+ // The third stack is the key stack which is used for Hash and Object
17
+ // members. The key stack elements store the keys that could be used for
18
+ // either a Hash or Object. Since the decision on whether the parent is a Hash
19
+ // or Object can not be made until the end of the JSON object the keys remain
20
+ // as strings until just before setting the Hash or Object members.
21
+ //
22
+ // The approach taken with the usual delegate is to configure the delegate for
23
+ // the parser up front so that the various options are not checked during
24
+ // parsing and thus avoiding conditionals as much as reasonably possible in
25
+ // the more time sensitive parsing. Configuration is simply setting the
26
+ // function pointers to point to the function to be used for the selected
27
+ // option.
28
+
29
+ #define DEBUG 0
30
+
31
+ // Used to mark the start of each Hash, Array, or Object. The members point at
32
+ // positions of the start in the value stack and if not an Array into the key
33
+ // stack.
34
+ typedef struct _col {
35
+ long vi; // value stack index
36
+ long ki; // key stack index if an hash else -1 for an array
37
+ } * Col;
38
+
39
+ typedef union _key {
40
+ struct {
41
+ int16_t len;
42
+ char buf[22];
43
+ };
44
+ struct {
45
+ int16_t xlen; // should be the same as len
46
+ char * key;
47
+ };
48
+ } * Key;
49
+
50
+ #define MISS_AUTO 'A'
51
+ #define MISS_RAISE 'R'
52
+ #define MISS_IGNORE 'I'
53
+
54
+ typedef struct _delegate {
55
+ VALUE *vhead;
56
+ VALUE *vtail;
57
+ VALUE *vend;
58
+
59
+ Col chead;
60
+ Col ctail;
61
+ Col cend;
62
+
63
+ Key khead;
64
+ Key ktail;
65
+ Key kend;
66
+
67
+ VALUE (*get_key)(ojParser p, Key kp);
68
+ struct _cache *key_cache; // same as str_cache or sym_cache
69
+ struct _cache *str_cache;
70
+ struct _cache *sym_cache;
71
+ struct _cache *class_cache;
72
+ struct _cache *attr_cache;
73
+
74
+ VALUE array_class;
75
+ VALUE hash_class;
76
+
77
+ char * create_id;
78
+ uint8_t create_id_len;
79
+ uint8_t cache_str;
80
+ uint8_t miss_class;
81
+ bool cache_keys;
82
+ bool ignore_json_create;
83
+ } * Delegate;
84
+
85
+ static ID to_f_id = 0;
86
+ static ID ltlt_id = 0;
87
+ static ID hset_id = 0;
88
+
89
+ static char *str_dup(const char *s, size_t len) {
90
+ char *d = ALLOC_N(char, len + 1);
91
+
92
+ memcpy(d, s, len);
93
+ d[len] = '\0';
94
+
95
+ return d;
96
+ }
97
+
98
+ static VALUE form_str(const char *str, size_t len) {
99
+ return rb_str_freeze(rb_utf8_str_new(str, len));
100
+ }
101
+
102
+ static VALUE form_sym(const char *str, size_t len) {
103
+ // return ID2SYM(rb_intern3(str, len, oj_utf8_encoding));
104
+ return rb_str_intern(rb_utf8_str_new(str, len));
105
+ }
106
+
107
+ static VALUE form_attr(const char *str, size_t len) {
108
+ char buf[256];
109
+
110
+ if (sizeof(buf) - 2 <= len) {
111
+ char *b = ALLOC_N(char, len + 2);
112
+ ID id;
113
+
114
+ *b = '@';
115
+ memcpy(b + 1, str, len);
116
+ b[len + 1] = '\0';
117
+
118
+ id = rb_intern3(buf, len + 1, oj_utf8_encoding);
119
+ xfree(b);
120
+ return id;
121
+ }
122
+ *buf = '@';
123
+ memcpy(buf + 1, str, len);
124
+ buf[len + 1] = '\0';
125
+
126
+ return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
127
+ }
128
+
129
+ static VALUE resolve_classname(VALUE mod, const char *classname, bool auto_define) {
130
+ VALUE clas;
131
+ ID ci = rb_intern(classname);
132
+
133
+ if (rb_const_defined_at(mod, ci)) {
134
+ clas = rb_const_get_at(mod, ci);
135
+ } else if (auto_define) {
136
+ clas = rb_define_class_under(mod, classname, oj_bag_class);
137
+ } else {
138
+ clas = Qundef;
139
+ }
140
+ return clas;
141
+ }
142
+
143
+ static VALUE resolve_classpath(const char *name, size_t len, bool auto_define) {
144
+ char class_name[1024];
145
+ VALUE clas;
146
+ char * end = class_name + sizeof(class_name) - 1;
147
+ char * s;
148
+ const char *n = name;
149
+
150
+ clas = rb_cObject;
151
+ for (s = class_name; 0 < len; n++, len--) {
152
+ if (':' == *n) {
153
+ *s = '\0';
154
+ n++;
155
+ len--;
156
+ if (':' != *n) {
157
+ return Qundef;
158
+ }
159
+ if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
160
+ return Qundef;
161
+ }
162
+ s = class_name;
163
+ } else if (end <= s) {
164
+ return Qundef;
165
+ } else {
166
+ *s++ = *n;
167
+ }
168
+ }
169
+ *s = '\0';
170
+ return resolve_classname(clas, class_name, auto_define);
171
+ }
172
+
173
+ static VALUE form_class(const char *str, size_t len) {
174
+ return resolve_classpath(str, len, false);
175
+ }
176
+
177
+ static VALUE form_class_auto(const char *str, size_t len) {
178
+ return resolve_classpath(str, len, true);
179
+ }
180
+
181
+ static void assure_cstack(Delegate d) {
182
+ if (d->cend <= d->ctail + 1) {
183
+ size_t cap = d->cend - d->chead;
184
+ long pos = d->ctail - d->chead;
185
+
186
+ cap *= 2;
187
+ REALLOC_N(d->chead, struct _col, cap);
188
+ d->ctail = d->chead + pos;
189
+ d->cend = d->chead + cap;
190
+ }
191
+ }
192
+
193
+ static void push(ojParser p, VALUE v) {
194
+ Delegate d = (Delegate)p->ctx;
195
+
196
+ if (d->vend <= d->vtail) {
197
+ size_t cap = d->vend - d->vhead;
198
+ long pos = d->vtail - d->vhead;
199
+
200
+ cap *= 2;
201
+ REALLOC_N(d->vhead, VALUE, cap);
202
+ d->vtail = d->vhead + pos;
203
+ d->vend = d->vhead + cap;
204
+ }
205
+ *d->vtail = v;
206
+ d->vtail++;
207
+ }
208
+
209
+ static VALUE cache_key(ojParser p, Key kp) {
210
+ Delegate d = (Delegate)p->ctx;
211
+
212
+ if ((size_t)kp->len < sizeof(kp->buf) - 1) {
213
+ return cache_intern(d->key_cache, kp->buf, kp->len);
214
+ }
215
+ return cache_intern(d->key_cache, kp->key, kp->len);
216
+ }
217
+
218
+ static VALUE str_key(ojParser p, Key kp) {
219
+ if ((size_t)kp->len < sizeof(kp->buf) - 1) {
220
+ return rb_str_freeze(rb_utf8_str_new(kp->buf, kp->len));
221
+ }
222
+ return rb_str_freeze(rb_utf8_str_new(kp->key, kp->len));
223
+ }
224
+
225
+ static VALUE sym_key(ojParser p, Key kp) {
226
+ if ((size_t)kp->len < sizeof(kp->buf) - 1) {
227
+ return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->buf, kp->len)));
228
+ }
229
+ return rb_str_freeze(rb_str_intern(rb_utf8_str_new(kp->key, kp->len)));
230
+ }
231
+
232
+ static ID get_attr_id(ojParser p, Key kp) {
233
+ Delegate d = (Delegate)p->ctx;
234
+
235
+ if ((size_t)kp->len < sizeof(kp->buf) - 1) {
236
+ return (ID)cache_intern(d->attr_cache, kp->buf, kp->len);
237
+ }
238
+ return (ID)cache_intern(d->attr_cache, kp->key, kp->len);
239
+ }
240
+
241
+ static void push_key(ojParser p) {
242
+ Delegate d = (Delegate)p->ctx;
243
+ size_t klen = buf_len(&p->key);
244
+ const char *key = buf_str(&p->key);
245
+
246
+ if (d->kend <= d->ktail) {
247
+ size_t cap = d->kend - d->khead;
248
+ long pos = d->ktail - d->khead;
249
+
250
+ cap *= 2;
251
+ REALLOC_N(d->khead, union _key, cap);
252
+ d->ktail = d->khead + pos;
253
+ d->kend = d->khead + cap;
254
+ }
255
+ d->ktail->len = klen;
256
+ if (klen <= sizeof(d->ktail->buf) + 1) {
257
+ memcpy(d->ktail->buf, key, klen);
258
+ d->ktail->buf[klen] = '\0';
259
+ } else {
260
+ d->ktail->key = str_dup(key, klen);
261
+ }
262
+ d->ktail++;
263
+ }
264
+
265
+ static void push2(ojParser p, VALUE v) {
266
+ Delegate d = (Delegate)p->ctx;
267
+
268
+ if (d->vend <= d->vtail + 1) {
269
+ size_t cap = d->vend - d->vhead;
270
+ long pos = d->vtail - d->vhead;
271
+
272
+ cap *= 2;
273
+ REALLOC_N(d->vhead, VALUE, cap);
274
+ d->vtail = d->vhead + pos;
275
+ d->vend = d->vhead + cap;
276
+ }
277
+ *d->vtail = Qundef; // key place holder
278
+ d->vtail++;
279
+ *d->vtail = v;
280
+ d->vtail++;
281
+ }
282
+
283
+ static void open_object(ojParser p) {
284
+ Delegate d = (Delegate)p->ctx;
285
+
286
+ assure_cstack(d);
287
+ d->ctail->vi = d->vtail - d->vhead;
288
+ d->ctail->ki = d->ktail - d->khead;
289
+ d->ctail++;
290
+ push(p, Qundef);
291
+ }
292
+
293
+ static void open_object_key(ojParser p) {
294
+ Delegate d = (Delegate)p->ctx;
295
+
296
+ push_key(p);
297
+ assure_cstack(d);
298
+ d->ctail->vi = d->vtail - d->vhead + 1;
299
+ d->ctail->ki = d->ktail - d->khead;
300
+ d->ctail++;
301
+ push2(p, Qundef);
302
+ }
303
+
304
+ static void open_array(ojParser p) {
305
+ Delegate d = (Delegate)p->ctx;
306
+
307
+ assure_cstack(d);
308
+ d->ctail->vi = d->vtail - d->vhead;
309
+ d->ctail->ki = -1;
310
+ d->ctail++;
311
+ push(p, Qundef);
312
+ }
313
+
314
+ static void open_array_key(ojParser p) {
315
+ Delegate d = (Delegate)p->ctx;
316
+
317
+ push_key(p);
318
+ assure_cstack(d);
319
+ d->ctail->vi = d->vtail - d->vhead + 1;
320
+ d->ctail->ki = -1;
321
+ d->ctail++;
322
+ push2(p, Qundef);
323
+ }
324
+
325
+ static void close_object(ojParser p) {
326
+ VALUE *vp;
327
+ Delegate d = (Delegate)p->ctx;
328
+
329
+ d->ctail--;
330
+
331
+ Col c = d->ctail;
332
+ Key kp = d->khead + c->ki;
333
+ VALUE * head = d->vhead + c->vi + 1;
334
+ volatile VALUE obj = rb_hash_new();
335
+
336
+ #if HAVE_RB_HASH_BULK_INSERT
337
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
338
+ *vp = d->get_key(p, kp);
339
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
340
+ xfree(kp->key);
341
+ }
342
+ }
343
+ rb_hash_bulk_insert(d->vtail - head, head, obj);
344
+ #else
345
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
346
+ rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
347
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
348
+ xfree(kp->key);
349
+ }
350
+ }
351
+ #endif
352
+ d->ktail = d->khead + c->ki;
353
+ d->vtail = head;
354
+ head--;
355
+ *head = obj;
356
+ }
357
+
358
+ static void close_object_class(ojParser p) {
359
+ VALUE *vp;
360
+ Delegate d = (Delegate)p->ctx;
361
+
362
+ d->ctail--;
363
+
364
+ Col c = d->ctail;
365
+ Key kp = d->khead + c->ki;
366
+ VALUE * head = d->vhead + c->vi + 1;
367
+ volatile VALUE obj = rb_class_new_instance(0, NULL, d->hash_class);
368
+
369
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
370
+ rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
371
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
372
+ xfree(kp->key);
373
+ }
374
+ }
375
+ d->ktail = d->khead + c->ki;
376
+ d->vtail = head;
377
+ head--;
378
+ *head = obj;
379
+ }
380
+
381
+ static void close_object_create(ojParser p) {
382
+ VALUE *vp;
383
+ Delegate d = (Delegate)p->ctx;
384
+
385
+ d->ctail--;
386
+
387
+ Col c = d->ctail;
388
+ Key kp = d->khead + c->ki;
389
+ VALUE * head = d->vhead + c->vi;
390
+ volatile VALUE obj;
391
+
392
+ if (Qundef == *head) {
393
+ head++;
394
+ if (Qnil == d->hash_class) {
395
+ obj = rb_hash_new();
396
+ #if HAVE_RB_HASH_BULK_INSERT
397
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
398
+ *vp = d->get_key(p, kp);
399
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
400
+ xfree(kp->key);
401
+ }
402
+ }
403
+ rb_hash_bulk_insert(d->vtail - head, head, obj);
404
+ #else
405
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
406
+ rb_hash_aset(obj, d->get_key(p, kp), *(vp + 1));
407
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
408
+ xfree(kp->key);
409
+ }
410
+ }
411
+ #endif
412
+ } else {
413
+ obj = rb_class_new_instance(0, NULL, d->hash_class);
414
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
415
+ rb_funcall(obj, hset_id, 2, d->get_key(p, kp), *(vp + 1));
416
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
417
+ xfree(kp->key);
418
+ }
419
+ }
420
+ }
421
+ } else {
422
+ VALUE clas = *head;
423
+
424
+ head++;
425
+ if (!d->ignore_json_create && rb_respond_to(clas, oj_json_create_id)) {
426
+ volatile VALUE arg = rb_hash_new();
427
+
428
+ #if HAVE_RB_HASH_BULK_INSERT
429
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
430
+ *vp = d->get_key(p, kp);
431
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
432
+ xfree(kp->key);
433
+ }
434
+ }
435
+ rb_hash_bulk_insert(d->vtail - head, head, arg);
436
+ #else
437
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
438
+ rb_hash_aset(arg, d->get_key(p, kp), *(vp + 1));
439
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
440
+ xfree(kp->key);
441
+ }
442
+ }
443
+ #endif
444
+ obj = rb_funcall(clas, oj_json_create_id, 1, arg);
445
+ } else {
446
+ obj = rb_class_new_instance(0, NULL, clas);
447
+ for (vp = head; kp < d->ktail; kp++, vp += 2) {
448
+ rb_ivar_set(obj, get_attr_id(p, kp), *(vp + 1));
449
+ if (sizeof(kp->buf) - 1 < (size_t)kp->len) {
450
+ xfree(kp->key);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ d->ktail = d->khead + c->ki;
456
+ d->vtail = head;
457
+ head--;
458
+ *head = obj;
459
+ }
460
+
461
+ static void close_array(ojParser p) {
462
+ Delegate d = (Delegate)p->ctx;
463
+
464
+ d->ctail--;
465
+ VALUE * head = d->vhead + d->ctail->vi + 1;
466
+ volatile VALUE a = rb_ary_new_from_values(d->vtail - head, head);
467
+
468
+ d->vtail = head;
469
+ head--;
470
+ *head = a;
471
+ }
472
+
473
+ static void close_array_class(ojParser p) {
474
+ VALUE *vp;
475
+ Delegate d = (Delegate)p->ctx;
476
+
477
+ d->ctail--;
478
+ VALUE * head = d->vhead + d->ctail->vi + 1;
479
+ volatile VALUE a = rb_class_new_instance(0, NULL, d->array_class);
480
+
481
+ for (vp = head; vp < d->vtail; vp++) {
482
+ rb_funcall(a, ltlt_id, 1, *vp);
483
+ }
484
+ d->vtail = head;
485
+ head--;
486
+ *head = a;
487
+ }
488
+
489
+ static void noop(ojParser p) {
490
+ }
491
+
492
+ static void add_null(ojParser p) {
493
+ push(p, Qnil);
494
+ }
495
+
496
+ static void add_null_key(ojParser p) {
497
+ push_key(p);
498
+ push2(p, Qnil);
499
+ }
500
+
501
+ static void add_true(ojParser p) {
502
+ push(p, Qtrue);
503
+ }
504
+
505
+ static void add_true_key(ojParser p) {
506
+ push_key(p);
507
+ push2(p, Qtrue);
508
+ }
509
+
510
+ static void add_false(ojParser p) {
511
+ push(p, Qfalse);
512
+ }
513
+
514
+ static void add_false_key(ojParser p) {
515
+ push_key(p);
516
+ push2(p, Qfalse);
517
+ }
518
+
519
+ static void add_int(ojParser p) {
520
+ push(p, LONG2NUM(p->num.fixnum));
521
+ }
522
+
523
+ static void add_int_key(ojParser p) {
524
+ push_key(p);
525
+ push2(p, LONG2NUM(p->num.fixnum));
526
+ }
527
+
528
+ static void add_float(ojParser p) {
529
+ push(p, rb_float_new(p->num.dub));
530
+ }
531
+
532
+ static void add_float_key(ojParser p) {
533
+ push_key(p);
534
+ push2(p, rb_float_new(p->num.dub));
535
+ }
536
+
537
+ static void add_float_as_big(ojParser p) {
538
+ char buf[64];
539
+
540
+ // fails on ubuntu
541
+ // snprintf(buf, sizeof(buf), "%Lg", p->num.dub);
542
+ sprintf(buf, "%Lg", p->num.dub);
543
+ push(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(buf)));
544
+ }
545
+
546
+ static void add_float_as_big_key(ojParser p) {
547
+ char buf[64];
548
+
549
+ snprintf(buf, sizeof(buf), "%Lg", p->num.dub);
550
+ push_key(p);
551
+ push2(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(buf)));
552
+ }
553
+
554
+ static void add_big(ojParser p) {
555
+ push(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))));
556
+ }
557
+
558
+ static void add_big_key(ojParser p) {
559
+ push_key(p);
560
+ push2(p, rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))));
561
+ }
562
+
563
+ static void add_big_as_float(ojParser p) {
564
+ volatile VALUE big = rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf)));
565
+
566
+ push(p, rb_funcall(big, to_f_id, 0));
567
+ }
568
+
569
+ static void add_big_as_float_key(ojParser p) {
570
+ volatile VALUE big = rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf)));
571
+
572
+ push_key(p);
573
+ push2(p, rb_funcall(big, to_f_id, 0));
574
+ }
575
+
576
+ static void add_big_as_ruby(ojParser p) {
577
+ push(p, rb_funcall(rb_str_new(buf_str(&p->buf), buf_len(&p->buf)), to_f_id, 0));
578
+ }
579
+
580
+ static void add_big_as_ruby_key(ojParser p) {
581
+ push_key(p);
582
+ push2(p, rb_funcall(rb_str_new(buf_str(&p->buf), buf_len(&p->buf)), to_f_id, 0));
583
+ }
584
+
585
+ static void add_str(ojParser p) {
586
+ Delegate d = (Delegate)p->ctx;
587
+ volatile VALUE rstr;
588
+ const char * str = buf_str(&p->buf);
589
+ size_t len = buf_len(&p->buf);
590
+
591
+ if (len < d->cache_str) {
592
+ rstr = cache_intern(d->str_cache, str, len);
593
+ } else {
594
+ rstr = rb_utf8_str_new(str, len);
595
+ }
596
+ push(p, rstr);
597
+ }
598
+
599
+ static void add_str_key(ojParser p) {
600
+ Delegate d = (Delegate)p->ctx;
601
+ volatile VALUE rstr;
602
+ const char * str = buf_str(&p->buf);
603
+ size_t len = buf_len(&p->buf);
604
+
605
+ if (len < d->cache_str) {
606
+ rstr = cache_intern(d->str_cache, str, len);
607
+ } else {
608
+ rstr = rb_utf8_str_new(str, len);
609
+ }
610
+ push_key(p);
611
+ push2(p, rstr);
612
+ }
613
+
614
+ static void add_str_key_create(ojParser p) {
615
+ Delegate d = (Delegate)p->ctx;
616
+ volatile VALUE rstr;
617
+ const char * str = buf_str(&p->buf);
618
+ size_t len = buf_len(&p->buf);
619
+ const char * key = buf_str(&p->key);
620
+ size_t klen = buf_len(&p->key);
621
+
622
+ if (klen == (size_t)d->create_id_len && 0 == strncmp(d->create_id, key, klen)) {
623
+ Col c = d->ctail - 1;
624
+ VALUE clas;
625
+
626
+ if (NULL != d->class_cache) {
627
+ clas = cache_intern(d->class_cache, str, len);
628
+ } else {
629
+ clas = resolve_classpath(str, len, MISS_AUTO == d->miss_class);
630
+ }
631
+ if (Qundef != clas) {
632
+ *(d->vhead + c->vi) = clas;
633
+ return;
634
+ }
635
+ if (MISS_RAISE == d->miss_class) {
636
+ rb_raise(rb_eLoadError, "%s is not define", str);
637
+ }
638
+ }
639
+ if (len < d->cache_str) {
640
+ rstr = cache_intern(d->str_cache, str, len);
641
+ } else {
642
+ rstr = rb_utf8_str_new(str, len);
643
+ }
644
+ push_key(p);
645
+ push2(p, rstr);
646
+ }
647
+
648
+ static VALUE result(ojParser p) {
649
+ Delegate d = (Delegate)p->ctx;
650
+
651
+ if (d->vhead < d->vtail) {
652
+ return *d->vhead;
653
+ }
654
+ return Qnil;
655
+ }
656
+
657
+ static void start(ojParser p) {
658
+ Delegate d = (Delegate)p->ctx;
659
+
660
+ d->vtail = d->vhead;
661
+ d->ctail = d->chead;
662
+ d->ktail = d->khead;
663
+ }
664
+
665
+ static void dfree(ojParser p) {
666
+ Delegate d = (Delegate)p->ctx;
667
+
668
+ cache_free(d->str_cache);
669
+ cache_free(d->attr_cache);
670
+ if (NULL != d->sym_cache) {
671
+ cache_free(d->sym_cache);
672
+ }
673
+ if (NULL != d->class_cache) {
674
+ cache_free(d->class_cache);
675
+ }
676
+ xfree(d->vhead);
677
+ xfree(d->chead);
678
+ xfree(d->khead);
679
+ xfree(d->create_id);
680
+ xfree(p->ctx);
681
+ p->ctx = NULL;
682
+ }
683
+
684
+ static void mark(ojParser p) {
685
+ if (NULL == p->ctx) {
686
+ return;
687
+ }
688
+ Delegate d = (Delegate)p->ctx;
689
+ VALUE *vp;
690
+
691
+ if (NULL == d) {
692
+ return;
693
+ }
694
+ cache_mark(d->str_cache);
695
+ if (NULL != d->sym_cache) {
696
+ cache_mark(d->sym_cache);
697
+ }
698
+ if (NULL != d->class_cache) {
699
+ cache_mark(d->class_cache);
700
+ }
701
+ for (vp = d->vhead; vp < d->vtail; vp++) {
702
+ if (Qundef != *vp) {
703
+ rb_gc_mark(*vp);
704
+ }
705
+ }
706
+ }
707
+
708
+ ///// options /////////////////////////////////////////////////////////////////
709
+
710
+ // Each option is handled by a separate function and then added to an assoc
711
+ // list (struct opt}. The list is then iterated over until there is a name
712
+ // match. This is done primarily to keep each option separate and easier to
713
+ // understand instead of placing all in one large function.
714
+
715
+ struct opt {
716
+ const char *name;
717
+ VALUE (*func)(ojParser p, VALUE value);
718
+ };
719
+
720
+ static VALUE opt_array_class(ojParser p, VALUE value) {
721
+ Delegate d = (Delegate)p->ctx;
722
+
723
+ return d->array_class;
724
+ }
725
+
726
+ static VALUE opt_array_class_set(ojParser p, VALUE value) {
727
+ Delegate d = (Delegate)p->ctx;
728
+
729
+ if (Qnil == value) {
730
+ p->funcs[TOP_FUN].close_array = close_array;
731
+ p->funcs[ARRAY_FUN].close_array = close_array;
732
+ p->funcs[OBJECT_FUN].close_array = close_array;
733
+ } else {
734
+ rb_check_type(value, T_CLASS);
735
+ if (!rb_method_boundp(value, ltlt_id, 1)) {
736
+ rb_raise(rb_eArgError, "An array class must implement the << method.");
737
+ }
738
+ p->funcs[TOP_FUN].close_array = close_array_class;
739
+ p->funcs[ARRAY_FUN].close_array = close_array_class;
740
+ p->funcs[OBJECT_FUN].close_array = close_array_class;
741
+ }
742
+ d->array_class = value;
743
+
744
+ return d->array_class;
745
+ }
746
+
747
+ static VALUE opt_cache_keys(ojParser p, VALUE value) {
748
+ Delegate d = (Delegate)p->ctx;
749
+
750
+ return d->cache_keys ? Qtrue : Qfalse;
751
+ }
752
+
753
+ static VALUE opt_cache_keys_set(ojParser p, VALUE value) {
754
+ Delegate d = (Delegate)p->ctx;
755
+
756
+ if (Qtrue == value) {
757
+ d->cache_keys = true;
758
+ d->get_key = cache_key;
759
+ if (NULL == d->sym_cache) {
760
+ d->key_cache = d->str_cache;
761
+ } else {
762
+ d->key_cache = d->sym_cache;
763
+ }
764
+ } else {
765
+ d->cache_keys = false;
766
+ if (NULL == d->sym_cache) {
767
+ d->get_key = str_key;
768
+ } else {
769
+ d->get_key = sym_key;
770
+ }
771
+ }
772
+ return d->cache_keys ? Qtrue : Qfalse;
773
+ }
774
+
775
+ static VALUE opt_cache_strings(ojParser p, VALUE value) {
776
+ Delegate d = (Delegate)p->ctx;
777
+
778
+ return INT2NUM((int)d->cache_str);
779
+ }
780
+
781
+ static VALUE opt_cache_strings_set(ojParser p, VALUE value) {
782
+ Delegate d = (Delegate)p->ctx;
783
+ int limit = NUM2INT(value);
784
+
785
+ if (CACHE_MAX_KEY < limit) {
786
+ limit = CACHE_MAX_KEY;
787
+ } else if (limit < 0) {
788
+ limit = 0;
789
+ }
790
+ d->cache_str = limit;
791
+
792
+ return INT2NUM((int)d->cache_str);
793
+ }
794
+
795
+ static VALUE opt_capacity(ojParser p, VALUE value) {
796
+ Delegate d = (Delegate)p->ctx;
797
+
798
+ return ULONG2NUM(d->vend - d->vhead);
799
+ }
800
+
801
+ static VALUE opt_capacity_set(ojParser p, VALUE value) {
802
+ Delegate d = (Delegate)p->ctx;
803
+ long cap = NUM2LONG(value);
804
+
805
+ if (d->vend - d->vhead < cap) {
806
+ long pos = d->vtail - d->vhead;
807
+
808
+ REALLOC_N(d->vhead, VALUE, cap);
809
+ d->vtail = d->vhead + pos;
810
+ d->vend = d->vhead + cap;
811
+ }
812
+ if (d->kend - d->khead < cap) {
813
+ long pos = d->ktail - d->khead;
814
+
815
+ REALLOC_N(d->khead, union _key, cap);
816
+ d->ktail = d->khead + pos;
817
+ d->kend = d->khead + cap;
818
+ }
819
+ return ULONG2NUM(d->vend - d->vhead);
820
+ }
821
+
822
+ static VALUE opt_class_cache(ojParser p, VALUE value) {
823
+ Delegate d = (Delegate)p->ctx;
824
+
825
+ return (NULL != d->class_cache) ? Qtrue : Qfalse;
826
+ }
827
+
828
+ static VALUE opt_class_cache_set(ojParser p, VALUE value) {
829
+ Delegate d = (Delegate)p->ctx;
830
+
831
+ if (Qtrue == value) {
832
+ if (NULL == d->class_cache) {
833
+ if (MISS_AUTO == d->miss_class) {
834
+ d->class_cache = cache_create(0, form_class_auto, true);
835
+ } else {
836
+ d->class_cache = cache_create(0, form_class, false);
837
+ }
838
+ }
839
+ } else if (NULL != d->class_cache) {
840
+ cache_free(d->class_cache);
841
+ d->class_cache = NULL;
842
+ }
843
+ return (NULL != d->class_cache) ? Qtrue : Qfalse;
844
+ }
845
+
846
+ static VALUE opt_create_id(ojParser p, VALUE value) {
847
+ Delegate d = (Delegate)p->ctx;
848
+
849
+ if (NULL == d->create_id) {
850
+ return Qnil;
851
+ }
852
+ return rb_utf8_str_new(d->create_id, d->create_id_len);
853
+ }
854
+
855
+ static VALUE opt_create_id_set(ojParser p, VALUE value) {
856
+ Delegate d = (Delegate)p->ctx;
857
+
858
+ if (Qnil == value) {
859
+ d->create_id = NULL;
860
+ d->create_id_len = 0;
861
+ p->funcs[OBJECT_FUN].add_str = add_str_key;
862
+ if (Qnil == d->hash_class) {
863
+ p->funcs[TOP_FUN].close_object = close_object;
864
+ p->funcs[ARRAY_FUN].close_object = close_object;
865
+ p->funcs[OBJECT_FUN].close_object = close_object;
866
+ } else {
867
+ p->funcs[TOP_FUN].close_object = close_object_class;
868
+ p->funcs[ARRAY_FUN].close_object = close_object_class;
869
+ p->funcs[OBJECT_FUN].close_object = close_object_class;
870
+ }
871
+ } else {
872
+ rb_check_type(value, T_STRING);
873
+ size_t len = RSTRING_LEN(value);
874
+
875
+ if (1 << sizeof(d->create_id_len) <= len) {
876
+ rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << sizeof(d->create_id_len));
877
+ }
878
+ d->create_id_len = (uint8_t)len;
879
+ d->create_id = str_dup(RSTRING_PTR(value), len);
880
+ p->funcs[OBJECT_FUN].add_str = add_str_key_create;
881
+ p->funcs[TOP_FUN].close_object = close_object_create;
882
+ p->funcs[ARRAY_FUN].close_object = close_object_create;
883
+ p->funcs[OBJECT_FUN].close_object = close_object_create;
884
+ }
885
+ return opt_create_id(p, value);
886
+ }
887
+
888
+ static VALUE opt_decimal(ojParser p, VALUE value) {
889
+ if (add_float_as_big == p->funcs[TOP_FUN].add_float) {
890
+ return ID2SYM(rb_intern("bigdecimal"));
891
+ }
892
+ if (add_big == p->funcs[TOP_FUN].add_big) {
893
+ return ID2SYM(rb_intern("auto"));
894
+ }
895
+ if (add_big_as_float == p->funcs[TOP_FUN].add_big) {
896
+ return ID2SYM(rb_intern("float"));
897
+ }
898
+ if (add_big_as_ruby == p->funcs[TOP_FUN].add_big) {
899
+ return ID2SYM(rb_intern("ruby"));
900
+ }
901
+ return Qnil;
902
+ }
903
+
904
+ static VALUE opt_decimal_set(ojParser p, VALUE value) {
905
+ const char * mode;
906
+ volatile VALUE s;
907
+
908
+ switch (rb_type(value)) {
909
+ case T_STRING: mode = RSTRING_PTR(value); break;
910
+ case T_SYMBOL:
911
+ s = rb_sym2str(value);
912
+ mode = RSTRING_PTR(s);
913
+ break;
914
+ default:
915
+ rb_raise(rb_eTypeError,
916
+ "the decimal options must be a Symbol or String, not %s.",
917
+ rb_class2name(rb_obj_class(value)));
918
+ break;
919
+ }
920
+ if (0 == strcmp("auto", mode)) {
921
+ p->funcs[TOP_FUN].add_big = add_big;
922
+ p->funcs[ARRAY_FUN].add_big = add_big;
923
+ p->funcs[OBJECT_FUN].add_big = add_big_key;
924
+ p->funcs[TOP_FUN].add_float = add_float;
925
+ p->funcs[ARRAY_FUN].add_float = add_float;
926
+ p->funcs[OBJECT_FUN].add_float = add_float_key;
927
+
928
+ return opt_decimal(p, Qnil);
929
+ }
930
+ if (0 == strcmp("bigdecimal", mode)) {
931
+ p->funcs[TOP_FUN].add_big = add_big;
932
+ p->funcs[ARRAY_FUN].add_big = add_big;
933
+ p->funcs[OBJECT_FUN].add_big = add_big_key;
934
+ p->funcs[TOP_FUN].add_float = add_float_as_big;
935
+ p->funcs[ARRAY_FUN].add_float = add_float_as_big;
936
+ p->funcs[OBJECT_FUN].add_float = add_float_as_big_key;
937
+
938
+ return opt_decimal(p, Qnil);
939
+ }
940
+ if (0 == strcmp("float", mode)) {
941
+ p->funcs[TOP_FUN].add_big = add_big_as_float;
942
+ p->funcs[ARRAY_FUN].add_big = add_big_as_float;
943
+ p->funcs[OBJECT_FUN].add_big = add_big_as_float_key;
944
+ p->funcs[TOP_FUN].add_float = add_float;
945
+ p->funcs[ARRAY_FUN].add_float = add_float;
946
+ p->funcs[OBJECT_FUN].add_float = add_float_key;
947
+
948
+ return opt_decimal(p, Qnil);
949
+ }
950
+ if (0 == strcmp("ruby", mode)) {
951
+ p->funcs[TOP_FUN].add_big = add_big_as_ruby;
952
+ p->funcs[ARRAY_FUN].add_big = add_big_as_ruby;
953
+ p->funcs[OBJECT_FUN].add_big = add_big_as_ruby_key;
954
+ p->funcs[TOP_FUN].add_float = add_float;
955
+ p->funcs[ARRAY_FUN].add_float = add_float;
956
+ p->funcs[OBJECT_FUN].add_float = add_float_key;
957
+
958
+ return opt_decimal(p, Qnil);
959
+ }
960
+ rb_raise(rb_eArgError, "%s is not a valid option for the decimal option.", mode);
961
+
962
+ return Qnil;
963
+ }
964
+
965
+ static VALUE opt_hash_class(ojParser p, VALUE value) {
966
+ Delegate d = (Delegate)p->ctx;
967
+
968
+ return d->hash_class;
969
+ }
970
+
971
+ static VALUE opt_hash_class_set(ojParser p, VALUE value) {
972
+ Delegate d = (Delegate)p->ctx;
973
+
974
+ if (Qnil != value) {
975
+ rb_check_type(value, T_CLASS);
976
+ if (!rb_method_boundp(value, hset_id, 1)) {
977
+ rb_raise(rb_eArgError, "A hash class must implement the []= method.");
978
+ }
979
+ }
980
+ d->hash_class = value;
981
+ if (NULL == d->create_id) {
982
+ if (Qnil == value) {
983
+ p->funcs[TOP_FUN].close_object = close_object;
984
+ p->funcs[ARRAY_FUN].close_object = close_object;
985
+ p->funcs[OBJECT_FUN].close_object = close_object;
986
+ } else {
987
+ p->funcs[TOP_FUN].close_object = close_object_class;
988
+ p->funcs[ARRAY_FUN].close_object = close_object_class;
989
+ p->funcs[OBJECT_FUN].close_object = close_object_class;
990
+ }
991
+ }
992
+ return d->hash_class;
993
+ }
994
+
995
+ static VALUE opt_ignore_json_create(ojParser p, VALUE value) {
996
+ Delegate d = (Delegate)p->ctx;
997
+
998
+ return d->ignore_json_create ? Qtrue : Qfalse;
999
+ }
1000
+
1001
+ static VALUE opt_ignore_json_create_set(ojParser p, VALUE value) {
1002
+ Delegate d = (Delegate)p->ctx;
1003
+
1004
+ d->ignore_json_create = (Qtrue == value);
1005
+
1006
+ return d->ignore_json_create ? Qtrue : Qfalse;
1007
+ }
1008
+
1009
+ static VALUE opt_missing_class(ojParser p, VALUE value) {
1010
+ Delegate d = (Delegate)p->ctx;
1011
+
1012
+ switch (d->miss_class) {
1013
+ case MISS_AUTO: return ID2SYM(rb_intern("auto"));
1014
+ case MISS_RAISE: return ID2SYM(rb_intern("raise"));
1015
+ case MISS_IGNORE:
1016
+ default: return ID2SYM(rb_intern("ignore"));
1017
+ }
1018
+ }
1019
+
1020
+ static VALUE opt_missing_class_set(ojParser p, VALUE value) {
1021
+ Delegate d = (Delegate)p->ctx;
1022
+ const char * mode;
1023
+ volatile VALUE s;
1024
+
1025
+ switch (rb_type(value)) {
1026
+ case T_STRING: mode = RSTRING_PTR(value); break;
1027
+ case T_SYMBOL:
1028
+ s = rb_sym2str(value);
1029
+ mode = RSTRING_PTR(s);
1030
+ break;
1031
+ default:
1032
+ rb_raise(rb_eTypeError,
1033
+ "the missing_class options must be a Symbol or String, not %s.",
1034
+ rb_class2name(rb_obj_class(value)));
1035
+ break;
1036
+ }
1037
+ if (0 == strcmp("auto", mode)) {
1038
+ d->miss_class = MISS_AUTO;
1039
+ if (NULL != d->class_cache) {
1040
+ cache_set_form(d->class_cache, form_class_auto);
1041
+ }
1042
+ } else if (0 == strcmp("ignore", mode)) {
1043
+ d->miss_class = MISS_IGNORE;
1044
+ if (NULL != d->class_cache) {
1045
+ cache_set_form(d->class_cache, form_class);
1046
+ }
1047
+ } else if (0 == strcmp("raise", mode)) {
1048
+ d->miss_class = MISS_RAISE;
1049
+ if (NULL != d->class_cache) {
1050
+ cache_set_form(d->class_cache, form_class);
1051
+ }
1052
+ } else {
1053
+ rb_raise(rb_eArgError, "%s is not a valid value for the missing_class option.", mode);
1054
+ }
1055
+ return opt_missing_class(p, value);
1056
+ }
1057
+
1058
+ static VALUE opt_omit_null(ojParser p, VALUE value) {
1059
+ return (noop == p->funcs[OBJECT_FUN].add_null) ? Qtrue : Qfalse;
1060
+ }
1061
+
1062
+ static VALUE opt_omit_null_set(ojParser p, VALUE value) {
1063
+ if (Qtrue == value) {
1064
+ p->funcs[OBJECT_FUN].add_null = noop;
1065
+ } else {
1066
+ p->funcs[OBJECT_FUN].add_null = add_null_key;
1067
+ }
1068
+ return (noop == p->funcs[OBJECT_FUN].add_null) ? Qtrue : Qfalse;
1069
+ }
1070
+
1071
+ static VALUE opt_symbol_keys(ojParser p, VALUE value) {
1072
+ Delegate d = (Delegate)p->ctx;
1073
+
1074
+ return (NULL != d->sym_cache) ? Qtrue : Qfalse;
1075
+ }
1076
+
1077
+ static VALUE opt_symbol_keys_set(ojParser p, VALUE value) {
1078
+ Delegate d = (Delegate)p->ctx;
1079
+
1080
+ if (Qtrue == value) {
1081
+ d->sym_cache = cache_create(0, form_sym, true);
1082
+ d->key_cache = d->sym_cache;
1083
+ if (!d->cache_keys) {
1084
+ d->get_key = sym_key;
1085
+ }
1086
+ } else {
1087
+ if (NULL != d->sym_cache) {
1088
+ cache_free(d->sym_cache);
1089
+ d->sym_cache = NULL;
1090
+ }
1091
+ if (!d->cache_keys) {
1092
+ d->get_key = str_key;
1093
+ }
1094
+ }
1095
+ return (NULL != d->sym_cache) ? Qtrue : Qfalse;
1096
+ }
1097
+
1098
+ static VALUE option(ojParser p, const char *key, VALUE value) {
1099
+ struct opt *op;
1100
+ struct opt opts[] = {
1101
+ {.name = "array_class", .func = opt_array_class},
1102
+ {.name = "array_class=", .func = opt_array_class_set},
1103
+ {.name = "cache_keys", .func = opt_cache_keys},
1104
+ {.name = "cache_keys=", .func = opt_cache_keys_set},
1105
+ {.name = "cache_strings", .func = opt_cache_strings},
1106
+ {.name = "cache_strings=", .func = opt_cache_strings_set},
1107
+ {.name = "capacity", .func = opt_capacity},
1108
+ {.name = "capacity=", .func = opt_capacity_set},
1109
+ {.name = "class_cache", .func = opt_class_cache},
1110
+ {.name = "class_cache=", .func = opt_class_cache_set},
1111
+ {.name = "create_id", .func = opt_create_id},
1112
+ {.name = "create_id=", .func = opt_create_id_set},
1113
+ {.name = "decimal", .func = opt_decimal},
1114
+ {.name = "decimal=", .func = opt_decimal_set},
1115
+ {.name = "hash_class", .func = opt_hash_class},
1116
+ {.name = "hash_class=", .func = opt_hash_class_set},
1117
+ {.name = "ignore_json_create", .func = opt_ignore_json_create},
1118
+ {.name = "ignore_json_create=", .func = opt_ignore_json_create_set},
1119
+ {.name = "missing_class", .func = opt_missing_class},
1120
+ {.name = "missing_class=", .func = opt_missing_class_set},
1121
+ {.name = "omit_null", .func = opt_omit_null},
1122
+ {.name = "omit_null=", .func = opt_omit_null_set},
1123
+ {.name = "symbol_keys", .func = opt_symbol_keys},
1124
+ {.name = "symbol_keys=", .func = opt_symbol_keys_set},
1125
+ {.name = NULL},
1126
+ };
1127
+
1128
+ for (op = opts; NULL != op->name; op++) {
1129
+ if (0 == strcmp(key, op->name)) {
1130
+ return op->func(p, value);
1131
+ }
1132
+ }
1133
+ rb_raise(rb_eArgError, "%s is not an option for the Usual delegate", key);
1134
+
1135
+ return Qnil; // Never reached due to the raise but required by the compiler.
1136
+ }
1137
+
1138
+ ///// the set up //////////////////////////////////////////////////////////////
1139
+
1140
+ void oj_set_parser_usual(ojParser p) {
1141
+ Delegate d = ALLOC(struct _delegate);
1142
+ int cap = 4096;
1143
+
1144
+ d->vhead = ALLOC_N(VALUE, cap);
1145
+ d->vend = d->vhead + cap;
1146
+ d->vtail = d->vhead;
1147
+
1148
+ d->khead = ALLOC_N(union _key, cap);
1149
+ d->kend = d->khead + cap;
1150
+ d->ktail = d->khead;
1151
+
1152
+ cap = 256;
1153
+ d->chead = ALLOC_N(struct _col, cap);
1154
+ d->cend = d->chead + cap;
1155
+ d->ctail = d->chead;
1156
+
1157
+ d->get_key = cache_key;
1158
+ d->cache_keys = true;
1159
+ d->ignore_json_create = false;
1160
+ d->cache_str = 6;
1161
+ d->array_class = Qnil;
1162
+ d->hash_class = Qnil;
1163
+ d->create_id = NULL;
1164
+ d->create_id_len = 0;
1165
+ d->miss_class = MISS_IGNORE;
1166
+
1167
+ Funcs f = &p->funcs[TOP_FUN];
1168
+ f->add_null = add_null;
1169
+ f->add_true = add_true;
1170
+ f->add_false = add_false;
1171
+ f->add_int = add_int;
1172
+ f->add_float = add_float;
1173
+ f->add_big = add_big;
1174
+ f->add_str = add_str;
1175
+ f->open_array = open_array;
1176
+ f->close_array = close_array;
1177
+ f->open_object = open_object;
1178
+ f->close_object = close_object;
1179
+
1180
+ f = &p->funcs[ARRAY_FUN];
1181
+ f->add_null = add_null;
1182
+ f->add_true = add_true;
1183
+ f->add_false = add_false;
1184
+ f->add_int = add_int;
1185
+ f->add_float = add_float;
1186
+ f->add_big = add_big;
1187
+ f->add_str = add_str;
1188
+ f->open_array = open_array;
1189
+ f->close_array = close_array;
1190
+ f->open_object = open_object;
1191
+ f->close_object = close_object;
1192
+
1193
+ f = &p->funcs[OBJECT_FUN];
1194
+ f->add_null = add_null_key;
1195
+ f->add_true = add_true_key;
1196
+ f->add_false = add_false_key;
1197
+ f->add_int = add_int_key;
1198
+ f->add_float = add_float_key;
1199
+ f->add_big = add_big_key;
1200
+ f->add_str = add_str_key;
1201
+ f->open_array = open_array_key;
1202
+ f->close_array = close_array;
1203
+ f->open_object = open_object_key;
1204
+ f->close_object = close_object;
1205
+
1206
+ d->str_cache = cache_create(0, form_str, true);
1207
+ d->attr_cache = cache_create(0, form_attr, false);
1208
+ d->sym_cache = NULL;
1209
+ d->class_cache = NULL;
1210
+ d->key_cache = d->str_cache;
1211
+
1212
+ p->ctx = (void *)d;
1213
+ p->option = option;
1214
+ p->result = result;
1215
+ p->free = dfree;
1216
+ p->mark = mark;
1217
+ p->start = start;
1218
+
1219
+ if (0 == to_f_id) {
1220
+ to_f_id = rb_intern("to_f");
1221
+ }
1222
+ if (0 == ltlt_id) {
1223
+ ltlt_id = rb_intern("<<");
1224
+ }
1225
+ if (0 == hset_id) {
1226
+ hset_id = rb_intern("[]=");
1227
+ }
1228
+ }