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.
- data/README.md +146 -86
- data/ext/oj/cache.c +148 -0
- data/ext/oj/cache.h +44 -0
- data/ext/oj/dump.c +308 -154
- data/ext/oj/load.c +262 -19
- data/ext/oj/oj.c +40 -0
- data/ext/oj/oj.h +10 -0
- data/lib/oj/version.rb +1 -1
- data/test/files.rb +29 -0
- data/test/perf.rb +106 -0
- data/test/perf_obj.rb +124 -0
- data/test/perf_strict.rb +131 -0
- data/test/sample.rb +40 -22
- data/test/sample/change.rb +14 -0
- data/test/sample/dir.rb +19 -0
- data/test/sample/doc.rb +36 -0
- data/test/sample/file.rb +48 -0
- data/test/sample/group.rb +16 -0
- data/test/sample/hasprops.rb +16 -0
- data/test/sample/layer.rb +12 -0
- data/test/sample/line.rb +20 -0
- data/test/sample/oval.rb +10 -0
- data/test/sample/rect.rb +10 -0
- data/test/sample/shape.rb +35 -0
- data/test/sample/text.rb +20 -0
- data/test/sample_json.rb +37 -0
- data/test/tests.rb +359 -0
- metadata +23 -3
- data/test/simple.rb +0 -208
data/ext/oj/load.c
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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 (
|
207
|
-
|
301
|
+
if (T_STRING == rb_type(key)) {
|
302
|
+
ks = StringValuePtr(key);
|
303
|
+
} else {
|
304
|
+
ks = 0;
|
208
305
|
}
|
209
|
-
if (Qundef == obj) {
|
210
|
-
|
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
|
421
|
+
VALUE obj;
|
259
422
|
|
260
|
-
|
261
|
-
|
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
|
-
|
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++;
|
data/ext/oj/oj.c
CHANGED
@@ -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
|