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,159 @@
1
+ /* odd.c
2
+ * Copyright (c) 2011, 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 "odd.h"
32
+
33
+ struct _Odd odds[5]; // bump up if new Odd classes are added
34
+
35
+ static void
36
+ set_class(Odd odd, const char *classname) {
37
+ const char **np;
38
+ ID *idp;
39
+
40
+ odd->classname = classname;
41
+ odd->clen = strlen(classname);
42
+ odd->clas = rb_const_get(rb_cObject, rb_intern(classname));
43
+ odd->create_obj = odd->clas;
44
+ odd->create_op = rb_intern("new");
45
+ for (np = odd->attr_names, idp = odd->attrs; 0 != *np; np++, idp++) {
46
+ *idp = rb_intern(*np);
47
+ }
48
+ *idp = 0;
49
+ }
50
+
51
+ void
52
+ oj_odd_init() {
53
+ Odd odd;
54
+ const char **np;
55
+
56
+ odd = odds;
57
+ // Rational
58
+ np = odd->attr_names;
59
+ *np++ = "numerator";
60
+ *np++ = "denominator";
61
+ *np = 0;
62
+ set_class(odd, "Rational");
63
+ odd->create_obj = rb_cObject;
64
+ odd->create_op = rb_intern("Rational");
65
+ odd->attr_cnt = 2;
66
+ // Date
67
+ odd++;
68
+ np = odd->attr_names;
69
+ *np++ = "year";
70
+ *np++ = "month";
71
+ *np++ = "day";
72
+ *np++ = "start";
73
+ *np++ = 0;
74
+ set_class(odd, "Date");
75
+ odd->attr_cnt = 4;
76
+ // DateTime
77
+ odd++;
78
+ np = odd->attr_names;
79
+ *np++ = "year";
80
+ *np++ = "month";
81
+ *np++ = "day";
82
+ *np++ = "hour";
83
+ *np++ = "min";
84
+ *np++ = "sec";
85
+ *np++ = "offset";
86
+ *np++ = "start";
87
+ *np++ = 0;
88
+ set_class(odd, "DateTime");
89
+ odd->attr_cnt = 8;
90
+ // Range
91
+ odd++;
92
+ np = odd->attr_names;
93
+ *np++ = "begin";
94
+ *np++ = "end";
95
+ *np++ = "exclude_end?";
96
+ *np++ = 0;
97
+ set_class(odd, "Range");
98
+ odd->attr_cnt = 3;
99
+ // The end. bump up the size of odds if a new class is added.
100
+ odd++;
101
+ odd->clas = Qundef;
102
+ }
103
+
104
+ Odd
105
+ oj_get_odd(VALUE clas) {
106
+ Odd odd = odds;
107
+
108
+ for (; Qundef != odd->clas; odd++) {
109
+ if (clas == odd->clas) {
110
+ return odd;
111
+ }
112
+ }
113
+ return 0;
114
+ }
115
+
116
+ Odd
117
+ oj_get_oddc(const char *classname, size_t len) {
118
+ Odd odd = odds;
119
+
120
+ for (; Qundef != odd->clas; odd++) {
121
+ if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
122
+ return odd;
123
+ }
124
+ }
125
+ return 0;
126
+ }
127
+
128
+ OddArgs
129
+ oj_odd_alloc_args(Odd odd) {
130
+ OddArgs oa = ALLOC_N(struct _OddArgs, 1);
131
+ VALUE *a;
132
+ int i;
133
+
134
+ oa->odd = odd;
135
+ for (i = odd->attr_cnt, a = oa->args; 0 < i; i--, a++) {
136
+ *a = Qnil;
137
+ }
138
+ return oa;
139
+ }
140
+
141
+ void
142
+ oj_odd_free(OddArgs args) {
143
+ xfree(args);
144
+ }
145
+
146
+ int
147
+ oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
148
+ const char **np;
149
+ VALUE *vp;
150
+ int i;
151
+
152
+ for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i; i--, np++, vp++) {
153
+ if (0 == strncmp(key, *np, klen) && '\0' == *((*np) + klen)) {
154
+ *vp = value;
155
+ return 0;
156
+ }
157
+ }
158
+ return -1;
159
+ }
@@ -0,0 +1,61 @@
1
+ /* odd.h
2
+ * Copyright (c) 2011, 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
+ #ifndef __OJ_ODD_H__
32
+ #define __OJ_ODD_H__
33
+
34
+ #include "ruby.h"
35
+
36
+ #define MAX_ODD_ARGS 10
37
+
38
+ typedef struct _Odd {
39
+ const char *classname;
40
+ size_t clen;
41
+ VALUE clas; // Ruby class
42
+ VALUE create_obj;
43
+ ID create_op;
44
+ int attr_cnt;
45
+ const char *attr_names[MAX_ODD_ARGS]; // 0 terminated attr IDs
46
+ ID attrs[MAX_ODD_ARGS]; // 0 terminated attr IDs
47
+ } *Odd;
48
+
49
+ typedef struct _OddArgs {
50
+ Odd odd;
51
+ VALUE args[MAX_ODD_ARGS];
52
+ } *OddArgs;
53
+
54
+ extern void oj_odd_init(void);
55
+ extern Odd oj_get_odd(VALUE clas);
56
+ extern Odd oj_get_oddc(const char *classname, size_t len);
57
+ extern OddArgs oj_odd_alloc_args(Odd odd);
58
+ extern void oj_odd_free(OddArgs args);
59
+ extern int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value);
60
+
61
+ #endif /* __OJ_ODD_H__ */
@@ -37,6 +37,9 @@
37
37
  #include <unistd.h>
