oj 3.10.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) 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 +1218 -0
  13. data/ext/oj/dump.c +1249 -0
  14. data/ext/oj/dump.h +96 -0
  15. data/ext/oj/dump_compat.c +975 -0
  16. data/ext/oj/dump_leaf.c +252 -0
  17. data/ext/oj/dump_object.c +844 -0
  18. data/ext/oj/dump_strict.c +434 -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 +53 -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 +890 -0
  28. data/ext/oj/object.c +775 -0
  29. data/ext/oj/odd.c +231 -0
  30. data/ext/oj/odd.h +44 -0
  31. data/ext/oj/oj.c +1723 -0
  32. data/ext/oj/oj.h +387 -0
  33. data/ext/oj/parse.c +1134 -0
  34. data/ext/oj/parse.h +112 -0
  35. data/ext/oj/rails.c +1528 -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 +924 -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 +287 -0
  74. data/pages/Rails.md +155 -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/abstract_unit.rb +45 -0
  85. data/test/activesupport5/decoding_test.rb +133 -0
  86. data/test/activesupport5/encoding_test.rb +500 -0
  87. data/test/activesupport5/encoding_test_cases.rb +98 -0
  88. data/test/activesupport5/test_helper.rb +72 -0
  89. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  90. data/test/activesupport6/abstract_unit.rb +44 -0
  91. data/test/activesupport6/decoding_test.rb +133 -0
  92. data/test/activesupport6/encoding_test.rb +507 -0
  93. data/test/activesupport6/encoding_test_cases.rb +98 -0
  94. data/test/activesupport6/test_common.rb +17 -0
  95. data/test/activesupport6/test_helper.rb +163 -0
  96. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  97. data/test/bar.rb +35 -0
  98. data/test/baz.rb +16 -0
  99. data/test/files.rb +29 -0
  100. data/test/foo.rb +52 -0
  101. data/test/helper.rb +26 -0
  102. data/test/isolated/shared.rb +308 -0
  103. data/test/isolated/test_mimic_after.rb +13 -0
  104. data/test/isolated/test_mimic_alone.rb +12 -0
  105. data/test/isolated/test_mimic_as_json.rb +45 -0
  106. data/test/isolated/test_mimic_before.rb +13 -0
  107. data/test/isolated/test_mimic_define.rb +28 -0
  108. data/test/isolated/test_mimic_rails_after.rb +22 -0
  109. data/test/isolated/test_mimic_rails_before.rb +21 -0
  110. data/test/isolated/test_mimic_redefine.rb +15 -0
  111. data/test/json_gem/json_addition_test.rb +216 -0
  112. data/test/json_gem/json_common_interface_test.rb +148 -0
  113. data/test/json_gem/json_encoding_test.rb +107 -0
  114. data/test/json_gem/json_ext_parser_test.rb +20 -0
  115. data/test/json_gem/json_fixtures_test.rb +35 -0
  116. data/test/json_gem/json_generator_test.rb +383 -0
  117. data/test/json_gem/json_generic_object_test.rb +90 -0
  118. data/test/json_gem/json_parser_test.rb +470 -0
  119. data/test/json_gem/json_string_matching_test.rb +42 -0
  120. data/test/json_gem/test_helper.rb +18 -0
  121. data/test/perf.rb +107 -0
  122. data/test/perf_compat.rb +130 -0
  123. data/test/perf_fast.rb +164 -0
  124. data/test/perf_file.rb +64 -0
  125. data/test/perf_object.rb +138 -0
  126. data/test/perf_saj.rb +109 -0
  127. data/test/perf_scp.rb +151 -0
  128. data/test/perf_simple.rb +287 -0
  129. data/test/perf_strict.rb +145 -0
  130. data/test/perf_wab.rb +131 -0
  131. data/test/prec.rb +23 -0
  132. data/test/sample.rb +54 -0
  133. data/test/sample/change.rb +14 -0
  134. data/test/sample/dir.rb +19 -0
  135. data/test/sample/doc.rb +36 -0
  136. data/test/sample/file.rb +48 -0
  137. data/test/sample/group.rb +16 -0
  138. data/test/sample/hasprops.rb +16 -0
  139. data/test/sample/layer.rb +12 -0
  140. data/test/sample/line.rb +20 -0
  141. data/test/sample/oval.rb +10 -0
  142. data/test/sample/rect.rb +10 -0
  143. data/test/sample/shape.rb +35 -0
  144. data/test/sample/text.rb +20 -0
  145. data/test/sample_json.rb +37 -0
  146. data/test/test_compat.rb +502 -0
  147. data/test/test_custom.rb +527 -0
  148. data/test/test_debian.rb +53 -0
  149. data/test/test_fast.rb +470 -0
  150. data/test/test_file.rb +239 -0
  151. data/test/test_gc.rb +49 -0
  152. data/test/test_hash.rb +29 -0
  153. data/test/test_integer_range.rb +72 -0
  154. data/test/test_null.rb +376 -0
  155. data/test/test_object.rb +1027 -0
  156. data/test/test_rails.rb +26 -0
  157. data/test/test_saj.rb +186 -0
  158. data/test/test_scp.rb +433 -0
  159. data/test/test_strict.rb +433 -0
  160. data/test/test_various.rb +719 -0
  161. data/test/test_wab.rb +307 -0
  162. data/test/test_writer.rb +380 -0
  163. data/test/tests.rb +25 -0
  164. data/test/tests_mimic.rb +14 -0
  165. data/test/tests_mimic_addition.rb +7 -0
  166. data/test/zoo.rb +13 -0
  167. metadata +381 -0
