oj 0.6.0 → 0.7.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.

@@ -36,6 +36,10 @@
36
36
  #include "ruby.h"
37
37
  #include "oj.h"
38
38
 
39
+ enum {
40
+ TIME_HINT = 0x0100,
41
+ };
42
+
39
43
  typedef struct _ParseInfo {
40
44
  char *str; /* buffer being read from */
41
45
  char *s; /* current position in buffer */
@@ -47,11 +51,13 @@ typedef struct _ParseInfo {
47
51
  Options options;
48
52
  } *ParseInfo;
49
53
 
50
- static VALUE read_next(ParseInfo pi);
54
+ static VALUE classname2class(const char *name, ParseInfo pi);
55
+ static VALUE read_next(ParseInfo pi, int hint);
51
56
  static VALUE read_obj(ParseInfo pi);
52
57
  static VALUE read_array(ParseInfo pi);
53
- static VALUE read_str(ParseInfo pi);
58
+ static VALUE read_str(ParseInfo pi, int hint);
54
59
  static VALUE read_num(ParseInfo pi);
60
+ static VALUE read_time(ParseInfo pi);
55
61
  static VALUE read_true(ParseInfo pi);
56
62
  static VALUE read_false(ParseInfo pi);
57
63
  static VALUE read_nil(ParseInfo pi);
@@ -105,6 +111,86 @@ next_white(ParseInfo pi) {
105
111
  }
106
112
  }
107
113
 
114
+ inline static VALUE
115
+ resolve_classname(VALUE mod, const char *class_name, int create) {
116
+ VALUE clas;
117
+ ID ci = rb_intern(class_name);
118
+
119
+ if (rb_const_defined_at(mod, ci) || !create) {
120
+ clas = rb_const_get_at(mod, ci);
121
+ } else {
122
+ //clas = rb_define_class_under(mod, class_name, oj_bag_clas);
123
+ clas = rb_const_get_at(mod, ci); // TBD temp
124
+ }
125
+ return clas;
126
+ }
127
+
128
+ inline static VALUE
129
+ classname2obj(const char *name, ParseInfo pi) {
130
+ VALUE clas = classname2class(name, pi);
131
+
132
+ if (Qundef == clas) {
133
+ return Qnil;
134
+ } else {
135
+ return rb_obj_alloc(clas);
136
+ }
137
+ }
138
+
139
+ static VALUE
140
+ classname2class(const char *name, ParseInfo pi) {
141
+ VALUE clas;
142
+ int create = 0; // TBD from options
143
+
144
+ #if 1
145
+ VALUE *slot;
146
+
147
+ if (Qundef == (clas = oj_cache_get(oj_class_cache, name, &slot))) {
148
+ char class_name[1024];
149
+ char *s;
150
+ const char *n = name;
151
+
152
+ clas = rb_cObject;
153
+ for (s = class_name; '\0' != *n; n++) {
154
+ if (':' == *n) {
155
+ *s = '\0';
156
+ n++;
157
+ if (Qundef == (clas = resolve_classname(clas, class_name, create))) {
158
+ return Qundef;
159
+ }
160
+ s = class_name;
161
+ } else {
162
+ *s++ = *n;
163
+ }
164
+ }
165
+ *s = '\0';
166
+ if (Qundef != (clas = resolve_classname(clas, class_name, create))) {
167
+ *slot = clas;
168
+ }
169
+ }
170
+ #else
171
+ char class_name[1024];
172
+ char *s;
173
+ const char *n = name;
174
+
175
+ clas = rb_cObject;
176
+ for (s = class_name; '\0' != *n; n++) {
177
+ if (':' == *n) {
178
+ *s = '\0';
179
+ n++;
180
+ if (Qundef == (clas = resolve_classname(clas, class_name, create))) {
181
+ return Qundef;
182
+ }
183
+ s = class_name;
184
+ } else {
185
+ *s++ = *n;
186
+ }
187
+ }
188
+ *s = '\0';
189
+ clas = resolve_classname(clas, class_name, create);
190
+ #endif
191
+ return clas;
192
+ }
193
+
108
194
  VALUE