38
38
 
39
39
  #include "oj.h"
40
+ #include "parse.h"
41
+ #include "hash.h"
42
+ #include "odd.h"
40
43
 
41
44
  typedef struct _YesNoOpt {
42
45
  VALUE sym;
@@ -48,12 +51,14 @@ void Init_oj();
48
51
  VALUE Oj = Qnil;
49
52
 
50
53
  ID oj_add_value_id;
54
+ ID oj_array_append_id;
51
55
  ID oj_array_end_id;
52
56
  ID oj_array_start_id;
53
57
  ID oj_as_json_id;
54
58
  ID oj_error_id;
55
59
  ID oj_fileno_id;
56
60
  ID oj_hash_end_id;
61
+ ID oj_hash_set_id;
57
62
  ID oj_hash_start_id;
58
63
  ID oj_instance_variables_id;
59
64
  ID oj_json_create_id;
@@ -91,7 +96,6 @@ static VALUE class_cache_sym;
91
96
  static VALUE compat_sym;
92
97
  static VALUE create_id_sym;
93
98
  static VALUE indent_sym;
94
- static VALUE max_stack_sym;
95
99
  static VALUE mode_sym;
96
100
  static VALUE null_sym;
97
101
  static VALUE object_sym;
@@ -112,9 +116,6 @@ static VALUE symbolize_names_sym;
112
116
 
113
117
  static VALUE mimic = Qnil;
114
118
 
115
- Cache oj_class_cache = 0;
116
- Cache oj_attr_cache = 0;
117
-
118
119
  #if HAS_ENCODING_SUPPORT
119
120
  rb_encoding *oj_utf8_encoding = 0;
120
121
  #endif
@@ -136,25 +137,12 @@ struct _Options oj_default_options = {
136
137
  Yes, // bigdec_as_num
137
138
  No, // bigdec_load
138
139
  json_class, // create_id
139
- 65536, // max_stack
140
+ 10, // create_id_len
140
141
  9, // sec_prec
141
142
  0, // dump_opts
142
143
  };
143
144
 
144
145
  static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
145
- static struct _Odd odds[5]; // bump up if new Odd classes are added
146
-
147
- Odd
148
- oj_get_odd(VALUE clas) {
149
- Odd odd = odds;
150
-
151
- for (; Qundef != odd->clas; odd++) {
152
- if (clas == odd->clas) {
153
- return odd;
154
- }
155
- }
156
- return 0;
157
- }
158
146
 
159
147
  /* call-seq: default_options() => Hash
160
148
  *
@@ -169,7 +157,6 @@ oj_get_odd(VALUE clas) {
169
157
  * - bigdecimal_as_decimal: [true|false|nil] dump BigDecimal as a decimal number or as a String
170
158
  * - bigdecimal_load: [true|false|nil] load decimals as BigDecimal instead of as a Float
171
159
  * - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
172
- * - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
173
160
  * - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
174
161
  * @return [Hash] all current option settings.
175
162
  */
@@ -179,7 +166,6 @@ get_def_opts(VALUE self) {
179
166
 
180
167
  rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
181
168
  rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
182
- rb_hash_aset(opts, max_stack_sym, INT2FIX(oj_default_options.max_stack));
183
169
  rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
184
170
  rb_hash_aset(opts, class_cache_sym, (Yes == oj_default_options.class_cache) ? Qtrue : ((No == oj_default_options.class_cache) ? Qfalse : Qnil));
185
171
  rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
@@ -230,7 +216,6 @@ get_def_opts(VALUE self) {
230
216
  * :xmlschema date-time format taken from XML Schema as a String,
231
217
  * :ruby Time.to_s formatted String
232
218
  * @param [String|nil] :create_id create id for json compatible object encoding
233
- * @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String
234
219
  * @param [Fixnum|nil] :second_precision number of digits after the decimal when dumping the seconds portion of time
235
220
  * @return [nil]
236
221
  */
@@ -268,17 +253,6 @@ set_def_opts(VALUE self, VALUE opts) {
268
253
  }
269
254
  oj_default_options.sec_prec = n;
270
255
  }
271
- v = rb_hash_aref(opts, max_stack_sym);
272
- if (Qnil != v) {
273
- int i;
274
-
275
- Check_Type(v, T_FIXNUM);
276
- i = FIX2INT(v);
277
- if (0 > i) {
278
- i = 0;
279
- }
280
- oj_default_options.max_stack = (size_t)i;
281
- }
282
256
 
283
257
  v = rb_hash_lookup(opts, mode_sym);
284
258
  if (Qnil == v) {
@@ -314,6 +288,7 @@ set_def_opts(VALUE self, VALUE opts) {
314
288
  xfree((char*)oj_default_options.create_id);
315
289
  }
316
290
  oj_default_options.create_id = 0;
291
+ oj_default_options.create_id_len = 0;
317
292
  }
318
293
  v = rb_hash_lookup(opts, create_id_sym);
319
294
  if (Qnil != v) {
@@ -321,6 +296,7 @@ set_def_opts(VALUE self, VALUE opts) {
321
296
 
322
297
  oj_default_options.create_id = ALLOC_N(char, len);
323
298
  strcpy((char*)oj_default_options.create_id, StringValuePtr(v));
299
+ oj_default_options.create_id_len = len - 1;
324
300
  }
325
301
  }
326
302
 
@@ -341,8 +317,8 @@ set_def_opts(VALUE self, VALUE opts) {
341
317
  return Qnil;
342
318
  }
343
319
 
344
- static void
345
- parse_options(VALUE ropts, Options copts) {
320
+ void
321
+ oj_parse_options(VALUE ropts, Options copts) {
346
322
  struct _YesNoOpt ynos[] = {
347
323
  { circular_sym, &copts->circular },
348
324
  { auto_define_sym, &copts->auto_define },
@@ -402,6 +378,28 @@ parse_options(VALUE ropts, Options copts) {
402
378
  rb_raise(rb_eArgError, ":time_format must be :unix, :xmlschema, or :ruby.");
403
379
  }
404
380
  }
381
+ if (Qtrue == rb_funcall(ropts, rb_intern("has_key?"), 1, create_id_sym)) {
382
+ v = rb_hash_lookup(ropts, create_id_sym);
383
+ if (Qnil == v) {
384
+ if (json_class != oj_default_options.create_id) {
385
+ xfree((char*)oj_default_options.create_id);
386
+ }
387
+ copts->create_id = 0;
388
+ copts->create_id_len = 0;
389
+ } else if (T_STRING == rb_type(v)) {
390
+ size_t len = RSTRING_LEN(v);
391
+ const char *str = StringValuePtr(v);
392
+
393
+ if (len != copts->create_id_len ||
394
+ 0 != strcmp(copts->create_id, str)) {
395
+ copts->create_id = ALLOC_N(char, len + 1);
396
+ strcpy((char*)copts->create_id, str);
397
+ copts->create_id_len = len;
398
+ }
399
+ } else {
400
+ rb_raise(rb_eArgError, ":create_id must be string.");
401
+ }
402
+ }
405
403
  for (o = ynos; 0 != o->attr; o++) {
406
404
  if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
407
405
  if (Qtrue == v) {
@@ -416,105 +414,118 @@ parse_options(VALUE ropts, Options copts) {
416
414
  }
417
415
  }
418
416
 
419
- static VALUE
420
- load_with_opts(VALUE input, Options copts) {
421
- char *json = 0;
422
- size_t len = 0;
423
- VALUE obj;
424
-
425
- if (rb_type(input) == T_STRING) {
426
- // the json string gets modified so make a copy of it
427
- len = RSTRING_LEN(input) + 1;
428
- if (copts->max_stack < len) {
429
- json = ALLOC_N(char, len);
430
- } else {
431
- json = ALLOCA_N(char, len);
432
- }
433
- strcpy(json, StringValuePtr(input));
434
- } else {
435
- VALUE clas = rb_obj_class(input);
436
- VALUE s;
437
-
438
- if (oj_stringio_class == clas) {
439
- s = rb_funcall2(input, oj_string_id, 0, 0);
440
- len = RSTRING_LEN(s) + 1;
441
- if (copts->max_stack < len) {
442
- json = ALLOC_N(char, len);
443
- } else {
444
- json = ALLOCA_N(char, len);
445
- }
446
- strcpy(json, StringValuePtr(s));
447
- #ifndef JRUBY_RUBY
448
- #if !IS_WINDOWS
449
- // JRuby gets confused with what is the real fileno.
450
- } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
451
- int fd = FIX2INT(s);
452
- ssize_t cnt;
453
-
454
- len = lseek(fd, 0, SEEK_END);
455
- lseek(fd, 0, SEEK_SET);
456
- if (copts->max_stack < len) {
457
- json = ALLOC_N(char, len + 1);
458
- } else {
459
- json = ALLOCA_N(char, len + 1);
460
- }
461
- if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
462
- rb_raise(rb_eIOError, "failed to read from IO Object.");
463
- }
464
- json[len] = '\0';
465
- #endif
466
- #endif
467
- } else if (rb_respond_to(input, oj_read_id)) {
468
- s = rb_funcall2(input, oj_read_id, 0, 0);
469
- len = RSTRING_LEN(s) + 1;
470
- if (copts->max_stack < len) {
471
- json = ALLOC_N(char, len);
472
- } else {
473
- json = ALLOCA_N(char, len);
474
- }
475
- strcpy(json, StringValuePtr(s));
476
- } else {
477
- rb_raise(rb_eArgError, "load() expected a String or IO Object.");
478
- }
479
- }
480
- obj = oj_parse(json, copts);
481
- if (copts->max_stack < len) {
482
- xfree(json);
483
- }
484
- return obj;
485
- }
417
+ /* Document-method: strict_load
418
+ * call-seq: strict_load(json, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
419
+ *
420
+ * Parses a JSON document String into an Hash, Array, String, Fixnum, Float,
421
+ * true, false, or nil. It parses using a mode that is strict in that it maps
422
+ * each primitive JSON type to a similar Ruby type. The :create_id is not
423
+ * honored in this mode. Note that a Ruby Hash is used to represent the JSON
424
+ * Object type. These two are not the same since teh JSON Object type can have
425
+ * repeating entries with the same key and Ruby Hash can not.
426
+ *
427
+ * Raises an exception if the JSON is malformed or the classes specified are not
428
+ * valid. If the input is not a valid JSON document (an empty string is not a
429
+ * valid JSON document) an exception is raised.
430
+ *
431
+ * @param [String|IO] json JSON String or an Object that responds to read()
432
+ * @param [Hash] options load options (same as default_options)
433
+ */
486
434
 
487
- /* call-seq: load(json, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
435
+ /* Document-method: compat_load
436
+ * call-seq: compat_load(json, options) => Object, Hash, Array, String, Fixnum, Float, true, false, or nil
488
437
  *
489
- * Parses a JSON document String into a Hash, Array, String, Fixnum, Float,
490
- * true, false, or nil. Raises an exception if the JSON is malformed or the
491
- * classes specified are not valid. If the string input is not a valid JSON
492
- * document (an empty string is not a valid JSON document) an exception is
493
- * raised.
438
+ * Parses a JSON document String into an Object, Hash, Array, String, Fixnum,
439
+ * Float, true, false, or nil. It parses using a mode that is generally
440
+ * compatible with other Ruby JSON parsers in that it will create objects based
441
+ * on the :create_id value. It is not compatible in every way to every other
442
+ * parser though as each parser has it's own variations.
494
443
  *
495
- * @param [String] json JSON String
444
+ * Raises an exception if the JSON is malformed or the classes specified are not
445
+ * valid. If the input is not a valid JSON document (an empty string is not a
446
+ * valid JSON document) an exception is raised.
447
+ *
448
+ * @param [String|IO] json JSON String or an Object that responds to read()
449
+ * @param [Hash] options load options (same as default_options)
450
+ */
451
+
452
+ /* Document-method: object_load
453
+ * call-seq: object_load(json, options) => Object, Hash, Array, String, Fixnum, Float, true, false, or nil
454
+ *
455
+ * Parses a JSON document String into an Object, Hash, Array, String, Fixnum,
456
+ * Float, true, false, or nil. In the :object mode the JSON should have been
457
+ * generated by Oj.dump(). The parser will reconstitute the original marshalled
458
+ * or dumped Object. The :auto_define and :circular options have meaning with
459
+ * this parsing mode.
460
+ *
461
+ * Raises an exception if the JSON is malformed or the classes specified are not
462
+ * valid. If the input is not a valid JSON document (an empty string is not a
463
+ * valid JSON document) an exception is raised.
464
+ *
465
+ * @param [String|IO] json JSON String or an Object that responds to read()
466
+ * @param [Hash] options load options (same as default_options)
467
+ */
468
+
469
+ /* call-seq: load(json, options) => Object, Hash, Array, String, Fixnum, Float, true, false, or nil
470
+ *
471
+ * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
472
+ * Float, true, false, or nil according to the default mode or the mode
473
+ * specified. Raises an exception if the JSON is malformed or the classes
474
+ * specified are not valid. If the string input is not a valid JSON document (an
475
+ * empty string is not a valid JSON document) an exception is raised.
476
+ *
477
+ * @param [String|IO] json JSON String or an Object that responds to read()
496
478
  * @param [Hash] options load options (same as default_options)
497
479
  */
498
480
  static VALUE
499
481
  load(int argc, VALUE *argv, VALUE self) {
500
- struct _Options options = oj_default_options;
482
+ Mode mode = oj_default_options.mode;
501
483
 
502
484
  if (1 > argc) {
503
485
  rb_raise(rb_eArgError, "Wrong number of arguments to load().");
504
486
  }
505
487
  if (2 <= argc) {
506
- parse_options(argv[1], &options);
488
+ VALUE ropts = argv[1];
489
+ VALUE v;
490
+
491
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
492
+ if (object_sym == v) {
493
+ mode = ObjectMode;
494
+ } else if (strict_sym == v) {
495
+ mode = StrictMode;
496
+ } else if (compat_sym == v) {
497
+ mode = CompatMode;
498
+ } else if (null_sym == v) {
499
+ mode = NullMode;
500
+ } else {
501
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
502
+ }
503
+ }
504
+ }
505
+ switch (mode) {
506
+ case StrictMode:
507
+ return oj_strict_parse(argc, argv, self);
508
+ case NullMode:
509
+ case CompatMode:
510
+ return oj_compat_parse(argc, argv, self);
511
+ case ObjectMode:
512
+ default:
513
+ break;
507
514
  }
508
- return load_with_opts(*argv, &options);
515
+ return oj_object_parse(argc, argv, self);
509
516
  }
510
517
 
511
518
  /* Document-method: load_file
512
- * call-seq: load_file(path, options) => Hash, Array, String, Fixnum, Float, true, false, or nil
519
+ * call-seq: load_file(path, options) => Object, Hash, Array, String, Fixnum, Float, true, false, or nil
513
520
  *
514
- * Parses a JSON document from a file into a Hash, Array, String, Fixnum, Float,
515
- * true, false, or nil. Raises an exception if the JSON is malformed or the
516
- * classes specified are not valid. If the input file is not a valid JSON
517
- * document (an empty file is not a valid JSON document) an exception is raised.
521
+ * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
522
+ * Float, true, false, or nil according to the default mode or the mode
523
+ * specified. Raises an exception if the JSON is malformed or the classes
524
+ * specified are not valid. If the string input is not a valid JSON document (an
525
+ * empty string is not a valid JSON document) an exception is raised.
526
+ *
527
+ * If the input file is not a valid JSON document (an empty file is not a valid
528
+ * JSON document) an exception is raised.
518
529
  *
519
530
  * @param [String] path path to a file containing a JSON document
520
531
  * @param [Hash] options load options (same as default_options)
@@ -525,9 +536,7 @@ load_file(int argc, VALUE *argv, VALUE self) {
525
536
  char *json;
526
537
  FILE *f;
527
538
  unsigned long len;
528
- VALUE obj;
529
- struct _Options options = oj_default_options;
530
- size_t max_stack = oj_default_options.max_stack;
539
+ Mode mode = oj_default_options.mode;
531
540
 
532
541
  Check_Type(*argv, T_STRING);
533
542
  path = StringValuePtr(*argv);
@@ -536,45 +545,98 @@ load_file(int argc, VALUE *argv, VALUE self) {
536
545
  }
537
546
  fseek(f, 0, SEEK_END);
538
547
  len = ftell(f);
539
- if (max_stack < len) {
540
- json = ALLOC_N(char, len + 1);
541
- } else {
542
- json = ALLOCA_N(char, len + 1);
543
- }
548
+ json = ALLOC_N(char, len + 1);
544
549
  fseek(f, 0, SEEK_SET);
545
550
  if (len != fread(json, 1, len, f)) {
551
+ xfree(json);
546
552
  fclose(f);
547
553
  rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")), "Failed to read %ld bytes from %s.", len, path);
548
554
  }
549
555
  fclose(f);
550
556
  json[len] = '\0';
557
+
558
+ if (1 > argc) {
559
+ rb_raise(rb_eArgError, "Wrong number of arguments to load().");
560
+ }
551
561
  if (2 <= argc) {
552
- parse_options(argv[1], &options);
562
+ VALUE ropts = argv[1];
563
+ VALUE v;
564
+
565
+ if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
566
+ if (object_sym == v) {
567
+ mode = ObjectMode;
568
+ } else if (strict_sym == v) {
569
+ mode = StrictMode;
570
+ } else if (compat_sym == v) {
571
+ mode = CompatMode;
572
+ } else if (null_sym == v) {
573
+ mode = NullMode;
574
+ } else {
575
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, or :null.");
576
+ }
577
+ }
553
578
  }
554
- obj = oj_parse(json, &options);
555
- if (max_stack < len) {
556
- xfree(json);
579
+ // The json string is freed in the parser when it is finished with it.
580
+ switch (mode) {
581
+ case StrictMode:
582
+ return oj_strict_parse_cstr(argc, argv, json);
583
+ case NullMode:
584
+ case CompatMode:
585
+ return oj_compat_parse_cstr(argc, argv, json);
586
+ case ObjectMode:
587
+ default:
588
+ break;
557
589
  }
558
- return obj;
590
+ return oj_object_parse_cstr(argc, argv, json);
559
591
  }
560
592
 
561
- /* call-seq: strict_load(doc)
593
+ /* call-seq: safe_load(doc)
594
+ *
595
+ * Loads a JSON document in strict mode with :auto_define and :symbol_keys
596
+ * turned off. This function should be safe to use with JSON received on an
597
+ * unprotected public interface.
562
598
  *
563
- * Loads a JSON document in strict mode with auto_define and symbol_keys turned off. This function should be safe to use
564
- * with JSON received on an unprotected public interface.
565
599
  * @param [String|IO] doc JSON String or IO to load
566
600
  * @return [Hash|Array|String|Fixnum|Bignum|BigDecimal|nil|True|False]
567
601
  */
568
602
  static VALUE
569
- strict_load(VALUE self, VALUE doc) {
570
- struct _Options options = oj_default_options;
603
+ safe_load(VALUE self, VALUE doc) {
604
+ struct _ParseInfo pi;
605
+ VALUE args[1];
606
+
607
+ pi.options = oj_default_options;
608
+ pi.options.auto_define = No;
609
+ pi.options.sym_key = No;
610
+ pi.options.mode = StrictMode;
611
+ oj_set_strict_callbacks(&pi);
612
+ *args = doc;
613
+
614
+ return oj_pi_parse(1, args, &pi, 0);
615
+ }
571
616
 
572
- options.auto_define = No;
573
- options.sym_key = No;
574
- options.mode = StrictMode;
617
+ /* call-seq: saj_parse(handler, io)
618
+ *
619
+ * Parses an IO stream or file containing a JSON document. Raises an exception
620
+ * if the JSON is malformed. This is a callback parser that calls the methods in
621
+ * the handler if they exist. A sample is the Oj::Saj class which can be used as
622
+ * a base class for the handler.
623
+ *
624
+ * @param [Oj::Saj] handler responds to Oj::Saj methods
625
+ * @param [IO|String] io IO Object to read from
626
+ */
575
627
 
576
- return load_with_opts(doc, &options);
577
- }
628
+ /* call-seq: sc_parse(handler, io)
629
+ *
630
+ * Parses an IO stream or file containing a JSON document. Raises an exception
631
+ * if the JSON is malformed. This is a callback parser (Simple Callback Parser)
632
+ * that calls the methods in the handler if they exist. A sample is the
633
+ * Oj::ScHandler class which can be used as a base class for the handler. This
634
+ * callback parser is slightly more efficient than the Saj callback parser and
635
+ * requires less argument checking.
636
+ *
637
+ * @param [Oj::ScHandler] handler responds to Oj::ScHandler methods
638
+ * @param [IO|String] io IO Object to read from
639
+ */
578
640
 
579
641
  /* call-seq: dump(obj, options) => json-string
580
642
  *
@@ -590,7 +652,7 @@ dump(int argc, VALUE *argv, VALUE self) {
590
652
  VALUE rstr;
591
653
 
592
654
  if (2 == argc) {
593
- parse_options(argv[1], &copts);
655
+ oj_parse_options(argv[1], &copts);
594
656
  }
595
657
  out.buf = buf;
596
658
  out.end = buf + sizeof(buf) - 10;
@@ -624,7 +686,7 @@ to_file(int argc, VALUE *argv, VALUE self) {
624
686
  struct _Options copts = oj_default_options;
625
687
 
626
688
  if (3 == argc) {
627
- parse_options(argv[2], &copts);
689
+ oj_parse_options(argv[2], &copts);
628
690
  }
629
691
  Check_Type(*argv, T_STRING);
630
692
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
@@ -632,85 +694,6 @@ to_file(int argc, VALUE *argv, VALUE self) {
632
694
  return Qnil;
633
695
  }
634
696
 
635
- /* call-seq: saj_parse(handler, io)
636
- *
637
- * Parses an IO stream or file containing an JSON document. Raises an exception
638
- * if the JSON is malformed.
639
- * @param [Oj::Saj] handler SAJ (responds to Oj::Saj methods) like handler
640
- * @param [IO|String] io IO Object to read from
641
- */
642
- static VALUE
643
- saj_parse(int argc, VALUE *argv, VALUE self) {
644
- struct _Options copts = oj_default_options;
645
- char *json = 0;
646
- size_t len = 0;
647
- VALUE input = argv[1];
648
-
649
- if (argc < 2) {
650
- rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
651
- }
652
- if (rb_type(input) == T_STRING) {
653
- // the json string gets modified so make a copy of it
654
- len = RSTRING_LEN(input) + 1;
655
- if (copts.max_stack < len) {
656
- json = ALLOC_N(char, len);
657
- } else {
658
- json = ALLOCA_N(char, len);
659
- }
660
- strcpy(json, StringValuePtr(input));
661
- } else {
662
- VALUE clas = rb_obj_class(input);
663
- VALUE s;
664
-
665
- if (oj_stringio_class == clas) {
666
- s = rb_funcall2(input, oj_string_id, 0, 0);
667
- len = RSTRING_LEN(s) + 1;
668
- if (copts.max_stack < len) {
669
- json = ALLOC_N(char, len);
670
- } else {
671
- json = ALLOCA_N(char, len);
672
- }
673
- strcpy(json, StringValuePtr(s));
674
- #ifndef JRUBY_RUBY
675
- #if !IS_WINDOWS
676
- // JRuby gets confused with what is the real fileno.
677
- } else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
678
- int fd = FIX2INT(s);
679
- ssize_t cnt;
680
-
681
- len = lseek(fd, 0, SEEK_END);
682
- lseek(fd, 0, SEEK_SET);
683
- if (copts.max_stack < len) {
684
- json = ALLOC_N(char, len + 1);
685
- } else {
686
- json = ALLOCA_N(char, len + 1);
687
- }
688
- if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
689
- rb_raise(rb_eIOError, "failed to read from IO Object.");
690
- }
691
- json[len] = '\0';
692
- #endif
693
- #endif
694
- } else if (rb_respond_to(input, oj_read_id)) {
695
- s = rb_funcall2(input, oj_read_id, 0, 0);
696
- len = RSTRING_LEN(s) + 1;
697
- if (copts.max_stack < len) {
698
- json = ALLOC_N(char, len);
699
- } else {
700
- json = ALLOCA_N(char, len);
701
- }
702
- strcpy(json, StringValuePtr(s));
703
- } else {
704
- rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
705
- }
706
- }
707
- oj_saj_parse(*argv, json);
708
- if (copts.max_stack < len) {
709
- xfree(json);
710
- }
711
- return Qnil;
712
- }
713
-
714
697
  // Mimic JSON section
715
698
 
716
699
  static VALUE
@@ -911,12 +894,16 @@ mimic_pretty_generate(int argc, VALUE *argv, VALUE self) {
911
894
 
912
895
  static VALUE
913
896
  mimic_parse(int argc, VALUE *argv, VALUE self) {
914
- struct _Options options = oj_default_options;
897
+ struct _ParseInfo pi;
898
+ VALUE args[1];
915
899
 
916
- if (1 > argc) {
917
- rb_raise(rb_eArgError, "Wrong number of arguments to parse().");
900
+ if (argc < 1) {
901
+ rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
918
902
  }
919
- if (2 <= argc && Qnil != argv[1]) {
903
+ oj_set_compat_callbacks(&pi);
904
+ pi.options = oj_default_options;
905
+ pi.options.auto_define = No;
906
+ if (2 <= argc) {
920
907
  VALUE ropts = argv[1];
921
908
  VALUE v;
922
909
 
@@ -924,17 +911,21 @@ mimic_parse(int argc, VALUE *argv, VALUE self) {
924
911
  rb_raise(rb_eArgError, "options must be a hash.");
925
912
  }
926
913
  if (Qnil != (v = rb_hash_lookup(ropts, symbolize_names_sym))) {
927
- options.sym_key = (Qtrue == v) ? Yes : No;
914
+ pi.options.sym_key = (Qtrue == v) ? Yes : No;
928
915
  }
929
- if (Qnil != (v = rb_hash_lookup(ropts, create_additions_sym))) {
930
- options.mode = (Qtrue == v) ? CompatMode : StrictMode;
916
+ if (Qnil != (v = rb_hash_lookup(ropts, create_additions_sym))) {
917
+ if (Qfalse == v) {
918
+ oj_set_strict_callbacks(&pi);
919
+ }
931
920
  }
932
921
  // :allow_nan is not supported as Oj always allows nan
933
- // :max_nesting is always set to 100
922
+ // :max_nesting is ignored as Oj has not nesting limit
934
923
  // :object_class is always Hash
935
924
  // :array_class is always Array
936
925
  }
937
- return load_with_opts(*argv, &options);
926
+ *args = *argv;
927
+
928
+ return oj_pi_parse(1, args, &pi, 0);
938
929
  }
939
930
 
940
931
  static VALUE
@@ -959,12 +950,14 @@ mimic_create_id(VALUE self, VALUE id) {
959
950
  xfree((char*)oj_default_options.create_id);
960
951
  }
961
952
  oj_default_options.create_id = 0;
953
+ oj_default_options.create_id_len = 0;
962
954
  }
963
955
  if (Qnil != id) {
964
956
  size_t len = RSTRING_LEN(id) + 1;
965
957
 
966
958
  oj_default_options.create_id = ALLOC_N(char, len);
967
959
  strcpy((char*)oj_default_options.create_id, StringValuePtr(id));
960
+ oj_default_options.create_id_len = len - 1;
968
961
  }
969
962
  return id;
970
963
  }
@@ -1045,7 +1038,7 @@ define_mimic_json(int argc, VALUE *argv, VALUE self) {
1045
1038
  object_nl_sym = ID2SYM(rb_intern("object_nl")); rb_gc_register_address(&object_nl_sym);
1046
1039
  space_before_sym = ID2SYM(rb_intern("space_before")); rb_gc_register_address(&space_before_sym);
1047
1040
  space_sym = ID2SYM(rb_intern("space")); rb_gc_register_address(&space_sym);
1048
- symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
1041
+ symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
1049
1042
 
1050
1043
  oj_default_options.mode = CompatMode;
1051
1044
  oj_default_options.ascii_only = Yes;
@@ -1054,9 +1047,6 @@ define_mimic_json(int argc, VALUE *argv, VALUE self) {
1054
1047
  }
1055
1048
 
1056
1049
  void Init_oj() {
1057
- Odd odd;
1058
- ID *idp;
1059
-
1060
1050
  Oj = rb_define_module("Oj");
1061
1051
 
1062
1052
  rb_require("time");
@@ -1070,19 +1060,26 @@ void Init_oj() {
1070
1060
  rb_define_module_function(Oj, "mimic_JSON", define_mimic_json, -1);
1071
1061
  rb_define_module_function(Oj, "load", load, -1);
1072
1062
  rb_define_module_function(Oj, "load_file", load_file, -1);
1073
- rb_define_module_function(Oj, "strict_load", strict_load, 1);
1063
+ rb_define_module_function(Oj, "safe_load", safe_load, 1);
1064
+ rb_define_module_function(Oj, "strict_load", oj_strict_parse, -1);
1065
+ rb_define_module_function(Oj, "compat_load", oj_compat_parse, -1);
1066
+ rb_define_module_function(Oj, "object_load", oj_object_parse, -1);
1067
+
1074
1068
  rb_define_module_function(Oj, "dump", dump, -1);
1075
1069
  rb_define_module_function(Oj, "to_file", to_file, -1);
1076
1070
 
1077
- rb_define_module_function(Oj, "saj_parse", saj_parse, -1);
1071
+ rb_define_module_function(Oj, "saj_parse", oj_saj_parse, -1);
1072
+ rb_define_module_function(Oj, "sc_parse", oj_sc_parse, -1);
1078
1073
 
1079
1074
  oj_add_value_id = rb_intern("add_value");
1075
+ oj_array_append_id = rb_intern("array_append");
1080
1076
  oj_array_end_id = rb_intern("array_end");
1081
1077
  oj_array_start_id = rb_intern("array_start");
1082
1078
  oj_as_json_id = rb_intern("as_json");
1083
1079
  oj_error_id = rb_intern("error");
1084
1080
  oj_fileno_id = rb_intern("fileno");
1085
1081
  oj_hash_end_id = rb_intern("hash_end");
1082
+ oj_hash_set_id = rb_intern("hash_set");
1086
1083
  oj_hash_start_id = rb_intern("hash_start");
1087
1084
  oj_instance_variables_id = rb_intern("instance_variables");
1088
1085
  oj_json_create_id = rb_intern("json_create");
@@ -1118,7 +1115,6 @@ void Init_oj() {
1118
1115
  compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1119
1116
  create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
1120
1117
  indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&indent_sym);
1121
- max_stack_sym = ID2SYM(rb_intern("max_stack")); rb_gc_register_address(&max_stack_sym);
1122
1118
  mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
1123
1119
  null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
1124
1120
  object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
@@ -1137,82 +1133,16 @@ void Init_oj() {
1137
1133
  oj_utf8_encoding = rb_enc_find("UTF-8");
1138
1134
  #endif
1139
1135
 
1140
- oj_cache_new(&oj_class_cache);
1141
- oj_cache_new(&oj_attr_cache);
1142
-
1143
- odd = odds;
1144
- // Rational
1145
- idp = odd->attrs;
1146
- odd->clas = rb_const_get(rb_cObject, rb_intern("Rational"));
1147
- odd->create_obj = rb_cObject;
1148
- odd->create_op = rb_intern("Rational");
1149
- odd->attr_cnt = 2;
1150
- *idp++ = rb_intern("numerator");
1151
- *idp++ = rb_intern("denominator");
1152
- *idp++ = 0;
1153
- // Date
1154
- odd++;
1155
- idp = odd->attrs;
1156
- odd->clas = oj_date_class;
1157
- odd->create_obj = odd->clas;
1158
- odd->create_op = oj_new_id;
1159
- odd->attr_cnt = 4;
1160
- *idp++ = rb_intern("year");
1161
- *idp++ = rb_intern("month");
1162
- *idp++ = rb_intern("day");
1163
- *idp++ = rb_intern("start");
1164
- *idp++ = 0;
1165
- // DateTime
1166
- odd++;
1167
- idp = odd->attrs;
1168
- odd->clas = oj_datetime_class;
1169
- odd->create_obj = odd->clas;
1170
- odd->create_op = oj_new_id;
1171
- odd->attr_cnt = 8;
1172
- *idp++ = rb_intern("year");
1173
- *idp++ = rb_intern("month");
1174
- *idp++ = rb_intern("day");
1175
- *idp++ = rb_intern("hour");
1176
- *idp++ = rb_intern("min");
1177
- *idp++ = rb_intern("sec");
1178
- *idp++ = rb_intern("offset");
1179
- *idp++ = rb_intern("start");
1180
- *idp++ = 0;
1181
- // Range
1182
- odd++;
1183
- idp = odd->attrs;
1184
- odd->clas = rb_const_get(rb_cObject, rb_intern("Range"));
1185
- odd->create_obj = odd->clas;
1186
- odd->create_op = oj_new_id;
1187
- odd->attr_cnt = 3;
1188
- *idp++ = rb_intern("begin");
1189
- *idp++ = rb_intern("end");
1190
- *idp++ = rb_intern("exclude_end?");
1191
- *idp++ = 0;
1192
- // The end. bump up the size of odds if a new class is added.
1193
- odd++;
1194
- odd->clas = Qundef;
1136
+ oj_hash_init();
1137
+ oj_odd_init();
1195
1138
 
1196
1139
  #if SAFE_CACHE
1197
1140
  pthread_mutex_init(&oj_cache_mutex, 0);
1198
1141
  #endif
1199
1142
  oj_init_doc();
1200
- }
1201
1143
 
1202
- void
1203
- _oj_raise_error(const char *msg, const char *json, const char *current, const char* file, int line) {
1204
- int jline = 1;
1205
- int col = 1;
1144
+ //rb_define_module_function(Oj, "hash_test", hash_test, 0);
1206
1145
 
1207
- for (; json < current && '\n' != *current; current--) {
1208
- col++;
1209
- }
1210
- for (; json < current; current--) {
1211
- if ('\n' == *current) {
1212
- jline++;
1213
- }
1214
- }
1215
- rb_raise(oj_parse_error_class, "%s at line %d, column %d [%s:%d]", msg, jline, col, file, line);
1216
1146
  }
1217
1147
 
1218
1148
  // mimic JSON documentation