oj 3.8.1

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