@@ -0,0 +1,775 @@
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
+ if (0 == ni->div || 9 < ni->di) {
280
+ rb_raise(rb_eArgError, "Invalid time decimal representation.");
281
+ //parent->val = rb_time_nano_new(0, 0);
282
+ } else {
283
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
284
+
285
+ if (ni->neg) {
286
+ ni->i = -ni->i;
287
+ if (0 < nsec) {
288
+ ni->i--;
289
+ nsec = 1000000000LL - nsec;
290
+ }
291
+ }
292
+ if (86400 == ni->exp) { // UTC time
293
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
294
+ // Since the ruby C routines alway create local time, the
295
+ // offset and then a conversion to UTC keeps makes the time
296
+ // match the expected value.
297
+ parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
298
+ } else if (ni->hasExp) {
299
+ int64_t t = (int64_t)(ni->i + ni->exp);
300
+ struct _timeInfo ti;
301
+ VALUE args[8];
302
+
303
+ sec_as_time(t, &ti);
304
+ args[0] = LONG2NUM((long)(ti.year));
305
+ args[1] = LONG2NUM(ti.mon);
306
+ args[2] = LONG2NUM(ti.day);
307
+ args[3] = LONG2NUM(ti.hour);
308
+ args[4] = LONG2NUM(ti.min);
309
+ args[5] = rb_float_new((double)ti.sec + ((double)nsec + 0.5) / 1000000000.0);
310
+ args[6] = LONG2NUM(ni->exp);
311
+ parent->val = rb_funcall2(rb_cTime, oj_new_id, 7, args);
312
+ } else {
313
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
314
+ }
315
+ }
316
+ break;
317
+ case 'i': // circular index
318
+ if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
319
+ if (Qnil == parent->val) {
320
+ parent->val = rb_hash_new();
321
+ }
322
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
323
+ } else {
324
+ return 0;
325
+ }
326
+ break;
327
+ default:
328
+ return 0;
329
+ break;
330
+ }
331
+ return 1; // handled
332
+ }
333
+ return 0;
334
+ }
335
+
336
+ static int
337
+ hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, volatile VALUE value) {
338
+ if (T_ARRAY == rb_type(value)) {
339
+ int len = (int)RARRAY_LEN(value);
340
+
341
+ if (2 == klen && 'u' == key[1]) {
342
+ volatile VALUE sc;
343
+ volatile VALUE e1;
344
+ int slen;
345
+
346
+ if (0 == len) {
347
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
348
+ return 1;
349
+ }
350
+ e1 = *RARRAY_PTR(value);
351
+ // check for anonymous Struct
352
+ if (T_ARRAY == rb_type(e1)) {
353
+ VALUE args[1024];
354
+ volatile VALUE rstr;
355
+ int i, cnt = (int)RARRAY_LEN(e1);
356
+
357
+ for (i = 0; i < cnt; i++) {
358
+ rstr = rb_ary_entry(e1, i);
359
+ args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
360
+ }
361
+ sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
362
+ } else {
363
+ // If struct is not defined then we let this fail and raise an exception.
364
+ sc = oj_name2struct(pi, *RARRAY_PTR(value), rb_eArgError);
365
+ }
366
+ // Create a properly initialized struct instance without calling the initialize method.
367
+ parent->val = rb_obj_alloc(sc);
368
+ // If the JSON array has more entries than the struct class allows, we record an error.
369
+ #ifdef RSTRUCT_LEN
370
+ #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
371
+ slen = (int)NUM2LONG(RSTRUCT_LEN(parent->val));
372
+ #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
373
+ slen = (int)RSTRUCT_LEN(parent->val);
374
+ #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
375
+ #else
376
+ slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
377
+ #endif
378
+ // MRI >= 1.9
379
+ if (len - 1 > slen) {
380
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
381
+ } else {
382
+ int i;
383
+
384
+ for (i = 0; i < len - 1; i++) {
385
+ rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
386
+ }
387
+ }
388
+ return 1;
389
+ } else if (3 <= klen && '#' == key[1]) {
390
+ volatile VALUE *a;
391
+
392
+ if (2 != len) {
393
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
394
+ return 1;
395
+ }
396
+ parent->val = rb_hash_new();
397
+ a = RARRAY_PTR(value);
398
+ rb_hash_aset(parent->val, *a, a[1]);
399
+
400
+ return 1;
401
+ }
402
+ }
403
+ return 0;
404
+ }
405
+
406
+ void
407
+ oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
408
+ const char *key = kval->key;
409
+ int klen = kval->klen;
410
+ ID var_id;
411
+ ID *slot;
412
+
413
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
414
+ pthread_mutex_lock(&oj_cache_mutex);
415
+ #else
416
+ rb_mutex_lock(oj_cache_mutex);
417
+ #endif
418
+ if (0 == (var_id = oj_attr_hash_get(key, klen, &slot))) {
419
+ char attr[256];
420
+
421
+ if ((int)sizeof(attr) <= klen + 2) {
422
+ char *buf = ALLOC_N(char, klen + 2);
423
+
424
+ if ('~' == *key) {
425
+ strncpy(buf, key + 1, klen - 1);
426
+ buf[klen - 1] = '\0';
427
+ } else {
428
+ *buf = '@';
429
+ strncpy(buf + 1, key, klen);
430
+ buf[klen + 1] = '\0';
431
+ }
432
+ var_id = rb_intern(buf);
433
+ xfree(buf);
434
+ } else {
435
+ if ('~' == *key) {
436
+ strncpy(attr, key + 1, klen - 1);
437
+ attr[klen - 1] = '\0';
438
+ } else {
439
+ *attr = '@';
440
+ strncpy(attr + 1, key, klen);
441
+ attr[klen + 1] = '\0';
442
+ }
443
+ var_id = rb_intern(attr);
444
+ }
445
+ *slot = var_id;
446
+ }
447
+ #ifdef HAVE_PTHREAD_MUTEX_INIT
448
+ pthread_mutex_unlock(&oj_cache_mutex);
449
+ #else
450
+ rb_mutex_unlock(oj_cache_mutex);
451
+ #endif
452
+ rb_ivar_set(parent->val, var_id, value);
453
+ }
454
+
455
+ static void
456
+ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
457
+ const char *key = kval->key;
458
+ int klen = kval->klen;
459
+ Val parent = stack_peek(&pi->stack);
460
+ volatile VALUE rval = Qnil;
461
+
462
+ WHICH_TYPE:
463
+ switch (rb_type(parent->val)) {
464
+ case T_NIL:
465
+ parent->odd_args = NULL; // make sure it is NULL in case not odd
466
+ if ('^' != *key || !hat_cstr(pi, parent, kval, str, len)) {
467
+ parent->val = rb_hash_new();
468
+ goto WHICH_TYPE;
469
+ }
470
+ break;
471
+ case T_HASH:
472
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), str_to_value(pi, str, len, orig));
473
+ break;
474
+ case T_STRING:
475
+ rval = str_to_value(pi, str, len, orig);
476
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
477
+ rb_funcall(parent->val, oj_replace_id, 1, rval);
478
+ } else {
479
+ oj_set_obj_ivar(parent, kval, rval);
480
+ }
481
+ break;
482
+ case T_OBJECT:
483
+ rval = str_to_value(pi, str, len, orig);
484
+ oj_set_obj_ivar(parent, kval, rval);
485
+ break;
486
+ case T_CLASS:
487
+ if (NULL == parent->odd_args) {
488
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
489
+ return;
490
+ } else {
491
+ rval = str_to_value(pi, str, len, orig);
492
+ if (0 != oj_odd_set_arg(parent->odd_args, kval->key, kval->klen, rval)) {
493
+ char buf[256];
494
+
495
+ if ((int)sizeof(buf) - 1 <= klen) {
496
+ klen = sizeof(buf) - 2;
497
+ }
498
+ memcpy(buf, key, klen);
499
+ buf[klen] = '\0';
500
+ 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)));
501
+ }
502
+ }
503
+ break;
504
+ default:
505
+ 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)));
506
+ return;
507
+ }
508
+ if (Yes == pi->options.trace) {
509
+ oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
510
+ }
511
+ }
512
+
513
+ static void
514
+ hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
515
+ const char *key = kval->key;
516
+ int klen = kval->klen;
517
+ Val parent = stack_peek(&pi->stack);
518
+ volatile VALUE rval = Qnil;
519
+
520
+ WHICH_TYPE:
521
+ switch (rb_type(parent->val)) {
522
+ case T_NIL:
523
+ parent->odd_args = NULL; // make sure it is NULL in case not odd
524
+ if ('^' != *key || !hat_num(pi, parent, kval, ni)) {
525
+ parent->val = rb_hash_new();
526
+ goto WHICH_TYPE;
527
+ }
528
+ break;
529
+ case T_HASH:
530
+ rval = oj_num_as_value(ni);
531
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), rval);
532
+ break;
533
+ case T_OBJECT:
534
+ if (2 == klen && '^' == *key && 'i' == key[1] &&
535
+ !ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
536
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
537
+ } else {
538
+ rval = oj_num_as_value(ni);
539
+ oj_set_obj_ivar(parent, kval, rval);
540
+ }
541
+ break;
542
+ case T_CLASS:
543
+ if (NULL == parent->odd_args) {
544
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
545
+ return;
546
+ } else {
547
+ rval = oj_num_as_value(ni);
548
+ if (0 != oj_odd_set_arg(parent->odd_args, key, klen, rval)) {
549
+ char buf[256];
550
+
551
+ if ((int)sizeof(buf) - 1 <= klen) {
552
+ klen = sizeof(buf) - 2;
553
+ }
554
+ memcpy(buf, key, klen);
555
+ buf[klen] = '\0';
556
+ 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)));
557
+ }
558
+ }
559
+ break;
560
+ default:
561
+ 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)));
562
+ return;
563
+ }
564
+ if (Yes == pi->options.trace) {
565
+ oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, rval);
566
+ }
567
+ }
568
+
569
+ static void
570
+ hash_set_value(ParseInfo pi, Val kval, VALUE value) {
571
+ const char *key = kval->key;
572
+ int klen = kval->klen;
573
+ Val parent = stack_peek(&pi->stack);
574
+
575
+ WHICH_TYPE:
576
+ switch (rb_type(parent->val)) {
577
+ case T_NIL:
578
+ parent->odd_args = NULL; // make sure it is NULL in case not odd
579
+ if ('^' != *key || !hat_value(pi, parent, key, klen, value)) {
580
+ parent->val = rb_hash_new();
581
+ goto WHICH_TYPE;
582
+ }
583
+ break;
584
+ case T_HASH:
585
+ if (rb_cHash != rb_obj_class(parent->val)) {
586
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
587
+ rb_funcall(parent->val, oj_replace_id, 1, value);
588
+ } else {
589
+ oj_set_obj_ivar(parent, kval, value);
590
+ }
591
+ } else {
592
+ if (3 <= klen && '^' == *key && '#' == key[1] && T_ARRAY == rb_type(value)) {
593
+ long len = RARRAY_LEN(value);
594
+ volatile VALUE *a = RARRAY_PTR(value);
595
+
596
+ if (2 != len) {
597
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
598
+ return;
599
+ }
600
+ rb_hash_aset(parent->val, *a, a[1]);
601
+ } else {
602
+ rb_hash_aset(parent->val, calc_hash_key(pi, kval, parent->k1), value);
603
+ }
604
+ }
605
+ break;
606
+ case T_ARRAY:
607
+ if (4 == klen && 's' == *key && 'e' == key[1] && 'l' == key[2] && 'f' == key[3]) {
608
+ rb_funcall(parent->val, oj_replace_id, 1, value);
609
+ } else {
610
+ oj_set_obj_ivar(parent, kval, value);
611
+ }
612
+ break;
613
+ case T_STRING: // for subclassed strings
614
+ case T_OBJECT:
615
+ oj_set_obj_ivar(parent, kval, value);
616
+ break;
617
+ case T_MODULE:
618
+ case T_CLASS:
619
+ if (NULL == parent->odd_args) {
620
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
621
+ return;
622
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
623
+ char buf[256];
624
+
625
+ if ((int)sizeof(buf) - 1 <= klen) {
626
+ klen = sizeof(buf) - 2;
627
+ }
628
+ memcpy(buf, key, klen);
629
+ buf[klen] = '\0';
630
+ 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)));
631
+ }
632
+ break;
633
+ default:
634
+ 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)));
635
+ return;
636
+ }
637
+ if (Yes == pi->options.trace) {
638
+ oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, value);
639
+ }
640
+ }
641
+
642
+ static VALUE
643
+ start_hash(ParseInfo pi) {
644
+ if (Yes == pi->options.trace) {
645
+ oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
646
+ }
647
+ return Qnil;
648
+ }
649
+
650
+ static void
651
+ end_hash(ParseInfo pi) {
652
+ Val parent = stack_peek(&pi->stack);
653
+
654
+ if (Qnil == parent->val) {
655
+ parent->val = rb_hash_new();
656
+ } else if (NULL != parent->odd_args) {
657
+ OddArgs oa = parent->odd_args;
658
+
659
+ parent->val = rb_funcall2(oa->odd->create_obj, oa->odd->create_op, oa->odd->attr_cnt, oa->args);
660
+ oj_odd_free(oa);
661
+ parent->odd_args = NULL;
662
+ }
663
+ if (Yes == pi->options.trace) {
664
+ oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
665
+ }
666
+ }
667
+
668
+ static void
669
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
670
+ volatile VALUE rval = Qnil;
671
+
672
+ // orig lets us know whether the string was ^r1 or \u005er1
673
+ if (3 <= len && 0 != pi->circ_array && '^' == orig[0] && 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
674
+ if ('i' == str[1]) {
675
+ long i = read_long(str + 2, len - 2);
676
+
677
+ if (0 < i) {
678
+ oj_circ_array_set(pi->circ_array, stack_peek(&pi->stack)->val, i);
679
+ return;
680
+ }
681
+ } else if ('r' == str[1]) {
682
+ long i = read_long(str + 2, len - 2);
683
+
684
+ if (0 < i) {
685
+ rb_ary_push(stack_peek(&pi->stack)->val, oj_circ_array_get(pi->circ_array, i));
686
+ return;
687
+ }
688
+
689
+ }
690
+ }
691
+ rval = str_to_value(pi, str, len, orig);
692
+ rb_ary_push(stack_peek(&pi->stack)->val, rval);
693
+ if (Yes == pi->options.trace) {
694
+ oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rval);
695
+ }
696
+ }
697
+
698
+ static void
699
+ array_append_num(ParseInfo pi, NumInfo ni) {
700
+ volatile VALUE rval = oj_num_as_value(ni);
701
+
702
+ rb_ary_push(stack_peek(&pi->stack)->val, rval);
703
+ if (Yes == pi->options.trace) {
704
+ oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
705
+ }
706
+ }
707
+
708
+ static void
709
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
710
+ pi->stack.head->val = str_to_value(pi, str, len, orig);
711
+ if (Yes == pi->options.trace) {
712
+ oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
713
+ }
714
+ }
715
+
716
+ static void
717
+ add_num(ParseInfo pi, NumInfo ni) {
718
+ pi->stack.head->val = oj_num_as_value(ni);
719
+ if (Yes == pi->options.trace) {
720
+ oj_trace_parse_call("add_num", pi, __FILE__, __LINE__, pi->stack.head->val);
721
+ }
722
+ }
723
+
724
+ void
725
+ oj_set_object_callbacks(ParseInfo pi) {
726
+ oj_set_strict_callbacks(pi);
727
+ pi->end_hash = end_hash;
728
+ pi->start_hash = start_hash;
729
+ pi->hash_set_cstr = hash_set_cstr;
730
+ pi->hash_set_num = hash_set_num;
731
+ pi->hash_set_value = hash_set_value;
732
+ pi->add_cstr = add_cstr;
733
+ pi->add_num = add_num;
734
+ pi->array_append_cstr = array_append_cstr;
735
+ pi->array_append_num = array_append_num;
736
+ }
737
+
738
+ VALUE
739
+ oj_object_parse(int argc, VALUE *argv, VALUE self) {
740
+ struct _parseInfo pi;
741
+
742
+ parse_info_init(&pi);
743
+ pi.options = oj_default_options;
744
+ pi.handler = Qnil;
745
+ pi.err_class = Qnil;
746
+ oj_set_object_callbacks(&pi);
747
+
748
+ if (T_STRING == rb_type(*argv)) {
749
+ return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
750
+ } else {
751
+ return oj_pi_sparse(argc, argv, &pi, 0);
752
+ }
753
+ }
754
+
755
+ VALUE
756
+ oj_object_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
757
+ struct _parseInfo pi;
758
+
759
+ parse_info_init(&pi);
760
+ pi.options = oj_default_options;
761
+ pi.handler = Qnil;
762
+ pi.err_class = Qnil;
763
+ oj_set_strict_callbacks(&pi);
764
+ pi.end_hash = end_hash;
765
+ pi.start_hash = start_hash;
766
+ pi.hash_set_cstr = hash_set_cstr;
767
+ pi.hash_set_num = hash_set_num;
768
+ pi.hash_set_value = hash_set_value;
769
+ pi.add_cstr = add_cstr;
770
+ pi.add_num = add_num;
771
+ pi.array_append_cstr = array_append_cstr;
772
+ pi.array_append_num = array_append_num;
773
+
774
+ return oj_pi_parse(argc, argv, &pi, json, len, 1);
775
+ }