109
195
  oj_parse(char *json, Options options) {
110
196
  VALUE obj;
@@ -122,7 +208,7 @@ oj_parse(char *json, Options options) {
122
208
  pi.encoding = 0;
123
209
  #endif
124
210
  pi.options = options;
125
- if (Qundef == (obj = read_next(&pi))) {
211
+ if (Qundef == (obj = read_next(&pi, 0))) {
126
212
  raise_error("no object read", pi.str, pi.s);
127
213
  }
128
214
  next_non_white(&pi); // skip white space
@@ -133,7 +219,7 @@ oj_parse(char *json, Options options) {
133
219
  }
134
220
 
135
221
  static VALUE
136
- read_next(ParseInfo pi) {
222
+ read_next(ParseInfo pi, int hint) {
137
223
  VALUE obj;
138
224
 
139
225
  next_non_white(pi); // skip white space
@@ -145,7 +231,7 @@ read_next(ParseInfo pi) {
145
231
  obj = read_array(pi);
146
232
  break;
147
233
  case '"':
148
- obj = read_str(pi);
234
+ obj = read_str(pi, hint);
149
235
  break;
150
236
  case '+':
151
237
  case '-':
@@ -159,7 +245,11 @@ read_next(ParseInfo pi) {
159
245
  case '7':
160
246
  case '8':
161
247
  case '9':
162
- obj = read_num(pi);
248
+ if (TIME_HINT == hint) {
249
+ obj = read_time(pi);
250
+ } else {
251
+ obj = read_num(pi);
252
+ }
163
253
  break;
164
254
  case 't':
165
255
  obj = read_true(pi);
@@ -185,6 +275,8 @@ read_obj(ParseInfo pi) {
185
275
  VALUE obj = Qundef;
186
276
  VALUE key = Qundef;
187
277
  VALUE val = Qundef;
278
+ const char *ks;
279
+ int obj_type = T_NONE;
188
280
 
189
281
  pi->s++;
190
282
  next_non_white(pi);
@@ -194,7 +286,10 @@ read_obj(ParseInfo pi) {
194
286
  }
195
287
  while (1) {
196
288
  next_non_white(pi);
197
- if ('"' != *pi->s || Qundef == (key = read_str(pi))) {
289
+ ks = 0;
290
+ key = Qundef;
291
+ val = Qundef;
292
+ if ('"' != *pi->s || Qundef == (key = read_str(pi, 0))) {
198
293
  raise_error("unexpected character", pi->str, pi->s);
199
294
  }
200
295
  next_non_white(pi);
@@ -203,13 +298,80 @@ read_obj(ParseInfo pi) {
203
298
  } else {
204
299
  raise_error("invalid format, expected :", pi->str, pi->s);
205
300
  }
206
- if (Qundef == (val = read_next(pi))) {
207
- raise_error("unexpected character", pi->str, pi->s);
301
+ if (T_STRING == rb_type(key)) {
302
+ ks = StringValuePtr(key);
303
+ } else {
304
+ ks = 0;
208
305
  }
209
- if (Qundef == obj) {
210
- obj = rb_hash_new();
306
+ if (0 != ks && Qundef == obj && ObjectMode == pi->options->mode) {
307
+ if ('^' == *ks && '\0' == ks[2]) { // special directions
308
+ switch (ks[1]) {
309
+ case 't': // Time
310
+ obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
311
+ key = Qundef;
312
+ break;
313
+ case 'c': // Class
314
+ obj = read_next(pi, T_CLASS);
315
+ key = Qundef;
316
+ break;
317
+ case 's': // Symbol
318
+ obj = read_next(pi, T_SYMBOL);
319
+ key = Qundef;
320
+ break;
321
+ case 'o': // Object
322
+ obj = read_next(pi, T_OBJECT);
323
+ obj_type = T_OBJECT;
324
+ key = Qundef;
325
+ break;
326
+ case 'i': // Id for circular reference
327
+ // TBD
328
+ default:
329
+ // handle later
330
+ break;
331
+ }
332
+ }
333
+ }
334
+ if (Qundef != key) {
335
+ if (Qundef == val && Qundef == (val = read_next(pi, 0))) {
336
+ raise_error("unexpected character", pi->str, pi->s);
337
+ }
338
+ if (ObjectMode == pi->options->mode &&
339
+ 0 != ks && '^' == *ks && '#' == ks[1] &&
340
+ (T_NONE == obj_type || T_HASH == obj_type) &&
341
+ rb_type(val) == T_ARRAY && 2 == RARRAY_LEN(val)) { // Hash entry
342
+ VALUE *np = RARRAY_PTR(val);
343
+
344
+ key = *np;
345
+ val = *(np + 1);
346
+ }
347
+ if (Qundef == obj) {
348
+ obj = rb_hash_new();
349
+ obj_type = T_HASH;
350
+ }
351
+ if (T_OBJECT == obj_type) {
352
+ VALUE *slot;
353
+ ID var_id;
354
+
355
+ if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
356
+ char attr[1024];
357
+
358
+ if ('~' == *ks) {
359
+ strncpy(attr, ks + 1, sizeof(attr) - 1);
360
+ } else {
361
+ *attr = '@';
362
+ strncpy(attr + 1, ks, sizeof(attr) - 2);
363
+ }
364
+ attr[sizeof(attr) - 1] = '\0';
365
+ var_id = rb_intern(attr);
366
+ *slot = var_id;
367
+ }
368
+ rb_ivar_set(obj, var_id, val);
369
+ } else if (T_HASH == obj_type) {
370
+ rb_hash_aset(obj, key, val);
371
+ } else {
372
+ raise_error("invalid Object format, too many Hash entries.", pi->str, pi->s);
373
+ }
211
374
  }
212
- rb_hash_aset(obj, key, val);
213
375
  next_non_white(pi);
214
376
  if ('}' == *pi->s) {
215
377
  pi->s++;
@@ -217,6 +379,7 @@ read_obj(ParseInfo pi) {
217
379
  } else if (',' == *pi->s) {
218
380
  pi->s++;
219
381
  } else {
382
+ //printf("*** '%s'\n", pi->s);
220
383
  raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
221
384
  }
222
385
  }
@@ -235,7 +398,7 @@ read_array(ParseInfo pi) {
235
398
  return a;
236
399
  }
237
400
  while (1) {
238
- if (Qundef == (e = read_next(pi))) {
401
+ if (Qundef == (e = read_next(pi, 0))) {
239
402
  raise_error("unexpected character", pi->str, pi->s);
240
403
  }
241
404
  rb_ary_push(a, e);
@@ -253,16 +416,75 @@ read_array(ParseInfo pi) {
253
416
  }
254
417
 
255
418
  static VALUE
256
- read_str(ParseInfo pi) {
419
+ read_str(ParseInfo pi, int hint) {
257
420
  char *text = read_quoted_value(pi);
258
- VALUE s = rb_str_new2(text);
421
+ VALUE obj;
259
422
 
260
- #ifdef HAVE_RUBY_ENCODING_H
261
- if (0 != pi->encoding) {
262
- rb_enc_associate(s, pi->encoding);
423
+ if (ObjectMode != pi->options->mode) {
424
+ hint = T_STRING;
263
425
  }
426
+ switch (hint) {
427
+ case T_CLASS:
428
+ obj = classname2class(text, pi);
429
+ break;
430
+ case T_OBJECT:
431
+ obj = classname2obj(text, pi);
432
+ break;
433
+ case T_STRING:
434
+ obj = rb_str_new2(text);
435
+ #ifdef HAVE_RUBY_ENCODING_H
436
+ if (0 != pi->encoding) {
437
+ rb_enc_associate(obj, pi->encoding);
438
+ }
439
+ #endif
440
+ break;
441
+ case T_SYMBOL:
442
+ #ifdef HAVE_RUBY_ENCODING_H
443
+ if (0 != pi->encoding) {
444
+ obj = rb_str_new2(text);
445
+ rb_enc_associate(obj, pi->encoding);
446
+ obj = rb_funcall(obj, oj_to_sym_id, 0);
447
+ } else {
448
+ obj = ID2SYM(rb_intern(text));
449
+ }
450
+ #else
451
+ obj = ID2SYM(rb_intern(text));
264
452
  #endif
265
- return s;
453
+ break;
454
+ case 0:
455
+ default:
456
+ if (':' == *text) {
457
+ if (':' == text[1]) { // escaped :, it s string
458
+ obj = rb_str_new2(text + 1);
459
+ #ifdef HAVE_RUBY_ENCODING_H
460
+ if (0 != pi->encoding) {
461
+ rb_enc_associate(obj, pi->encoding);
462
+ }
463
+ #endif
464
+ } else { // Symbol
465
+ #ifdef HAVE_RUBY_ENCODING_H
466
+ if (0 != pi->encoding) {
467
+ obj = rb_str_new2(text + 1);
468
+ rb_enc_associate(obj, pi->encoding);
469
+ obj = rb_funcall(obj, oj_to_sym_id, 0);
470
+ } else {
471
+ obj = ID2SYM(rb_intern(text + 1));
472
+ }
473
+ #else
474
+ obj = ID2SYM(rb_intern(text + 1));
475
+ #endif
476
+ }
477
+ } else {
478
+ obj = rb_str_new2(text);
479
+ #ifdef HAVE_RUBY_ENCODING_H
480
+ if (0 != pi->encoding) {
481
+ rb_enc_associate(obj, pi->encoding);
482
+ }
483
+ #endif
484
+ }
485
+ break;
486
+ }
487
+ return obj;
266
488
  }
267
489
 
268
490
  #ifdef RUBINIUS
@@ -344,6 +566,27 @@ read_num(ParseInfo pi) {
344
566
  }
345
567
  }
346
568
 
569
+ static VALUE
570
+ read_time(ParseInfo pi) {
571
+ VALUE args[2];
572
+ long v = 0;
573
+ long v2 = 0;
574
+
575
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
576
+ v = v * 10 + (*pi->s - '0');
577
+ }
578
+ if ('.' == *pi->s) {
579
+ pi->s++;
580
+ for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
581
+ v2 = v2 * 10 + (*pi->s - '0');
582
+ }
583
+ }
584
+ args[0] = LONG2NUM(v);
585
+ args[1] = LONG2NUM(v2);
586
+
587
+ return rb_funcall2(oj_time_class, oj_at_id, 2, args);
588
+ }
589
+
347
590
  static VALUE
348
591
  read_true(ParseInfo pi) {
349
592
  pi->s++;
@@ -45,12 +45,17 @@ void Init_oj();
45
45
 
46
46
  VALUE Oj = Qnil;
47
47
 
48
+ ID oj_at_id;
48
49
  ID oj_instance_variables_id;
49
50
  ID oj_to_hash_id;
50
51
  ID oj_to_json_id;
52
+ ID oj_to_sym_id;
53
+ ID oj_tv_nsec_id;
51
54
  ID oj_tv_sec_id;
52
55
  ID oj_tv_usec_id;
53
56
 
57
+ VALUE oj_time_class;
58
+
54
59
  static VALUE circular_sym;
55
60
  static VALUE compat_sym;
56
61
  static VALUE encoding_sym;
@@ -60,6 +65,9 @@ static VALUE null_sym;
60
65
  static VALUE object_sym;
61
66
  static VALUE strict_sym;
62
67
 
68
+ Cache oj_class_cache = 0;
69
+ Cache oj_attr_cache = 0;
70
+
63
71
  static struct _Options default_options = {
64
72
  { '\0' }, // encoding
65
73
  0, // indent
@@ -312,6 +320,29 @@ dump(int argc, VALUE *argv, VALUE self) {
312
320
  return rstr;
313
321
  }
314
322
 
323
+
324
+ /* call-seq: to_file(file_path, obj, options)
325
+ *
326
+ * Dumps an Object to the specified file.
327
+ * @param [String] file_path file path to write the JSON document to
328
+ * @param [Object] obj Object to serialize as an JSON document String
329
+ * @param [Hash] options formating options
330
+ * @param [Fixnum] :indent format expected
331
+ * @param [true|false] :circular allow circular references, default: false
332
+ */
333
+ static VALUE
334
+ to_file(int argc, VALUE *argv, VALUE self) {
335
+ struct _Options copts = default_options;
336
+
337
+ if (3 == argc) {
338
+ parse_options(argv[2], &copts);
339
+ }
340
+ Check_Type(*argv, T_STRING);
341
+ oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
342
+
343
+ return Qnil;
344
+ }
345
+
315
346
  void Init_oj() {
316
347
  VALUE keep = Qnil;
317
348
 
@@ -324,13 +355,19 @@ void Init_oj() {
324
355
  rb_define_module_function(Oj, "load", load_str, -1);
325
356
  rb_define_module_function(Oj, "load_file", load_file, -1);
326
357
  rb_define_module_function(Oj, "dump", dump, -1);
358
+ rb_define_module_function(Oj, "to_file", to_file, -1);
327
359
 
360
+ oj_at_id = rb_intern("at");
328
361
  oj_instance_variables_id = rb_intern("instance_variables");
329
362
  oj_to_hash_id = rb_intern("to_hash");
330
363
  oj_to_json_id = rb_intern("to_json");
364
+ oj_to_sym_id = rb_intern("to_sym");
365
+ oj_tv_nsec_id = rb_intern("tv_nsec");
331
366
  oj_tv_sec_id = rb_intern("tv_sec");
332
367
  oj_tv_usec_id = rb_intern("tv_usec");
333
368
 
369
+ oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
370
+
334
371
  circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
335
372
  compat_sym = ID2SYM(rb_intern("compat")); rb_ary_push(keep, compat_sym);
336
373
  encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
@@ -341,6 +378,9 @@ void Init_oj() {
341
378
  strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
342
379
 
343
380
  default_options.mode = ObjectMode;
381
+
382
+ oj_cache_new(&oj_class_cache);
383
+ oj_cache_new(&oj_attr_cache);
344
384
  }
345
385
 
346
386
  void