oj 2.0.14 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of oj might be problematic. Click here for more details.

@@ -0,0 +1,514 @@
1
+ /* object.c
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * - Redistributions of source code must retain the above copyright notice, this
9
+ * list of conditions and the following disclaimer.
10
+ *
11
+ * - Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
14
+ *
15
+ * - Neither the name of Peter Ohler nor the names of its contributors may be
16
+ * used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #include <stdio.h>
32
+
33
+ #include "oj.h"
34
+ #include "err.h"
35
+ #include "parse.h"
36
+ #include "resolve.h"
37
+ #include "hash.h"
38
+ #include "odd.h"
39
+
40
+ inline static long
41
+ read_long(const char *str, size_t len) {
42
+ long n = 0;
43
+
44
+ for (; 0 < len; str++, len--) {
45
+ if ('0' <= *str && *str <= '9') {
46
+ n = n * 10 + (*str - '0');
47
+ } else {
48
+ return -1;
49
+ }
50
+ }
51
+ return n;
52
+ }
53
+
54
+ static VALUE
55
+ hash_key(ParseInfo pi, const char *key, size_t klen, char k1) {
56
+ VALUE rkey;
57
+
58
+ if (':' == k1) {
59
+ rkey = rb_str_new(key + 1, klen - 1);
60
+ #if HAS_ENCODING_SUPPORT
61
+ rb_enc_associate(rkey, oj_utf8_encoding);
62
+ #endif
63
+ rkey = rb_funcall(rkey, oj_to_sym_id, 0);
64
+ } else {
65
+ rkey = rb_str_new(key, klen);
66
+
67
+ #if HAS_ENCODING_SUPPORT
68
+ rb_enc_associate(rkey, oj_utf8_encoding);
69
+ #endif
70
+ if (Yes == pi->options.sym_key) {
71
+ rkey = rb_str_intern(rkey);
72
+ }
73
+ }
74
+ return rkey;
75
+ }
76
+
77
+ static VALUE
78
+ str_to_value(ParseInfo pi, const char *str, size_t len, const char *orig) {
79
+ VALUE rstr = Qnil;
80
+
81
+ if (':' == *orig && 0 < len) {
82
+ rstr = rb_str_new(str + 1, len - 1);
83
+ #if HAS_ENCODING_SUPPORT
84
+ rb_enc_associate(rstr, oj_utf8_encoding);
85
+ #endif
86
+ rstr = rb_funcall(rstr, oj_to_sym_id, 0);
87
+ } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
88
+ long i = read_long(str + 2, len - 2);
89
+
90
+ if (0 > i) {
91
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a valid ID number");
92
+ return Qnil;
93
+ }
94
+ rstr = oj_circ_array_get(pi->circ_array, i);
95
+ } else {
96
+ rstr = rb_str_new(str, len);
97
+ #if HAS_ENCODING_SUPPORT
98
+ rb_enc_associate(rstr, oj_utf8_encoding);
99
+ #endif
100
+ }
101
+ return rstr;
102
+ }
103
+
104
+ static int
105
+ hat_cstr(ParseInfo pi, Val parent, const char *key, size_t klen, const char *str, size_t len) {
106
+ if (2 == klen) {
107
+ switch (key[1]) {
108
+ case 'o': // object
109
+ { // name2class sets and error if the class is not found or created
110
+ VALUE clas = oj_name2class(pi, str, len, Yes == pi->options.auto_define);
111
+
112
+ if (Qundef != clas) {
113
+ parent->val = rb_obj_alloc(clas);
114
+ }
115
+ }
116
+ break;
117
+ case 'O': // odd object
118
+ {
119
+ Odd odd = oj_get_oddc(str, len);
120
+
121
+ if (0 == odd) {
122
+ return 0;
123
+ }
124
+ parent->val = odd->clas;
125
+ parent->odd_args = oj_odd_alloc_args(odd);
126
+ }
127
+ break;
128
+ case 'm':
129
+ parent->val = rb_str_new(str + 1, len - 1);
130
+ #if HAS_ENCODING_SUPPORT
131
+ rb_enc_associate(parent->val, oj_utf8_encoding);
132
+ #endif
133
+ parent->val = rb_funcall(parent->val, oj_to_sym_id, 0);
134
+ break;
135
+ case 's':
136
+ parent->val = rb_str_new(str, len);
137
+ #if HAS_ENCODING_SUPPORT
138
+ rb_enc_associate(parent->val, oj_utf8_encoding);
139
+ #endif
140
+ break;
141
+ case 'c': // class
142
+ parent->val = oj_name2class(pi, str, len, Yes == pi->options.auto_define);
143
+ break;
144
+ default:
145
+ return 0;
146
+ break;
147
+ }
148
+ return 1; // handled
149
+ }
150
+ return 0;
151
+ }
152
+
153
+ static int
154
+ hat_num(ParseInfo pi, Val parent, const char *key, size_t klen, NumInfo ni) {
155
+ if (2 == klen) {
156
+ switch (key[1]) {
157
+ case 't': // time as a float
158
+ {
159
+ int64_t nsec = ni->num * 1000000000LL / ni->div;
160
+
161
+ if (ni->neg) {
162
+ ni->i = -ni->i;
163
+ if (0 < nsec) {
164
+ ni->i--;
165
+ nsec = 1000000000LL - nsec;
166
+ }
167
+ }
168
+ #if HAS_NANO_TIME
169
+ parent->val = rb_time_nano_new(ni->i, (long)nsec);
170
+ #else
171
+ parent->val = rb_time_new(ni->i, (long)(nsec / 1000));
172
+ #endif
173
+ }
174
+ break;
175
+ case 'i': // circular index
176
+ if (!ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
177
+ if (Qnil == parent->val) {
178
+ parent->val = rb_hash_new();
179
+ }
180
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
181
+ } else {
182
+ return 0;
183
+ }
184
+ break;
185
+ default:
186
+ return 0;
187
+ break;
188
+ }
189
+ return 1; // handled
190
+ }
191
+ return 0;
192
+ }
193
+
194
+ static int
195
+ hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, VALUE value) {
196
+ if (2 == klen && 'u' == key[1] && T_ARRAY == rb_type(value)) {
197
+ #if HAS_RSTRUCT
198
+ long len = RARRAY_LEN(value);
199
+ VALUE *a = RARRAY_PTR(value);
200
+ VALUE sc;
201
+ VALUE s;
202
+ VALUE *sv;
203
+
204
+ if (0 == len) {
205
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
206
+ return 1;
207
+ }
208
+ sc = rb_const_get(oj_struct_class, rb_to_id(*a));
209
+ //sc = rb_const_get(oj_struct_class, rb_intern_str(*a));
210
+ // use encoding as the indicator for Ruby 1.8.7 or 1.9.x
211
+ #if HAS_ENCODING_SUPPORT
212
+ s = rb_struct_alloc_noinit(sc);
213
+ #else
214
+ s = rb_struct_new(sc);
215
+ #endif
216
+ sv = RSTRUCT_PTR(s);
217
+ if (RSTRUCT_LEN(s) < len - 1) {
218
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Too many elements for Struct");
219
+ return 1;
220
+ }
221
+ for (a++; 0 < len; len--, a++, sv++) {
222
+ *sv = *a;
223
+ }
224
+ parent->val = s;
225
+ #else
226
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Ruby structs not supported with this version of Ruby");
227
+ #endif
228
+ return 1;
229
+ } else if (3 <= klen && '#' == key[1] && T_ARRAY == rb_type(value)) {
230
+ long len = RARRAY_LEN(value);
231
+ VALUE *a = RARRAY_PTR(value);
232
+
233
+ if (2 != len) {
234
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
235
+ return 1;
236
+ }
237
+ parent->val = rb_hash_new();
238
+ rb_hash_aset(parent->val, *a, a[1]);
239
+ return 1;
240
+ }
241
+ return 0;
242
+ }
243
+
244
+ static void
245
+ set_obj_ivar(Val parent, const char *key, size_t klen, VALUE value) {
246
+ ID var_id;
247
+ ID *slot;
248
+
249
+ if ('~' == *key && Qtrue == rb_obj_is_kind_of(parent->val, rb_eException)) {
250
+ if (5 == klen && 0 == strncmp("~mesg", key, klen)) {
251
+ VALUE args[1];
252
+
253
+ args[0] = value;
254
+ parent->val = rb_class_new_instance(1, args, rb_class_of(parent->val));
255
+ } else if (3 == klen && 0 == strncmp("~bt", key, klen)) {
256
+ rb_funcall(parent->val, rb_intern("set_backtrace"), 1, value);
257
+ }
258
+ }
259
+ #if SAFE_CACHE
260
+ pthread_mutex_lock(&oj_cache_mutex);
261
+ #endif
262
+ if (0 == (var_id = oj_attr_hash_get(key, klen, &slot))) {
263
+ char attr[256];
264
+
265
+ if (sizeof(attr) <= klen + 2) {
266
+ char *buf = ALLOC_N(char, klen + 2);
267
+
268
+ if ('~' == *key) {
269
+ strncpy(buf, key + 1, klen - 1);
270
+ buf[klen - 1] = '\0';
271
+ } else {
272
+ *buf = '@';
273
+ strncpy(buf + 1, key, klen);
274
+ buf[klen + 1] = '\0';
275
+ }
276
+ var_id = rb_intern(buf);
277
+ xfree(buf);
278
+ } else {
279
+ if ('~' == *key) {
280
+ strncpy(attr, key + 1, klen - 1);
281
+ attr[klen - 1] = '\0';
282
+ } else {
283
+ *attr = '@';
284
+ strncpy(attr + 1, key, klen);
285
+ attr[klen + 1] = '\0';
286
+ }
287
+ var_id = rb_intern(attr);
288
+ }
289
+ *slot = var_id;
290
+ }
291
+ #if SAFE_CACHE
292
+ pthread_mutex_unlock(&oj_cache_mutex);
293
+ #endif
294
+ rb_ivar_set(parent->val, var_id, value);
295
+ }
296
+
297
+ static void
298
+ hash_set_cstr(ParseInfo pi, const char *key, size_t klen, const char *str, size_t len, const char *orig) {
299
+ Val parent = stack_peek(&pi->stack);
300
+
301
+ WHICH_TYPE:
302
+ switch (rb_type(parent->val)) {
303
+ case T_NIL:
304
+ parent->odd_args = 0; // make sure it is 0 in case not odd
305
+ if ('^' != *key || !hat_cstr(pi, parent, key, klen, str, len)) {
306
+ parent->val = rb_hash_new();
307
+ goto WHICH_TYPE;
308
+ }
309
+ break;
310
+ case T_HASH:
311
+ rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), str_to_value(pi, str, len, orig));
312
+ break;
313
+ case T_OBJECT:
314
+ set_obj_ivar(parent, key, klen, str_to_value(pi, str, len, orig));
315
+ break;
316
+ case T_CLASS:
317
+ if (0 == parent->odd_args) {
318
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
319
+ return;
320
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, str_to_value(pi, str, len, orig))) {
321
+ char buf[256];
322
+
323
+ if (sizeof(buf) - 1 <= klen) {
324
+ klen = sizeof(buf) - 2;
325
+ }
326
+ memcpy(buf, key, klen);
327
+ buf[klen] = '\0';
328
+ 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)));
329
+ }
330
+ break;
331
+ default:
332
+ 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)));
333
+ return;
334
+ }
335
+ }
336
+
337
+ static void
338
+ hash_set_num(ParseInfo pi, const char *key, size_t klen, NumInfo ni) {
339
+ Val parent = stack_peek(&pi->stack);
340
+
341
+ WHICH_TYPE:
342
+ switch (rb_type(parent->val)) {
343
+ case T_NIL:
344
+ parent->odd_args = 0; // make sure it is 0 in case not odd
345
+ if ('^' != *key || !hat_num(pi, parent, key, klen, ni)) {
346
+ parent->val = rb_hash_new();
347
+ goto WHICH_TYPE;
348
+ }
349
+ break;
350
+ case T_HASH:
351
+ rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), oj_num_as_value(ni));
352
+ break;
353
+ case T_OBJECT:
354
+ if (2 == klen && '^' == *key && 'i' == key[1] &&
355
+ !ni->infinity && !ni->neg && 1 == ni->div && 0 == ni->exp && 0 != pi->circ_array) { // fixnum
356
+ oj_circ_array_set(pi->circ_array, parent->val, ni->i);
357
+ } else {
358
+ set_obj_ivar(parent, key, klen, oj_num_as_value(ni));
359
+ }
360
+ break;
361
+ case T_CLASS:
362
+ if (0 == parent->odd_args) {
363
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
364
+ return;
365
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, oj_num_as_value(ni))) {
366
+ char buf[256];
367
+
368
+ if (sizeof(buf) - 1 <= klen) {
369
+ klen = sizeof(buf) - 2;
370
+ }
371
+ memcpy(buf, key, klen);
372
+ buf[klen] = '\0';
373
+ 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)));
374
+ }
375
+ break;
376
+ default:
377
+ 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)));
378
+ return;
379
+ }
380
+ }
381
+
382
+ static void
383
+ hash_set_value(ParseInfo pi, const char *key, size_t klen, VALUE value) {
384
+ Val parent = stack_peek(&pi->stack);
385
+
386
+ WHICH_TYPE:
387
+ switch (rb_type(parent->val)) {
388
+ case T_NIL:
389
+ parent->odd_args = 0; // make sure it is 0 in case not odd
390
+ if ('^' != *key || !hat_value(pi, parent, key, klen, value)) {
391
+ parent->val = rb_hash_new();
392
+ goto WHICH_TYPE;
393
+ }
394
+ break;
395
+ case T_HASH:
396
+ if (3 <= klen && '#' == key[1] && T_ARRAY == rb_type(value)) {
397
+ long len = RARRAY_LEN(value);
398
+ VALUE *a = RARRAY_PTR(value);
399
+
400
+ if (2 != len) {
401
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
402
+ return;
403
+ }
404
+ rb_hash_aset(parent->val, *a, a[1]);
405
+ } else {
406
+ rb_hash_aset(parent->val, hash_key(pi, key, klen, parent->k1), value);
407
+ }
408
+ break;
409
+ case T_OBJECT:
410
+ set_obj_ivar(parent, key, klen, value);
411
+ break;
412
+ case T_CLASS:
413
+ if (0 == parent->odd_args) {
414
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "%s is not an odd class", rb_class2name(rb_obj_class(parent->val)));
415
+ return;
416
+ } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
417
+ char buf[256];
418
+
419
+ if (sizeof(buf) - 1 <= klen) {
420
+ klen = sizeof(buf) - 2;
421
+ }
422
+ memcpy(buf, key, klen);
423
+ buf[klen] = '\0';
424
+ 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)));
425
+ }
426
+ break;
427
+ default:
428
+ 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)));
429
+ return;
430
+ }
431
+ }
432
+
433
+
434
+ static VALUE
435
+ start_hash(ParseInfo pi) {
436
+ return Qnil;
437
+ }
438
+
439
+ static void
440
+ end_hash(struct _ParseInfo *pi) {
441
+ Val parent = stack_peek(&pi->stack);
442
+
443
+ if (Qnil == parent->val) {
444
+ parent->val = rb_hash_new();
445
+ } else if (0 != parent->odd_args) {
446
+ OddArgs oa = parent->odd_args;
447
+
448
+ parent->val = rb_funcall2(oa->odd->create_obj, oa->odd->create_op, oa->odd->attr_cnt, oa->args);
449
+ oj_odd_free(oa);
450
+ parent->odd_args = 0;
451
+ }
452
+ }
453
+
454
+ static void
455
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
456
+ if (3 <= len && 0 != pi->circ_array) {
457
+ if ('i' == str[1]) {
458
+ long i = read_long(str + 2, len - 2);
459
+
460
+ if (0 < i) {
461
+ oj_circ_array_set(pi->circ_array, stack_peek(&pi->stack)->val, i);
462
+ return;
463
+ }
464
+ } else if ('r' == str[1]) {
465
+ long i = read_long(str + 2, len - 2);
466
+
467
+ if (0 < i) {
468
+ rb_ary_push(stack_peek(&pi->stack)->val, oj_circ_array_get(pi->circ_array, i));
469
+ return;
470
+ }
471
+
472
+ }
473
+ }
474
+ rb_ary_push(stack_peek(&pi->stack)->val, str_to_value(pi, str, len, orig));
475
+ }
476
+
477
+ static void
478
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
479
+ pi->stack.head->val = str_to_value(pi, str, len, orig);
480
+ }
481
+
482
+ VALUE
483
+ oj_object_parse(int argc, VALUE *argv, VALUE self) {
484
+ struct _ParseInfo pi;
485
+
486
+ pi.options = oj_default_options;
487
+ oj_set_strict_callbacks(&pi);
488
+ pi.end_hash = end_hash;
489
+ pi.start_hash = start_hash;
490
+ pi.hash_set_cstr = hash_set_cstr;
491
+ pi.hash_set_num = hash_set_num;
492
+ pi.hash_set_value = hash_set_value;
493
+ pi.add_cstr = add_cstr;
494
+ pi.array_append_cstr = array_append_cstr;
495
+
496
+ return oj_pi_parse(argc, argv, &pi, 0);
497
+ }
498
+
499
+ VALUE
500
+ oj_object_parse_cstr(int argc, VALUE *argv, char *json) {
501
+ struct _ParseInfo pi;
502
+
503
+ pi.options = oj_default_options;
504
+ oj_set_strict_callbacks(&pi);
505
+ pi.end_hash = end_hash;
506
+ pi.start_hash = start_hash;
507
+ pi.hash_set_cstr = hash_set_cstr;
508
+ pi.hash_set_num = hash_set_num;
509
+ pi.hash_set_value = hash_set_value;
510
+ pi.add_cstr = add_cstr;
511
+ pi.array_append_cstr = array_append_cstr;
512
+
513
+ return oj_pi_parse(argc, argv, &pi, json);
514
+ }