oj 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }