oj 3.10.7

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 (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
+ }