oj 2.0.0 → 3.0.0

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.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/object.c ADDED
@@ -0,0 +1,752 @@
1
+ /* object.c
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdio.h>
7
+ #include <time.h>
8
+
9
+ #include "oj.h"
10
+ #include "err.h"
11
+ #include "parse.h"
12
+ #include "resolve.h"
13
+ #include "hash.h"
14
+ #include "odd.h"
15
+ #include "encode.h"
16
+
17
+ inline static long
18
+ read_long(const char *str, size_t len) {
19
+ long n = 0;
20
+
21
+ for (; 0 < len; str++, len--) {
22
+ if ('0' <= *str && *str <= '9') {
23
+ n = n * 10 + (*str - '0');
24
+ } else {
25
+ return -1;
26
+ }
27
+ }
28
+ return n;
29
+ }
30
+
31
+ static VALUE
32
+ calc_hash_key(ParseInfo pi, Val kval, char k1) {
33
+ volatile VALUE rkey;
34
+
35
+ if (':' == k1) {
36
+ rkey = rb_str_new(kval->key + 1, kval->klen - 1);
37
+ rkey = oj_encode(rkey);
38
+ rkey = rb_funcall(rkey, oj_to_sym_id, 0);
39
+ } else {
40
+ rkey = rb_str_new(kval->key, kval->klen);
41
+ rkey = oj_encode(rkey);
42
+ if (Yes == pi->options.sym_key) {
43
+ rkey = rb_str_intern(rkey);
44
+ }
45
+ }
46
+ return rkey;
47
+ }
48
+
49
+ static VALUE
50
+ str_to_value(ParseInfo pi, const char *str, size_t len, const char *orig) {
51
+ volatile VALUE rstr = Qnil;
52
+
53
+ if (':' == *orig && 0 < len) {
54
+ rstr = rb_str_new(str + 1, len - 1);
55
+ rstr = oj_encode(rstr);
56
+ rstr = rb_funcall(rstr, oj_to_sym_id, 0);
57
+ } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
58
+ long i = read_long(str + 2, len - 2);
59
+
60
+ if (0 > i) {
61
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a valid ID number");
62
+ return Qnil;
63
+ }
64
+ rstr = oj_circ_array_get(pi->circ_array, i);
65
+ } else {
66
+ rstr = rb_str_new(str, len);
67
+ rstr = oj_encode(rstr);
68
+ }
69
+ return rstr;
70
+ }
71
+
72
+ #if (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
73
+ static VALUE
74
+ oj_parse_xml_time(const char *str, int len) {
75
+ return rb_funcall(rb_cTime, oj_parse_id, 1, rb_str_new(str, len));
76
+ }
77
+ #else
78
+ // The much faster approach (4x faster)
79
+ static int
80
+ parse_num(const char *str, const char *end, int cnt) {
81
+ int n = 0;
82
+ char c;
83
+ int i;
84
+
85
+ for (i = cnt; 0 < i; i--, str++) {
86
+ c = *str;
87
+ if (end <= str || c < '0' || '9' < c) {
88
+ return -1;
89
+ }
90
+ n = n * 10 + (c - '0');
91
+ }
92
+ return n;
93
+ }
94
+
95
+ VALUE
96
+ oj_parse_xml_time(const char *str, int len) {
97
+ VALUE args[8];
98
+ const char *end = str + len;
99
+ int n;
100
+
101
+ // year
102
+ if (0 > (n = parse_num(str, end, 4))) {
103
+ return Qnil;
104
+ }
105
+ str += 4;
106
+ args[0] = LONG2NUM(n);
107
+ if ('-' != *str++) {
108
+ return Qnil;
109
+ }
110
+ // month
111
+ if (0 > (n = parse_num(str, end, 2))) {
112
+ return Qnil;
113
+ }
114
+ str += 2;
115
+ args[1] = LONG2NUM(n);
116
+ if ('-' != *str++) {
117
+ return Qnil;
118
+ }
119
+ // day
120
+ if (0 > (n = parse_num(str, end, 2))) {
121
+ return Qnil;
122
+ }
123
+ str += 2;
124
+ args[2] = LONG2NUM(n);
125
+ if ('T' != *str++) {
126
+ return Qnil;
127
+ }
128
+ // hour
129
+ if (0 > (n = parse_num(str, end, 2))) {
130
+ return Qnil;
131
+ }
132
+ str += 2;
133
+ args[3] = LONG2NUM(n);
134
+ if (':' != *str++) {
135
+ return Qnil;
136
+ }
137
+ // minute
138
+ if (0 > (n = parse_num(str, end, 2))) {
139
+ return Qnil;
140
+ }
141
+ str += 2;
142
+ args[4] = LONG2NUM(n);
143
+ if (':' != *str++) {
144
+ return Qnil;
145
+ }
146
+ // second
147
+ if (0 > (n = parse_num(str, end, 2))) {
148
+ return Qnil;
149
+ }
150
+ str += 2;
151
+ if (str == end) {
152
+ args[5] = LONG2NUM(n);
153
+ args[6] = LONG2NUM(0);
154
+ } else {
155
+ char c = *str++;
156
+
157
+ if ('.' == c) {
158
+ long long nsec = 0;
159
+
160
+ for (; str < end; str++) {
161
+ c = *str;
162
+ if (c < '0' || '9' < c) {
163
+ str++;
164
+ break;
165
+ }
166
+ nsec = nsec * 10 + (c - '0');
167
+ }
168
+ args[5] = rb_float_new((double)n + ((double)nsec + 0.5) / 1000000000.0);
169
+ } else {
170
+ args[5] = rb_ll2inum(n);
171
+ }
172
+ if (end < str) {
173
+ args[6] = LONG2NUM(0);
174
+ } else {
175
+ if ('Z' == c) {
176
+ return rb_funcall2(rb_cTime, oj_utc_id, 6, args);
177
+ } else if ('+' == c) {
178
+ int hr = parse_num(str, end, 2);
179
+ int min;
180
+
181
+ str += 2;
182
+ if (0 > hr || ':' != *str++) {
183
+ return Qnil;
184
+ }
185
+ min = parse_num(str, end, 2);
186
+ if (0 > min) {
187
+ return Qnil;
188
+ }
189
+ args[6] = LONG2NUM(hr * 3600 + min * 60);
190
+ } else if ('-' == c) {
191
+ int hr = parse_num(str, end, 2);
192
+ int min;
193
+
194
+ str += 2;
195
+ if (0 > hr || ':' != *str++) {
196
+ return Qnil;
197
+ }
198
+ min = parse_num(str, end, 2);
199
+ if (0 > min) {
200
+ return Qnil;
201
+ }
202
+ args[6] = LONG2NUM(-(hr * 3600 + min * 60));
203
+ } else {
204
+ args[6] = LONG2NUM(0);
205
+ }
206
+ }
207
+ }
208
+ return rb_funcall2(rb_cTime, oj_new_id, 7, args);
209
+ }
210
+ #endif
211
+
212
+ static int
213
+ hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
214
+ const char *key = kval->key;
215
+ int klen = kval->klen;
216
+
217
+ if (2 == klen) {
218
+ switch (key[1]) {
219
+ case 'o': // object
220
+ { // name2class sets an error if the class is not found or created
221
+ VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
222
+
223
+ if (Qundef != clas) {
224
+ parent->val = rb_obj_alloc(clas);
225
+ }
226
+ }
227
+ break;
228
+ case 'O': // odd object
229
+ {
230
+ Odd odd = oj_get_oddc(str, len);
231
+
232
+ if (0 == odd) {
233
+ return 0;
234
+ }
235
+ parent->val = odd->clas;
236
+ parent->odd_args = oj_odd_alloc_args(odd);
237
+ }
238
+ break;
239
+ case 'm':
240
+ parent->val = rb_str_new(str + 1, len - 1);
241
+ parent->val = oj_encode(parent->val);
242
+ parent->val = rb_funcall(parent->val, oj_to_sym_id, 0);
243
+ break;
244
+ case 's':
245
+ parent->val = rb_str_new(str, len);
246
+ parent->val = oj_encode(parent->val);
247
+ break;
248
+ case 'c': // class
249
+ {
250
+ VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define, rb_eArgError);
251
+
252
+ if (Qundef == clas) {
253
+ return 0;
254
+ } else {
255
+ parent->val = clas;
256
+ }
257
+ }
258
+ break;
259
+ case 't': // time
260
+ parent->val = oj_parse_xml_time(str, len);
261
+ break;
262
+ default:
263
+ return 0;
264
+ break;
265
+ }
266
+ return 1; // handled
267
+ }
268
+ return 0;
269
+ }
270
+
271
+ static int
272
+ hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
273
+ if (2 == kval->klen) {
274
+ switch (kval->key[1]) {
275
+ case 't': // time as a float
276
+ {
277
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
278
+
279
+ if (ni->neg) {
280
+ ni->i = -ni->i;
281
+ if (0 < nsec) {
282
+ ni->i--;
283
+ nsec = 1000000000LL - nsec;
284
+ }
285
+ }
286
+ if (86400 == ni->exp) { // UTC time
287
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
288
+ // Since the ruby C routines alway create local time, the
289
+ // offset and then a convertion to UTC keeps makes the time
290
+ // match the expected value.
291
+ parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
292
+ } else if (ni->hasExp) {
293
+ time_t t = (time_t)(ni->i + ni->exp);
294
+ struct tm *st = gmtime(&t);
295
+ VALUE args[8];
296
+
297
+ args[0] = LONG2NUM(1900 + st->tm_year);
298
+ args[1] = LONG2NUM(1 + st->tm_mon);
299
+ args[2] = LONG2NUM(st->tm_mday);
300
+ args[3] = LONG2NUM(st->tm_hour);
301
+ args[4] = LONG2NUM(st->tm_min);
302
+ args[5] = rb_float_new((double)st->tm_sec + ((double)nsec + 0.5) / 1000000000.0);
303
+ args[6] = LONG2NUM(ni->exp);
304
+ parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
305
+ } else {
306
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
307
+ }
308
+ }
309
+ break;
310
+ case 'i': // circular index
311
+ if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
312
+ if (Qnil == parent->val) {
313
+ parent->val = rb_hash_new();
314
+ }
315
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
316
+ } else {
317
+ return 0;
318
+ }
319
+ break;
320
+ default:
321
+ return 0;
322
+ break;
323
+ }
324
+ return 1; // handled
325
+ }
326
+ return 0;
327
+ }
328
+
329
+ static int
330
+ hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, volatile VALUE value) {
331
+ if (T_ARRAY == rb_type(value)) {
332
+ int len = (int)RARRAY_LEN(value);
333
+
334
+ if (2 == klen && 'u' == key[1]) {
335
+ volatile VALUE sc;
336
+ volatile VALUE e1;
337
+ int slen;
338
+
339
+ if (0 == len) {
340
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
341
+ return 1;
342
+ }
343
+ e1 = *RARRAY_PTR(value);
344
+ // check for anonymous Struct
345
+ if (T_ARRAY == rb_type(e1)) {
346
+ VALUE args[1024];
347
+ volatile VALUE rstr;
348
+ int i, cnt = (int)RARRAY_LEN(e1);
349
+
350
+ for (i = 0; i < cnt; i++) {
351
+ rstr = rb_ary_entry(e1, i);
352
+ args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
353
+ }
354
+ sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
355
+ } else {
356
+ // If struct is not defined then we let this fail and raise an exception.
357
+ sc = oj_name2struct(pi, *RARRAY_PTR(value), rb_eArgError);
358
+ }
359
+ // Create a properly initialized struct instance without calling the initialize method.
360
+ parent->val = rb_obj_alloc(sc);
361
+ // If the JSON array has more entries than the struct class allows, we record an error.
362
+ #ifdef RSTRUCT_LEN
363
+ #if UNIFY_FIXNUM_AND_BIGNUM
364
+ slen = (int)NUM2LONG(RSTRUCT_LEN(parent->val));
365
+ #else // UNIFY_FIXNUM_AND_INTEGER
366
+ slen = (int)RSTRUCT_LEN(parent->val);
367
+ #endif // UNIFY_FIXNUM_AND_INTEGER
368
+ #else
369
+ slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
370
+ #endif
371
+ // MRI >= 1.9
372
+ if (len - 1 > slen) {
373
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
374
+ } else {
375
+ int i;
376
+
377
+ for (i = 0; i < slen; i++) {
378
+ rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
379
+ }
380
+ }
381
+ return 1;
382
+ } else if (3 <= klen && '#' == key[1]) {
383
+ volatile VALUE *a;
384
+
385
+ if (2 != len) {
386
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
387
+ return 1;
388
+ }
389
+ parent->val = rb_hash_new();
390
+ a = RARRAY_PTR(value);
391
+ rb_hash_aset(parent->val, *a, a[1]);
392
+
393
+ return 1;
394
+ }
395
+ }
396
+ return 0;
397
+ }
398
+
399
+ static void
400
+ copy_ivars(VALUE target, VALUE src) {
401
+ volatile VALUE vars = rb_funcall(src, oj_instance_variables_id, 0);
402
+ volatile VALUE *np = RARRAY_PTR(vars);
403
+ ID vid;
404
+ int i, cnt = (int)RARRAY_LEN(vars);
405
+ const char *attr;
406
+
407
+ for (i = cnt; 0 < i; i--, np++) {
408
+ vid = rb_to_id(*np);
409
+ attr = rb_id2name(vid);
410
+ if ('@' == *attr) {
411
+ rb_ivar_set(target, vid, rb_ivar_get(src, vid));
412
+ }
413
+ }
414
+ }
415
+
416
+ void
417
+ oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
418
+ const char *key = kval->key;
419
+ int klen = kval->klen;
420
+ ID var_id;
421
+ ID *slot;
422
+
423
+ if ('~' == *key && Qtrue == rb_obj_is_kind_of(parent->val, rb_eException)) {
424
+ if (5 == klen && 0 == strncmp("~mesg", key, klen)) {
425
+ VALUE args[1];
426
+ volatile VALUE prev = parent->val;
427
+
428
+ args[0] = value;
429
+ parent->val = rb_class_new_instance(1, args, rb_class_of(parent->val));
430
+ copy_ivars(parent->val, prev);
431
+ } else if (3 == klen && 0 == strncmp("~bt", key, klen)) {
432
+ rb_funcall(parent->val, rb_intern("set_backtrace"), 1, value);
433
+ }
434
+ }
435
+ #if USE_PTHREAD_MUTEX
436
+ pthread_mutex_lock(&oj_cache_mutex);
437
+ #elif USE_RB_MUTEX
438
+ rb_mutex_lock(oj_cache_mutex);
439
+ #endif
440
+ if (0 == (var_id = oj_attr_hash_get(key, klen, &slot))) {
441
+ char attr[256];
442
+
443
+ if ((int)sizeof(attr) <= klen + 2) {
444
+ char *buf = ALLOC_N(char, klen + 2);
445
+
446
+ if ('~' == *key) {
447
+ strncpy(buf, key + 1, klen - 1);
448
+ buf[klen - 1] = '\0';
449
+ } else {
450
+ *buf = '@';
451
+ strncpy(buf + 1, key, klen);
452
+ buf[klen + 1] = '\0';
453
+ }
454
+ var_id = rb_intern(buf);
455
+ xfree(buf);
456
+ } else {
457
+ if ('~' == *key) {
458
+ strncpy(attr, key + 1, klen - 1);
459
+ attr[klen - 1] = '\0';
460
+ } else {
461
+ *attr = '@';
462
+ strncpy(attr + 1, key, klen);
463
+ attr[klen + 1] = '\0';
464
+ }
465
+ var_id = rb_intern(attr);
466
+ }
467
+ *slot = var_id;
468
+ }
469
+ #if USE_PTHREAD_MUTEX
470
+ pthread_mutex_unlock(&oj_cache_mutex);
471
+ #elif USE_RB_MUTEX
472
+ rb_mutex_unlock(oj_cache_mutex);
473
+ #endif
474
+ rb_ivar_set(parent->val, var_id, value);
475
+ }
476
+
477
+ static void
478
+ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
479
+ const char *key = kval->key;
480
+ int klen = kval->klen;
481
+ Val parent = stack_peek(&pi->stack);
482
+
483
+ WHICH_TYPE:
484
+ switch (rb_type(parent->val)) {
485
+ case T_NIL:
486
+ parent->odd_args = 0; // make sure it is 0 in case not odd
487
+ if ('^' != *key || !hat_cstr(pi, parent, kval, str, len)) {
488
+ parent->val = rb_hash_new();
489
+ goto WHICH_TYPE;
490
+ }
491
+ break;
492
+ case T_HASH:
493
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), str_to_value(pi, str, len, orig));
494
+ break;
495
+ case T_STRING:
496
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
497
+ rb_funcall(parent->val, oj_replace_id, 1, str_to_value(pi, str, len, orig));
498
+ } else {
499
+ oj_set_obj_ivar(parent, kval, str_to_value(pi, str, len, orig));
500
+ }
501
+ break;
502
+ case T_OBJECT:
503
+ oj_set_obj_ivar(parent, kval, str_to_value(pi, str, len, orig));
504
+ break;
505
+ case T_CLASS:
506
+ if (0 == parent->odd_args) {
507
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
508
+ return;
509
+ } else if (0 != oj_odd_set_arg(parent->odd_args, kval->key, kval->klen, str_to_value(pi, str, len, orig))) {
510
+ char buf[256];
511
+
512
+ if ((int)sizeof(buf) - 1 <= klen) {
513
+ klen = sizeof(buf) - 2;
514
+ }
515
+ memcpy(buf, key, klen);
516
+ buf[klen] = '\0';
517
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an attribute of %s", buf, rb_class2name(rb_obj_class(parent->val)));
518
+ }
519
+ break;
520
+ default:
521
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "can not add attributes to a %s", rb_class2name(rb_obj_class(parent->val)));
522
+ return;
523
+ }
524
+ }
525
+
526
+ static void
527
+ hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
528
+ const char *key = kval->key;
529
+ int klen = kval->klen;
530
+ Val parent = stack_peek(&pi->stack);
531
+
532
+ WHICH_TYPE:
533
+ switch (rb_type(parent->val)) {
534
+ case T_NIL:
535
+ parent->odd_args = 0; // make sure it is 0 in case not odd
536
+ if ('^' != *key || !hat_num(pi, parent, kval, ni)) {
537
+ parent->val = rb_hash_new();
538
+ goto WHICH_TYPE;
539
+ }
540
+ break;
541
+ case T_HASH:
542
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), oj_num_as_value(ni));
543
+ break;
544
+ case T_OBJECT:
545
+ if (2 == klen && '^' == *key && 'i' == key[1] &&
546
+ !ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
547
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
548
+ } else {
549
+ oj_set_obj_ivar(parent, kval, oj_num_as_value(ni));
550
+ }
551
+ break;
552
+ case T_CLASS:
553
+ if (0 == parent->odd_args) {
554
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
555
+ return;
556
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, oj_num_as_value(ni))) {
557
+ char buf[256];
558
+
559
+ if ((int)sizeof(buf) - 1 <= klen) {
560
+ klen = sizeof(buf) - 2;
561
+ }
562
+ memcpy(buf, key, klen);
563
+ buf[klen] = '\0';
564
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an attribute of %s", buf, rb_class2name(rb_obj_class(parent->val)));
565
+ }
566
+ break;
567
+ default:
568
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "can not add attributes to a %s", rb_class2name(rb_obj_class(parent->val)));
569
+ return;
570
+ }
571
+ }
572
+
573
+ static void
574
+ hash_set_value(ParseInfo pi, Val kval, VALUE value) {
575
+ const char *key = kval->key;
576
+ int klen = kval->klen;
577
+ Val parent = stack_peek(&pi->stack);
578
+
579
+ WHICH_TYPE:
580
+ switch (rb_type(parent->val)) {
581
+ case T_NIL:
582
+ parent->odd_args = 0; // make sure it is 0 in case not odd
583
+ if ('^' != *key || !hat_value(pi, parent, key, klen, value)) {
584
+ parent->val = rb_hash_new();
585
+ goto WHICH_TYPE;
586
+ }
587
+ break;
588
+ case T_HASH:
589
+ if (rb_cHash != rb_obj_class(parent->val)) {
590
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
591
+ rb_funcall(parent->val, oj_replace_id, 1, value);
592
+ } else {
593
+ oj_set_obj_ivar(parent, kval, value);
594
+ }
595
+ } else {
596
+ if (3 <= klen && '^' == *key && '#' == key[1] && T_ARRAY == rb_type(value)) {
597
+ long len = RARRAY_LEN(value);
598
+ volatile VALUE *a = RARRAY_PTR(value);
599
+
600
+ if (2 != len) {
601
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
602
+ return;
603
+ }
604
+ rb_hash_aset(parent->val, *a, a[1]);
605
+ } else {
606
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), value);
607
+ }
608
+ }
609
+ break;
610
+ case T_ARRAY:
611
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
612
+ rb_funcall(parent->val, oj_replace_id, 1, value);
613
+ } else {
614
+ oj_set_obj_ivar(parent, kval, value);
615
+ }
616
+ break;
617
+ case T_STRING: // for subclassed strings
618
+ case T_OBJECT:
619
+ oj_set_obj_ivar(parent, kval, value);
620
+ break;
621
+ case T_MODULE:
622
+ case T_CLASS:
623
+ if (0 == parent->odd_args) {
624
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
625
+ return;
626
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
627
+ char buf[256];
628
+
629
+ if ((int)sizeof(buf) - 1 <= klen) {
630
+ klen = sizeof(buf) - 2;
631
+ }
632
+ memcpy(buf, key, klen);
633
+ buf[klen] = '\0';
634
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an attribute of %s", buf, rb_class2name(rb_obj_class(parent->val)));
635
+ }
636
+ break;
637
+ default:
638
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "can not add attributes to a %s", rb_class2name(rb_obj_class(parent->val)));
639
+ return;
640
+ }
641
+ }
642
+
643
+ static VALUE
644
+ start_hash(ParseInfo pi) {
645
+ return Qnil;
646
+ }
647
+
648
+ static void
649
+ end_hash(struct _ParseInfo *pi) {
650
+ Val parent = stack_peek(&pi->stack);
651
+
652
+ if (Qnil == parent->val) {
653
+ parent->val = rb_hash_new();
654
+ } else if (0 != parent->odd_args) {
655
+ OddArgs oa = parent->odd_args;
656
+
657
+ parent->val = rb_funcall2(oa->odd->create_obj, oa->odd->create_op, oa->odd->attr_cnt, oa->args);
658
+ oj_odd_free(oa);
659
+ parent->odd_args = 0;
660
+ }
661
+ }
662
+
663
+ static void
664
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
665
+ if (3 <= len && 0 != pi->circ_array) {
666
+ if ('i' == str[1]) {
667
+ long i = read_long(str + 2, len - 2);
668
+
669
+ if (0 < i) {
670
+ oj_circ_array_set(pi->circ_array, stack_peek(&pi->stack)->val, i);
671
+ return;
672
+ }
673
+ } else if ('r' == str[1]) {
674
+ long i = read_long(str + 2, len - 2);
675
+
676
+ if (0 < i) {
677
+ rb_ary_push(stack_peek(&pi->stack)->val, oj_circ_array_get(pi->circ_array, i));
678
+ return;
679
+ }
680
+
681
+ }
682
+ }
683
+ rb_ary_push(stack_peek(&pi->stack)->val, str_to_value(pi, str, len, orig));
684
+ }
685
+
686
+ static void
687
+ array_append_num(ParseInfo pi, NumInfo ni) {
688
+ rb_ary_push(stack_peek(&pi->stack)->val, oj_num_as_value(ni));
689
+ }
690
+
691
+ static void
692
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
693
+ pi->stack.head->val = str_to_value(pi, str, len, orig);
694
+ }
695
+
696
+ static void
697
+ add_num(ParseInfo pi, NumInfo ni) {
698
+ pi->stack.head->val = oj_num_as_value(ni);
699
+ }
700
+
701
+ void
702
+ oj_set_object_callbacks(ParseInfo pi) {
703
+ oj_set_strict_callbacks(pi);
704
+ pi->end_hash = end_hash;
705
+ pi->start_hash = start_hash;
706
+ pi->hash_set_cstr = hash_set_cstr;
707
+ pi->hash_set_num = hash_set_num;
708
+ pi->hash_set_value = hash_set_value;
709
+ pi->add_cstr = add_cstr;
710
+ pi->add_num = add_num;
711
+ pi->array_append_cstr = array_append_cstr;
712
+ pi->array_append_num = array_append_num;
713
+ }
714
+
715
+ VALUE
716
+ oj_object_parse(int argc, VALUE *argv, VALUE self) {
717
+ struct _ParseInfo pi;
718
+
719
+ parse_info_init(&pi);
720
+ pi.options = oj_default_options;
721
+ pi.handler = Qnil;
722
+ pi.err_class = Qnil;
723
+ oj_set_object_callbacks(&pi);
724
+
725
+ if (T_STRING == rb_type(*argv)) {
726
+ return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
727
+ } else {
728
+ return oj_pi_sparse(argc, argv, &pi, 0);
729
+ }
730
+ }
731
+
732
+ VALUE
733
+ oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
734
+ struct _ParseInfo pi;
735
+
736
+ parse_info_init(&pi);
737
+ pi.options = oj_default_options;
738
+ pi.handler = Qnil;
739
+ pi.err_class = Qnil;
740
+ oj_set_strict_callbacks(&pi);
741
+ pi.end_hash = end_hash;
742
+ pi.start_hash = start_hash;
743
+ pi.hash_set_cstr = hash_set_cstr;
744
+ pi.hash_set_num = hash_set_num;
745
+ pi.hash_set_value = hash_set_value;
746
+ pi.add_cstr = add_cstr;
747
+ pi.add_num = add_num;
748
+ pi.array_append_cstr = array_append_cstr;
749
+ pi.array_append_num = array_append_num;
750
+
751
+ return oj_pi_parse(argc, argv, &pi, json, len, 1);
752
+ }