oj 0.5 → 0.5.1
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 +1 -1
- data/ext/oj/dump.c +7 -3
- data/ext/oj/load.c +13 -6
- data/ext/oj/oj.c +213 -11
- data/ext/oj/oj.h +13 -7
- data/lib/oj/version.rb +1 -1
- metadata +1 -1
data/README.md
CHANGED
@@ -12,7 +12,7 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
12
12
|
|
13
13
|
## <a name="build_status">Build Status</a>
|
14
14
|
|
15
|
-
[![Build Status](
|
15
|
+
[![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
|
16
16
|
|
17
17
|
## <a name="links">Links of Interest</a>
|
18
18
|
|
data/ext/oj/dump.c
CHANGED
@@ -35,6 +35,11 @@
|
|
35
35
|
#include <string.h>
|
36
36
|
|
37
37
|
#include "ruby.h"
|
38
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
39
|
+
#include "ruby/st.h"
|
40
|
+
#else
|
41
|
+
#include "st.h"
|
42
|
+
#endif
|
38
43
|
#include "oj.h"
|
39
44
|
|
40
45
|
typedef unsigned long ulong;
|
@@ -124,8 +129,8 @@ json_friendly_size(const u_char *str, int len) {
|
|
124
129
|
|
125
130
|
inline static void
|
126
131
|
fill_indent(Out out, int cnt) {
|
127
|
-
cnt
|
128
|
-
|
132
|
+
if (0 < cnt && 0 < out->indent) {
|
133
|
+
cnt *= out->indent;
|
129
134
|
*out->cur++ = '\n';
|
130
135
|
for (; 0 < cnt; cnt--) {
|
131
136
|
*out->cur++ = ' ';
|
@@ -447,7 +452,6 @@ dump_obj_to_json(VALUE obj, Options copts, Out out) {
|
|
447
452
|
ox_cache8_new(&out->circ_cache);
|
448
453
|
}*/
|
449
454
|
out->indent = copts->indent;
|
450
|
-
out->indent = 2; // TBD
|
451
455
|
dump_val(obj, 0, out);
|
452
456
|
|
453
457
|
/* if (Yes == copts->circular) {
|
data/ext/oj/load.c
CHANGED
@@ -44,7 +44,7 @@ typedef struct _ParseInfo {
|
|
44
44
|
#else
|
45
45
|
void *encoding;
|
46
46
|
#endif
|
47
|
-
|
47
|
+
Options options;
|
48
48
|
} *ParseInfo;
|
49
49
|
|
50
50
|
static VALUE read_next(ParseInfo pi);
|
@@ -106,21 +106,18 @@ next_white(ParseInfo pi) {
|
|
106
106
|
}
|
107
107
|
|
108
108
|
VALUE
|
109
|
-
parse(char *json,
|
109
|
+
parse(char *json, Options options) {
|
110
110
|
VALUE obj;
|
111
111
|
struct _ParseInfo pi;
|
112
112
|
|
113
113
|
if (0 == json) {
|
114
114
|
raise_error("Invalid arg, xml string can not be null", json, 0);
|
115
115
|
}
|
116
|
-
if (trace) {
|
117
|
-
printf("Parsing JSON:\n%s\n", json);
|
118
|
-
}
|
119
116
|
/* initialize parse info */
|
120
117
|
pi.str = json;
|
121
118
|
pi.s = json;
|
122
119
|
pi.encoding = 0;
|
123
|
-
pi.
|
120
|
+
pi.options = options;
|
124
121
|
if (Qundef == (obj = read_next(&pi))) {
|
125
122
|
raise_error("no object read", pi.str, pi.s);
|
126
123
|
}
|
@@ -186,6 +183,11 @@ read_obj(ParseInfo pi) {
|
|
186
183
|
VALUE val = Qundef;
|
187
184
|
|
188
185
|
pi->s++;
|
186
|
+
next_non_white(pi);
|
187
|
+
if ('}' == *pi->s) {
|
188
|
+
pi->s++;
|
189
|
+
return rb_hash_new();
|
190
|
+
}
|
189
191
|
while (1) {
|
190
192
|
next_non_white(pi);
|
191
193
|
if ('"' != *pi->s || Qundef == (key = read_str(pi))) {
|
@@ -223,6 +225,11 @@ read_array(ParseInfo pi) {
|
|
223
225
|
VALUE e;
|
224
226
|
|
225
227
|
pi->s++;
|
228
|
+
next_non_white(pi);
|
229
|
+
if (']' == *pi->s) {
|
230
|
+
pi->s++;
|
231
|
+
return a;
|
232
|
+
}
|
226
233
|
while (1) {
|
227
234
|
if (Qundef == (e = read_next(pi))) {
|
228
235
|
raise_error("unexpected character", pi->str, pi->s);
|
data/ext/oj/oj.c
CHANGED
@@ -36,25 +36,211 @@
|
|
36
36
|
#include "ruby.h"
|
37
37
|
#include "oj.h"
|
38
38
|
|
39
|
+
typedef struct _YesNoOpt {
|
40
|
+
VALUE sym;
|
41
|
+
char *attr;
|
42
|
+
} *YesNoOpt;
|
43
|
+
|
39
44
|
struct _Options default_options = {
|
40
|
-
{ '\0' },
|
41
|
-
2,
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
// StrictEffort, // effort
|
45
|
+
{ '\0' }, // encoding
|
46
|
+
2, // indent
|
47
|
+
No, // circular
|
48
|
+
NoMode, // mode
|
49
|
+
TolerantEffort, // effort
|
46
50
|
};
|
47
51
|
|
48
52
|
void Init_oj();
|
49
53
|
|
50
54
|
VALUE Oj = Qnil;
|
51
55
|
|
56
|
+
VALUE circular_sym;
|
57
|
+
VALUE effort_sym;
|
58
|
+
VALUE encoding_sym;
|
59
|
+
VALUE indent_sym;
|
60
|
+
VALUE lazy_sym;
|
61
|
+
VALUE mode_sym;
|
62
|
+
VALUE object_sym;
|
63
|
+
VALUE simple_sym;
|
64
|
+
VALUE strict_sym;
|
65
|
+
VALUE tolerant_sym;
|
66
|
+
|
67
|
+
|
68
|
+
/* call-seq: default_options() => Hash
|
69
|
+
*
|
70
|
+
* Returns the default load and dump options as a Hash. The options are
|
71
|
+
* - indent: [Fixnum] number of spaces to indent each element in an XML document
|
72
|
+
* - encoding: [String] character encoding for the JSON file
|
73
|
+
* - circular: [true|false|nil] support circular references while dumping
|
74
|
+
* - mode: [:object|:simple|nil] load method to use for JSON
|
75
|
+
* - effort: [:strict|:tolerant|:lazy_define] set the tolerance level for loading
|
76
|
+
* @return [Hash] all current option settings.
|
77
|
+
*/
|
78
|
+
static VALUE
|
79
|
+
get_def_opts(VALUE self) {
|
80
|
+
VALUE opts = rb_hash_new();
|
81
|
+
int elen = (int)strlen(default_options.encoding);
|
82
|
+
|
83
|
+
rb_hash_aset(opts, encoding_sym, (0 == elen) ? Qnil : rb_str_new(default_options.encoding, elen));
|
84
|
+
rb_hash_aset(opts, indent_sym, INT2FIX(default_options.indent));
|
85
|
+
rb_hash_aset(opts, circular_sym, (Yes == default_options.circular) ? Qtrue : ((No == default_options.circular) ? Qfalse : Qnil));
|
86
|
+
switch (default_options.mode) {
|
87
|
+
case ObjectMode: rb_hash_aset(opts, mode_sym, object_sym); break;
|
88
|
+
case SimpleMode: rb_hash_aset(opts, mode_sym, simple_sym); break;
|
89
|
+
case NoMode:
|
90
|
+
default: rb_hash_aset(opts, mode_sym, Qnil); break;
|
91
|
+
}
|
92
|
+
switch (default_options.effort) {
|
93
|
+
case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
|
94
|
+
case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
|
95
|
+
case LazyEffort: rb_hash_aset(opts, effort_sym, lazy_sym); break;
|
96
|
+
case NoEffort:
|
97
|
+
default: rb_hash_aset(opts, effort_sym, Qnil); break;
|
98
|
+
}
|
99
|
+
return opts;
|
100
|
+
}
|
101
|
+
|
102
|
+
/* call-seq: default_options=(opts)
|
103
|
+
*
|
104
|
+
* Sets the default options for load and dump.
|
105
|
+
* @param [Hash] opts options to change
|
106
|
+
* @param [Fixnum] :indent number of spaces to indent each element in an XML document
|
107
|
+
* @param [String] :encoding character encoding for the JSON file
|
108
|
+
* @param [true|false|nil] :circular support circular references while dumping
|
109
|
+
* @param [:object|:simple|nil] :mode load method to use for JSON
|
110
|
+
* @param [:strict|:tolerant|:lazy] :effort set the tolerance level for loading
|
111
|
+
* @return [nil]
|
112
|
+
*/
|
113
|
+
static VALUE
|
114
|
+
set_def_opts(VALUE self, VALUE opts) {
|
115
|
+
struct _YesNoOpt ynos[] = {
|
116
|
+
{ circular_sym, &default_options.circular },
|
117
|
+
{ Qnil, 0 }
|
118
|
+
};
|
119
|
+
YesNoOpt o;
|
120
|
+
VALUE v;
|
121
|
+
|
122
|
+
Check_Type(opts, T_HASH);
|
123
|
+
|
124
|
+
v = rb_hash_aref(opts, encoding_sym);
|
125
|
+
if (Qnil == v) {
|
126
|
+
*default_options.encoding = '\0';
|
127
|
+
} else {
|
128
|
+
Check_Type(v, T_STRING);
|
129
|
+
strncpy(default_options.encoding, StringValuePtr(v), sizeof(default_options.encoding) - 1);
|
130
|
+
}
|
131
|
+
|
132
|
+
v = rb_hash_aref(opts, indent_sym);
|
133
|
+
if (Qnil != v) {
|
134
|
+
Check_Type(v, T_FIXNUM);
|
135
|
+
default_options.indent = FIX2INT(v);
|
136
|
+
}
|
137
|
+
|
138
|
+
v = rb_hash_aref(opts, mode_sym);
|
139
|
+
if (Qnil == v) {
|
140
|
+
default_options.mode = NoMode;
|
141
|
+
} else if (object_sym == v) {
|
142
|
+
default_options.mode = ObjectMode;
|
143
|
+
} else if (simple_sym == v) {
|
144
|
+
default_options.mode = SimpleMode;
|
145
|
+
} else {
|
146
|
+
rb_raise(rb_eArgError, ":mode must be :object, :simple, or nil.\n");
|
147
|
+
}
|
148
|
+
|
149
|
+
v = rb_hash_aref(opts, effort_sym);
|
150
|
+
if (Qnil == v) {
|
151
|
+
default_options.effort = NoEffort;
|
152
|
+
} else if (strict_sym == v) {
|
153
|
+
default_options.effort = StrictEffort;
|
154
|
+
} else if (tolerant_sym == v) {
|
155
|
+
default_options.effort = TolerantEffort;
|
156
|
+
} else if (lazy_sym == v) {
|
157
|
+
default_options.effort = LazyEffort;
|
158
|
+
} else {
|
159
|
+
rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, :lazy, or nil.\n");
|
160
|
+
}
|
161
|
+
for (o = ynos; 0 != o->attr; o++) {
|
162
|
+
v = rb_hash_lookup(opts, o->sym);
|
163
|
+
if (Qnil == v) {
|
164
|
+
*o->attr = NotSet;
|
165
|
+
} else if (Qtrue == v) {
|
166
|
+
*o->attr = Yes;
|
167
|
+
} else if (Qfalse == v) {
|
168
|
+
*o->attr = No;
|
169
|
+
} else {
|
170
|
+
rb_raise(rb_eArgError, "%s must be true, false, or nil.\n", StringValuePtr(o->sym));
|
171
|
+
}
|
172
|
+
}
|
173
|
+
return Qnil;
|
174
|
+
}
|
175
|
+
|
176
|
+
static void
|
177
|
+
parse_options(VALUE ropts, Options copts) {
|
178
|
+
struct _YesNoOpt ynos[] = {
|
179
|
+
{ circular_sym, &copts->circular },
|
180
|
+
{ Qnil, 0 }
|
181
|
+
};
|
182
|
+
YesNoOpt o;
|
183
|
+
|
184
|
+
if (rb_cHash == rb_obj_class(ropts)) {
|
185
|
+
VALUE v;
|
186
|
+
|
187
|
+
if (Qnil != (v = rb_hash_lookup(ropts, indent_sym))) {
|
188
|
+
if (rb_cFixnum != rb_obj_class(v)) {
|
189
|
+
rb_raise(rb_eArgError, ":indent must be a Fixnum.\n");
|
190
|
+
}
|
191
|
+
copts->indent = NUM2INT(v);
|
192
|
+
}
|
193
|
+
if (Qnil != (v = rb_hash_lookup(ropts, encoding_sym))) {
|
194
|
+
if (rb_cString != rb_obj_class(v)) {
|
195
|
+
rb_raise(rb_eArgError, ":encoding must be a String.\n");
|
196
|
+
}
|
197
|
+
strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
|
198
|
+
}
|
199
|
+
if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
|
200
|
+
if (object_sym == v) {
|
201
|
+
copts->mode = ObjectMode;
|
202
|
+
} else if (simple_sym == v) {
|
203
|
+
copts->mode = SimpleMode;
|
204
|
+
} else {
|
205
|
+
rb_raise(rb_eArgError, ":mode must be :object or :simple.\n");
|
206
|
+
}
|
207
|
+
}
|
208
|
+
if (Qnil != (v = rb_hash_lookup(ropts, effort_sym))) {
|
209
|
+
if (lazy_sym == v) {
|
210
|
+
copts->effort = LazyEffort;
|
211
|
+
} else if (tolerant_sym == v) {
|
212
|
+
copts->effort = TolerantEffort;
|
213
|
+
} else if (strict_sym == v) {
|
214
|
+
copts->effort = StrictEffort;
|
215
|
+
} else {
|
216
|
+
rb_raise(rb_eArgError, ":effort must be :strict, :tolerant, or :lazy.\n");
|
217
|
+
}
|
218
|
+
}
|
219
|
+
for (o = ynos; 0 != o->attr; o++) {
|
220
|
+
if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
|
221
|
+
VALUE c = rb_obj_class(v);
|
222
|
+
|
223
|
+
if (rb_cTrueClass == c) {
|
224
|
+
*o->attr = Yes;
|
225
|
+
} else if (rb_cFalseClass == c) {
|
226
|
+
*o->attr = No;
|
227
|
+
} else {
|
228
|
+
rb_raise(rb_eArgError, "%s must be true or false.\n", StringValuePtr(o->sym));
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
52
235
|
static VALUE
|
53
236
|
load(char *json, int argc, VALUE *argv, VALUE self) {
|
54
|
-
VALUE
|
237
|
+
VALUE obj;
|
238
|
+
struct _Options options = default_options;
|
55
239
|
|
56
|
-
|
57
|
-
|
240
|
+
if (1 == argc) {
|
241
|
+
parse_options(*argv, &options);
|
242
|
+
}
|
243
|
+
obj = parse(json, &options);
|
58
244
|
free(json);
|
59
245
|
|
60
246
|
return obj;
|
@@ -73,7 +259,7 @@ load_str(int argc, VALUE *argv, VALUE self) {
|
|
73
259
|
char *json;
|
74
260
|
|
75
261
|
Check_Type(*argv, T_STRING);
|
76
|
-
// the
|
262
|
+
// the json string gets modified so make a copy of it
|
77
263
|
json = strdup(StringValuePtr(*argv));
|
78
264
|
|
79
265
|
return load(json, argc - 1, argv + 1, self);
|
@@ -115,7 +301,7 @@ dump(int argc, VALUE *argv, VALUE self) {
|
|
115
301
|
VALUE rstr;
|
116
302
|
|
117
303
|
if (2 == argc) {
|
118
|
-
|
304
|
+
parse_options(argv[1], &copts);
|
119
305
|
}
|
120
306
|
if (0 == (json = write_obj_to_str(*argv, &copts))) {
|
121
307
|
rb_raise(rb_eNoMemError, "Not enough memory.\n");
|
@@ -132,12 +318,28 @@ dump(int argc, VALUE *argv, VALUE self) {
|
|
132
318
|
}
|
133
319
|
|
134
320
|
void Init_oj() {
|
321
|
+
VALUE keep = Qnil;
|
135
322
|
|
136
323
|
Oj = rb_define_module("Oj");
|
324
|
+
keep = rb_cv_get(Oj, "@@keep"); // needed to stop GC from deleting and reusing VALUEs
|
325
|
+
|
326
|
+
rb_define_module_function(Oj, "default_options", get_def_opts, 0);
|
327
|
+
rb_define_module_function(Oj, "default_options=", set_def_opts, 1);
|
137
328
|
|
138
329
|
rb_define_module_function(Oj, "load", load_str, -1);
|
139
330
|
rb_define_module_function(Oj, "load_file", load_file, -1);
|
140
331
|
rb_define_module_function(Oj, "dump", dump, -1);
|
332
|
+
|
333
|
+
circular_sym = ID2SYM(rb_intern("circular")); rb_ary_push(keep, circular_sym);
|
334
|
+
effort_sym = ID2SYM(rb_intern("effort")); rb_ary_push(keep, effort_sym);
|
335
|
+
encoding_sym = ID2SYM(rb_intern("encoding")); rb_ary_push(keep, encoding_sym);
|
336
|
+
indent_sym = ID2SYM(rb_intern("indent")); rb_ary_push(keep, indent_sym);
|
337
|
+
lazy_sym = ID2SYM(rb_intern("lazy")); rb_ary_push(keep, lazy_sym);
|
338
|
+
mode_sym = ID2SYM(rb_intern("mode")); rb_ary_push(keep, mode_sym);
|
339
|
+
object_sym = ID2SYM(rb_intern("object")); rb_ary_push(keep, object_sym);
|
340
|
+
simple_sym = ID2SYM(rb_intern("simple")); rb_ary_push(keep, simple_sym);
|
341
|
+
strict_sym = ID2SYM(rb_intern("strict")); rb_ary_push(keep, strict_sym);
|
342
|
+
tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_ary_push(keep, tolerant_sym);
|
141
343
|
}
|
142
344
|
|
143
345
|
void
|
data/ext/oj/oj.h
CHANGED
@@ -67,21 +67,27 @@ typedef enum {
|
|
67
67
|
} YesNo;
|
68
68
|
|
69
69
|
typedef enum {
|
70
|
-
|
71
|
-
|
72
|
-
NoMode
|
73
|
-
}
|
70
|
+
ObjectMode = 'o',
|
71
|
+
SimpleMode = 's',
|
72
|
+
NoMode = 0
|
73
|
+
} Mode;
|
74
|
+
|
75
|
+
typedef enum {
|
76
|
+
StrictEffort = 's',
|
77
|
+
TolerantEffort = 't',
|
78
|
+
LazyEffort = 'z',
|
79
|
+
NoEffort = 0,
|
80
|
+
} Effort;
|
74
81
|
|
75
82
|
typedef struct _Options {
|
76
83
|
char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
|
77
84
|
int indent; // indention for dump, default 2
|
78
|
-
int trace; // trace level
|
79
85
|
char circular; // YesNo
|
80
|
-
char mode; //
|
86
|
+
char mode; // Mode
|
81
87
|
char effort; // Effort
|
82
88
|
} *Options;
|
83
89
|
|
84
|
-
extern VALUE parse(char *json,
|
90
|
+
extern VALUE parse(char *json, Options options);
|
85
91
|
extern char* write_obj_to_str(VALUE obj, Options copts);
|
86
92
|
|
87
93
|
extern void _raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
|
data/lib/oj/version.rb
CHANGED