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 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](http://travis-ci.org/ohler55/oj.png)](http://travis-ci.org/ohler55/oj)
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
 
@@ -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 *= out->indent;
128
- if (0 <= cnt) {
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) {
@@ -44,7 +44,7 @@ typedef struct _ParseInfo {
44
44
  #else
45
45
  void *encoding;
46
46
  #endif
47
- int trace;
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, int trace) {
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.trace = trace;
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);
@@ -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' }, // encoding
41
- 2, // indent
42
- 0, // trace
43
- No, // circular
44
- NoMode, // mode
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 obj;
237
+ VALUE obj;
238
+ struct _Options options = default_options;
55
239
 
56
- // TBD other options like obj mode
57
- obj = parse(json, 0);
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 xml string gets modified so make a copy of it
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
- //parse_dump_options(argv[1], &copts);
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
@@ -67,21 +67,27 @@ typedef enum {
67
67
  } YesNo;
68
68
 
69
69
  typedef enum {
70
- ObjMode = 'o',
71
- GenMode = 'g',
72
- NoMode = 0
73
- } LoadMode;
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; // LoadMode
86
+ char mode; // Mode
81
87
  char effort; // Effort
82
88
  } *Options;
83
89
 
84
- extern VALUE parse(char *json, int trace);
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);
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '0.5'
4
+ VERSION = '0.5.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: