oj 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70265e88ab461a3fc0c6207db8f333504df2f53c
4
- data.tar.gz: 9a0bd96af511e73ed8f5aa6caaebe2f7a4b00bed
3
+ metadata.gz: 1373424e4c2fd32c52eaea6561ddfff276babeb5
4
+ data.tar.gz: e35e9cd9d69496cc46838dcdf3da63a46908a657
5
5
  SHA512:
6
- metadata.gz: 546692fc1cdc5bebc182b539a38267789ee67e354dd791154e6141df22ab0514927a0c501915bb1ed38c1b3ebc64c07986326abcb5980c9550a0fd68717bd854
7
- data.tar.gz: dedc2c96f831f2d09576c74bf6d9ec97af06ca36497e389a02d321d8f60c3645aaeb81bcc3384835fad28d9638b02309473071e37281440760c7f7bc378dffc5
6
+ metadata.gz: 19cc74647936b9ca942b3e88fea44f93efd7d3bb6d703fca5c53ed7a3cefd3f12e0c48f32d71fdc2a264d68ad6dd2662edffddcee046ab3b65d5b1354487116b
7
+ data.tar.gz: 90ce6b08d284b3cded6aba7b946ce794751a1b85f6e5ff0f1e37337679263cf88641949f3fe5898a22db89bf5d71d9b7c36322de37a11ef31bf275965d69d364
@@ -572,6 +572,7 @@ oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VA
572
572
  case CompatMode: oj_dump_compat_val(obj, 0, out, Yes == copts->to_json); break;
573
573
  case RailsMode: oj_dump_rails_val(obj, 0, out); break;
574
574
  case CustomMode: oj_dump_custom_val(obj, 0, out, true); break;
575
+ case WabMode: oj_dump_wab_val(obj, 0, out); break;
575
576
  default: oj_dump_custom_val(obj, 0, out, true); break;
576
577
  }
577
578
  if (0 < out->indent) {
@@ -680,7 +681,7 @@ oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
680
681
  void
681
682
  oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
682
683
  const char *sym = rb_id2name(SYM2ID(obj));
683
-
684
+
684
685
  oj_dump_cstr(sym, strlen(sym), 0, 0, out);
685
686
  }
686
687
 
@@ -43,6 +43,7 @@ extern void oj_dump_obj_val(VALUE obj, int depth, Out out);
43
43
  extern void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok);
44
44
  extern void oj_dump_rails_val(VALUE obj, int depth, Out out);
45
45
  extern void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok);
46
+ extern void oj_dump_wab_val(VALUE obj, int depth, Out out);
46
47
 
47
48
  extern VALUE oj_add_to_json(int argc, VALUE *argv, VALUE self);
48
49
  extern VALUE oj_remove_to_json(int argc, VALUE *argv, VALUE self);
@@ -139,6 +139,7 @@ static VALUE unix_zone_sym;
139
139
  static VALUE use_as_json_sym;
140
140
  static VALUE use_to_hash_sym;
141
141
  static VALUE use_to_json_sym;
142
+ static VALUE wab_sym;
142
143
  static VALUE word_sym;
143
144
  static VALUE xmlschema_sym;
144
145
  static VALUE xss_safe_sym;
@@ -218,7 +219,7 @@ struct _Options oj_default_options = {
218
219
  * - *:symbol_keys* [_Boolean_|_nil_] use symbols instead of strings for hash keys
219
220
  * - *:escape_mode* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] determines the characters to escape
220
221
  * - *:class_cache* [_Boolean_|_nil_] cache classes for faster parsing (if dynamically modifying classes or reloading classes then don't use this)
221
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_] load and dump modes to use for JSON
222
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump modes to use for JSON
222
223
  * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping in :compat and :object mode
223
224
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
224
225
  * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
@@ -277,6 +278,7 @@ get_def_opts(VALUE self) {
277
278
  case ObjectMode: rb_hash_aset(opts, mode_sym, object_sym); break;
278
279
  case CustomMode: rb_hash_aset(opts, mode_sym, custom_sym); break;
279
280
  case RailsMode: rb_hash_aset(opts, mode_sym, rails_sym); break;
281
+ case WabMode: rb_hash_aset(opts, mode_sym, wab_sym); break;
280
282
  default: rb_hash_aset(opts, mode_sym, object_sym); break;
281
283
  }
282
284
  switch (oj_default_options.escape_mode) {
@@ -334,7 +336,7 @@ get_def_opts(VALUE self) {
334
336
  * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding, :newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <, and >, and some others.
335
337
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String.
336
338
  * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
337
- * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_] load and dump mode to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more mimics rails and Active behavior.
339
+ * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump mode to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more mimics rails and Active behavior.
338
340
  * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time format taken from XML Schema as a String, :ruby Time.to_s formatted String.
339
341
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
340
342
  * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time.
@@ -463,7 +465,9 @@ oj_parse_options(VALUE ropts, Options copts) {
463
465
  copts->sec_prec = n;
464
466
  }
465
467
  if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
466
- if (object_sym == v) {
468
+ if (wab_sym == v) {
469
+ copts->mode = WabMode;
470
+ } else if (object_sym == v) {
467
471
  copts->mode = ObjectMode;
468
472
  } else if (strict_sym == v) {
469
473
  copts->mode = StrictMode;
@@ -476,7 +480,7 @@ oj_parse_options(VALUE ropts, Options copts) {
476
480
  } else if (rails_sym == v) {
477
481
  copts->mode = RailsMode;
478
482
  } else {
479
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, or :rails.");
483
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
480
484
  }
481
485
  }
482
486
  if (Qnil != (v = rb_hash_lookup(ropts, time_format_sym))) {
@@ -759,8 +763,10 @@ load(int argc, VALUE *argv, VALUE self) {
759
763
  mode = CustomMode;
760
764
  } else if (rails_sym == v) {
761
765
  mode = RailsMode;
766
+ } else if (wab_sym == v) {
767
+ mode = WabMode;
762
768
  } else {
763
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, or :rails.");
769
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
764
770
  }
765
771
  }
766
772
  }
@@ -773,6 +779,8 @@ load(int argc, VALUE *argv, VALUE self) {
773
779
  return oj_compat_parse(argc, argv, self);
774
780
  case CustomMode:
775
781
  return oj_custom_parse(argc, argv, self);
782
+ case WabMode:
783
+ return oj_wab_parse(argc, argv, self);
776
784
  case ObjectMode:
777
785
  default:
778
786
  break;
@@ -849,8 +857,10 @@ load_file(int argc, VALUE *argv, VALUE self) {
849
857
  mode = CustomMode;
850
858
  } else if (rails_sym == v) {
851
859
  mode = RailsMode;
860
+ } else if (wab_sym == v) {
861
+ mode = WabMode;
852
862
  } else {
853
- rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails.");
863
+ rb_raise(rb_eArgError, ":mode must be :object, :strict, :compat, :null, :custom, :rails, or :wab.");
854
864
  }
855
865
  }
856
866
  }
@@ -868,6 +878,9 @@ load_file(int argc, VALUE *argv, VALUE self) {
868
878
  case RailsMode:
869
879
  oj_set_compat_callbacks(&pi);
870
880
  return oj_pi_sparse(argc, argv, &pi, fd);
881
+ case WabMode:
882
+ oj_set_wab_callbacks(&pi);
883
+ return oj_pi_sparse(argc, argv, &pi, fd);
871
884
  case ObjectMode:
872
885
  default:
873
886
  break;
@@ -1269,6 +1282,42 @@ extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
1269
1282
  */
1270
1283
  extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
1271
1284
 
1285
+ /* Document-method: wab_load
1286
+ * call-seq: wab_load(json, options) { _|_obj, start, len_|_ }
1287
+ *
1288
+ * Parses a JSON document String into an Hash, Array, String, Fixnum, Float,
1289
+ * true, false, or nil. It parses using a mode that is :wab in that it maps
1290
+ * each primitive JSON type to a similar Ruby type. The :create_id is not
1291
+ * honored in this mode. Note that a Ruby Hash is used to represent the JSON
1292
+ * Object type. These two are not the same since the JSON Object type can have
1293
+ * repeating entries with the same key and Ruby Hash can not.
1294
+ *
1295
+ * When used with a document that has multiple JSON elements the block, if
1296
+ * any, will be yielded to. If no block then the last element read will be
1297
+ * returned.
1298
+ *
1299
+ * Raises an exception if the JSON is malformed or the classes specified are not
1300
+ * valid. If the input is not a valid JSON document (an empty string is not a
1301
+ * valid JSON document) an exception is raised.
1302
+ *
1303
+ * A block can be provided with a single argument. That argument will be the
1304
+ * parsed JSON document. This is useful when parsing a string that includes
1305
+ * multiple JSON documents. The block can take up to 3 arguments, the parsed
1306
+ * object, the position in the string or stream of the start of the JSON for
1307
+ * that object, and the length of the JSON for that object plus trailing
1308
+ * whitespace.
1309
+ *
1310
+ * - *json* [_String_|_IO_] JSON String or an Object that responds to read().
1311
+ * - *options* [_Hash_] load options (same as default_options).
1312
+ * - -
1313
+ * - *obj* [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_] parsed object.
1314
+ * - *start* [_optional, _Integer_] start position of parsed JSON for obj.
1315
+ * - *len* [_optional, _Integer_] length of parsed JSON for obj.
1316
+ *
1317
+ * Returns [_Hash_|_Array_|_String_|_Fixnum_|_Float_|_Boolean_|_nil_]
1318
+ */
1319
+ extern VALUE oj_wab_parse(int argc, VALUE *argv, VALUE self);
1320
+
1272
1321
  /* Document-method: add_to_json
1273
1322
  * call-seq: add_to_json(*args)
1274
1323
  *
@@ -1406,6 +1455,8 @@ protect_require(VALUE x) {
1406
1455
  * - *:rails* is the compatibility mode for Rails or Active support.
1407
1456
  *
1408
1457
  * - *:custom* is the most configurable mode.
1458
+ *
1459
+ * - *:wab* specifically for WAB data exchange.
1409
1460
  */
1410
1461
  void
1411
1462
  Init_oj() {
@@ -1445,6 +1496,7 @@ Init_oj() {
1445
1496
  rb_define_module_function(Oj, "strict_load", oj_strict_parse, -1);
1446
1497
  rb_define_module_function(Oj, "compat_load", oj_compat_parse, -1);
1447
1498
  rb_define_module_function(Oj, "object_load", oj_object_parse, -1);
1499
+ rb_define_module_function(Oj, "wab_load", oj_wab_parse, -1);
1448
1500
 
1449
1501
  rb_define_module_function(Oj, "dump", dump, -1);
1450
1502
 
@@ -1580,6 +1632,7 @@ Init_oj() {
1580
1632
  use_as_json_sym = ID2SYM(rb_intern("use_as_json")); rb_gc_register_address(&use_as_json_sym);
1581
1633
  use_to_hash_sym = ID2SYM(rb_intern("use_to_hash")); rb_gc_register_address(&use_to_hash_sym);
1582
1634
  use_to_json_sym = ID2SYM(rb_intern("use_to_json")); rb_gc_register_address(&use_to_json_sym);
1635
+ wab_sym = ID2SYM(rb_intern("wab")); rb_gc_register_address(&wab_sym);
1583
1636
  word_sym = ID2SYM(rb_intern("word")); rb_gc_register_address(&word_sym);
1584
1637
  xmlschema_sym = ID2SYM(rb_intern("xmlschema")); rb_gc_register_address(&xmlschema_sym);
1585
1638
  xss_safe_sym = ID2SYM(rb_intern("xss_safe")); rb_gc_register_address(&xss_safe_sym);
@@ -61,6 +61,7 @@ typedef enum {
61
61
  CompatMode = 'c',
62
62
  RailsMode = 'r',
63
63
  CustomMode = 'C',
64
+ WabMode = 'w',
64
65
  } Mode;
65
66
 
66
67
  typedef enum {
@@ -245,6 +246,7 @@ extern VALUE oj_strict_sparse(int argc, VALUE *argv, VALUE self);
245
246
  extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
246
247
  extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
247
248
  extern VALUE oj_custom_parse(int argc, VALUE *argv, VALUE self);
249
+ extern VALUE oj_wab_parse(int argc, VALUE *argv, VALUE self);
248
250
 
249
251
  extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
250
252
  extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len);
@@ -80,6 +80,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
80
80
  extern void oj_set_strict_callbacks(ParseInfo pi);
81
81
  extern void oj_set_object_callbacks(ParseInfo pi);
82
82
  extern void oj_set_compat_callbacks(ParseInfo pi);
83
+ extern void oj_set_wab_callbacks(ParseInfo pi);
83
84
 
84
85
  extern void oj_sparse2(ParseInfo pi);
85
86
  extern VALUE oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd);
@@ -0,0 +1,562 @@
1
+ /* wab.c
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdlib.h>
7
+ #include <stdio.h>
8
+ #include <string.h>
9
+ #include <time.h>
10
+ #include <unistd.h>
11
+
12
+ #include "oj.h"
13
+ #include "err.h"
14
+ #include "parse.h"
15
+ #include "encode.h"
16
+ #include "dump.h"
17
+
18
+ // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
19
+ #define OJ_INFINITY (1.0/0.0)
20
+
21
+ static char hex_chars[256] = "\
22
+ ................................\
23
+ ................xxxxxxxxxx......\
24
+ .xxxxxx.........................\
25
+ .xxxxxx.........................\
26
+ ................................\
27
+ ................................\
28
+ ................................\
29
+ ................................";
30
+
31
+ static VALUE wab_uuid_clas = Qundef;
32
+ static VALUE uri_clas = Qundef;
33
+ static VALUE uri_http_clas = Qundef;
34
+
35
+ ///// dump functions /////
36
+
37
+ static VALUE
38
+ resolve_wab_uuid_class() {
39
+ if (Qundef == wab_uuid_clas) {
40
+ volatile VALUE wab_module;
41
+
42
+ wab_uuid_clas = Qnil;
43
+ if (rb_const_defined_at(rb_cObject, rb_intern("WAB"))) {
44
+ wab_module = rb_const_get_at(rb_cObject, rb_intern("WAB"));
45
+ if (rb_const_defined_at(wab_module, rb_intern("UUID"))) {
46
+ wab_uuid_clas = rb_const_get(wab_module, rb_intern("UUID"));
47
+ }
48
+ }
49
+ }
50
+ return wab_uuid_clas;
51
+ }
52
+
53
+ static VALUE
54
+ resolve_uri_class() {
55
+ if (Qundef == uri_clas) {
56
+
57
+ uri_clas = Qnil;
58
+ if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
59
+ uri_clas = rb_const_get_at(rb_cObject, rb_intern("URI"));
60
+ }
61
+ }
62
+ return uri_clas;
63
+ }
64
+
65
+ static VALUE
66
+ resolve_uri_http_class() {
67
+ if (Qundef == uri_http_clas) {
68
+ volatile VALUE uri_module;
69
+
70
+ uri_http_clas = Qnil;
71
+ if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
72
+ uri_module = rb_const_get_at(rb_cObject, rb_intern("URI"));
73
+ if (rb_const_defined_at(uri_module, rb_intern("HTTP"))) {
74
+ uri_http_clas = rb_const_get(uri_module, rb_intern("HTTP"));
75
+ }
76
+ }
77
+ }
78
+ return uri_http_clas;
79
+ }
80
+
81
+ static void
82
+ raise_wab(VALUE obj) {
83
+ rb_raise(rb_eTypeError, "Failed to dump %s Object to JSON in wab mode.\n", rb_class2name(rb_obj_class(obj)));
84
+ }
85
+
86
+ // Removed dependencies on math due to problems with CentOS 5.4.
87
+ static void
88
+ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
89
+ char buf[64];
90
+ char *b;
91
+ double d = rb_num2dbl(obj);
92
+ int cnt = 0;
93
+
94
+ if (0.0 == d) {
95
+ b = buf;
96
+ *b++ = '0';
97
+ *b++ = '.';
98
+ *b++ = '0';
99
+ *b++ = '\0';
100
+ cnt = 3;
101
+ } else {
102
+ if (OJ_INFINITY == d || -OJ_INFINITY == d || isnan(d)) {
103
+ raise_wab(obj);
104
+ } else if (d == (double)(long long int)d) {
105
+ cnt = snprintf(buf, sizeof(buf), "%.1f", d);
106
+ } else {
107
+ cnt = snprintf(buf, sizeof(buf), "%0.16g", d);
108
+ }
109
+ }
110
+ assure_size(out, cnt);
111
+ for (b = buf; '\0' != *b; b++) {
112
+ *out->cur++ = *b;
113
+ }
114
+ *out->cur = '\0';
115
+ }
116
+
117
+ static void
118
+ dump_array(VALUE a, int depth, Out out, bool as_ok) {
119
+ size_t size;
120
+ int i, cnt;
121
+ int d2 = depth + 1;
122
+
123
+ cnt = (int)RARRAY_LEN(a);
124
+ *out->cur++ = '[';
125
+ size = 2;
126
+ assure_size(out, size);
127
+ if (0 == cnt) {
128
+ *out->cur++ = ']';
129
+ } else {
130
+ size = d2 * out->indent + 2;
131
+ cnt--;
132
+ for (i = 0; i <= cnt; i++) {
133
+ assure_size(out, size);
134
+ fill_indent(out, d2);
135
+ oj_dump_wab_val(rb_ary_entry(a, i), d2, out);
136
+ if (i < cnt) {
137
+ *out->cur++ = ',';
138
+ }
139
+ }
140
+ size = depth * out->indent + 1;
141
+ assure_size(out, size);
142
+ fill_indent(out, depth);
143
+ *out->cur++ = ']';
144
+ }
145
+ *out->cur = '\0';
146
+ }
147
+
148
+ static int
149
+ hash_cb(VALUE key, VALUE value, Out out) {
150
+ int depth = out->depth;
151
+ long size;
152
+ int rtype = rb_type(key);
153
+
154
+ if (rtype != T_SYMBOL) {
155
+ rb_raise(rb_eTypeError, "In :wab mode all Hash keys must be Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));
156
+ }
157
+ size = depth * out->indent + 1;
158
+ assure_size(out, size);
159
+ fill_indent(out, depth);
160
+ oj_dump_sym(key, 0, out, false);
161
+ *out->cur++ = ':';
162
+ oj_dump_wab_val(value, depth, out);
163
+ out->depth = depth;
164
+ *out->cur++ = ',';
165
+
166
+ return ST_CONTINUE;
167
+ }
168
+
169
+ static void
170
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
171
+ int cnt;
172
+ size_t size;
173
+
174
+ cnt = (int)RHASH_SIZE(obj);
175
+ size = depth * out->indent + 2;
176
+ assure_size(out, 2);
177
+ *out->cur++ = '{';
178
+ if (0 == cnt) {
179
+ *out->cur++ = '}';
180
+ } else {
181
+ out->depth = depth + 1;
182
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
183
+ if (',' == *(out->cur - 1)) {
184
+ out->cur--; // backup to overwrite last comma
185
+ }
186
+ assure_size(out, size);
187
+ fill_indent(out, depth);
188
+ *out->cur++ = '}';
189
+ }
190
+ *out->cur = '\0';
191
+ }
192
+
193
+ static void
194
+ dump_time(VALUE obj, Out out) {
195
+ char buf[64];
196
+ struct tm *tm;
197
+ #if HAS_RB_TIME_TIMESPEC
198
+ struct timespec ts = rb_time_timespec(obj);
199
+ time_t sec = ts.tv_sec;
200
+ long nsec = ts.tv_nsec;
201
+ #else
202
+ time_t sec = NUM2LONG(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
203
+ #if HAS_NANO_TIME
204
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
205
+ #else
206
+ long long nsec = rb_num2ll(rb_funcall2(obj, oj_tv_usec_id, 0, 0)) * 1000;
207
+ #endif
208
+ #endif
209
+ int len;
210
+
211
+ assure_size(out, 36);
212
+ // 2012-01-05T23:58:07.123456000Z
213
+ tm = gmtime(&sec);
214
+
215
+ len = sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d.%09ldZ",
216
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
217
+ tm->tm_hour, tm->tm_min, tm->tm_sec, nsec);
218
+ oj_dump_cstr(buf, len, 0, 0, out);
219
+ }
220
+
221
+ static void
222
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
223
+ volatile VALUE clas = rb_obj_class(obj);
224
+
225
+ if (rb_cTime == clas) {
226
+ dump_time(obj, out);
227
+ } else if (oj_bigdecimal_class == clas) {
228
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
229
+
230
+ oj_dump_raw(rb_string_value_ptr((VALUE*)&rstr), RSTRING_LEN(rstr), out);
231
+ } else if (resolve_wab_uuid_class() == clas) {
232
+ oj_dump_str(rb_funcall(obj, oj_to_s_id, 0), depth, out, false);
233
+ } else if (resolve_uri_http_class() == clas) {
234
+ oj_dump_str(rb_funcall(obj, oj_to_s_id, 0), depth, out, false);
235
+ } else {
236
+ raise_wab(obj);
237
+ }
238
+ }
239
+
240
+ static DumpFunc wab_funcs[] = {
241
+ NULL, // RUBY_T_NONE = 0x00,
242
+ dump_obj, // RUBY_T_OBJECT = 0x01,
243
+ NULL, // RUBY_T_CLASS = 0x02,
244
+ NULL, // RUBY_T_MODULE = 0x03,
245
+ dump_float, // RUBY_T_FLOAT = 0x04,
246
+ oj_dump_str, // RUBY_T_STRING = 0x05,
247
+ NULL, // RUBY_T_REGEXP = 0x06,
248
+ dump_array, // RUBY_T_ARRAY = 0x07,
249
+ dump_hash, // RUBY_T_HASH = 0x08,
250
+ NULL, // RUBY_T_STRUCT = 0x09,
251
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
252
+ NULL, // RUBY_T_FILE = 0x0b,
253
+ dump_obj, // RUBY_T_DATA = 0x0c,
254
+ NULL, // RUBY_T_MATCH = 0x0d,
255
+ NULL, // RUBY_T_COMPLEX = 0x0e,
256
+ NULL, // RUBY_T_RATIONAL = 0x0f,
257
+ NULL, // 0x10
258
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
259
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
260
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
261
+ oj_dump_sym, // RUBY_T_SYMBOL = 0x14,
262
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
263
+ };
264
+
265
+ void
266
+ oj_dump_wab_val(VALUE obj, int depth, Out out) {
267
+ int type = rb_type(obj);
268
+
269
+ if (MAX_DEPTH < depth) {
270
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
271
+ }
272
+ if (0 < type && type <= RUBY_T_FIXNUM) {
273
+ DumpFunc f = wab_funcs[type];
274
+
275
+ //printf("*** type %02x\n", type);
276
+
277
+ if (NULL != f) {
278
+ f(obj, depth, out, false);
279
+ return;
280
+ }
281
+ }
282
+ raise_wab(obj);
283
+ }
284
+
285
+ ///// load functions /////
286
+
287
+ static void
288
+ noop_end(struct _ParseInfo *pi) {
289
+ }
290
+
291
+ static VALUE
292
+ noop_hash_key(struct _ParseInfo *pi, const char *key, size_t klen) {
293
+ return Qundef;
294
+ }
295
+
296
+ static void
297
+ add_value(ParseInfo pi, VALUE val) {
298
+ pi->stack.head->val = val;
299
+ }
300
+
301
+ // 123e4567-e89b-12d3-a456-426655440000
302
+ static bool
303
+ uuid_check(const char *str, int len) {
304
+ int i;
305
+
306
+ for (i = 0; i < 8; i++, str++) {
307
+ if ('x' != hex_chars[*(uint8_t*)str]) {
308
+ return false;
309
+ }
310
+ }
311
+ str++;
312
+ for (i = 0; i < 4; i++, str++) {
313
+ if ('x' != hex_chars[*(uint8_t*)str]) {
314
+ return false;
315
+ }
316
+ }
317
+ str++;
318
+ for (i = 0; i < 4; i++, str++) {
319
+ if ('x' != hex_chars[*(uint8_t*)str]) {
320
+ return false;
321
+ }
322
+ }
323
+ str++;
324
+ for (i = 0; i < 4; i++, str++) {
325
+ if ('x' != hex_chars[*(uint8_t*)str]) {
326
+ return false;
327
+ }
328
+ }
329
+ str++;
330
+ for (i = 0; i < 12; i++, str++) {
331
+ if ('x' != hex_chars[*(uint8_t*)str]) {
332
+ return false;
333
+ }
334
+ }
335
+ return true;
336
+ }
337
+
338
+ static const char*
339
+ read_num(const char *s, int len, int *vp) {
340
+ uint32_t v = 0;
341
+
342
+ for (; 0 < len; len--, s++) {
343
+ if ('0' <= *s && *s <= '9') {
344
+ v = v * 10 + *s - '0';
345
+ } else {
346
+ return NULL;
347
+ }
348
+ }
349
+ *vp = (int)v;
350
+
351
+ return s;
352
+ }
353
+
354
+ static VALUE
355
+ time_parse(const char *s, int len) {
356
+ struct tm tm;
357
+ bool neg = false;
358
+ long nsecs = 0;
359
+ int i;
360
+ time_t secs;
361
+
362
+ memset(&tm, 0, sizeof(tm));
363
+ if ('-' == *s) {
364
+ s++;
365
+ neg = true;
366
+ }
367
+ if (NULL == (s = read_num(s, 4, &tm.tm_year))) {
368
+ return Qnil;
369
+ }
370
+ if (neg) {
371
+ tm.tm_year = -tm.tm_year;
372
+ neg = false;
373
+ }
374
+ tm.tm_year -= 1900;
375
+ s++;
376
+ if (NULL == (s = read_num(s, 2, &tm.tm_mon))) {
377
+ return Qnil;
378
+ }
379
+ tm.tm_mon--;
380
+ s++;
381
+ if (NULL == (s = read_num(s, 2, &tm.tm_mday))) {
382
+ return Qnil;
383
+ }
384
+ s++;
385
+ if (NULL == (s = read_num(s, 2, &tm.tm_hour))) {
386
+ return Qnil;
387
+ }
388
+ s++;
389
+ if (NULL == (s = read_num(s, 2, &tm.tm_min))) {
390
+ return Qnil;
391
+ }
392
+ s++;
393
+ if (NULL == (s = read_num(s, 2, &tm.tm_sec))) {
394
+ return Qnil;
395
+ }
396
+ s++;
397
+
398
+ for (i = 9; 0 < i; i--, s++) {
399
+ if ('0' <= *s && *s <= '9') {
400
+ nsecs = nsecs * 10 + *s - '0';
401
+ } else {
402
+ return Qnil;
403
+ }
404
+ }
405
+ secs = (time_t)timegm(&tm);
406
+
407
+ return rb_funcall(rb_time_nano_new(secs, nsecs), oj_utc_id, 0);
408
+ }
409
+
410
+ static VALUE
411
+ protect_uri(VALUE rstr) {
412
+ return rb_funcall(resolve_uri_class(), oj_parse_id, 1, rstr);
413
+ }
414
+
415
+ static VALUE
416
+ cstr_to_rstr(const char *str, size_t len) {
417
+ volatile VALUE v = Qnil;
418
+
419
+ if (30 == len && '-' == str[4] && '-' == str[7] && 'T' == str[10] && ':' == str[13] && ':' == str[16] && '.' == str[19] && 'Z' == str[29]) {
420
+ if (Qnil != (v = time_parse(str, len))) {
421
+ return v;
422
+ }
423
+ }
424
+ if (36 == len && '-' == str[8] && '-' == str[13] && '-' == str[18] && '-' == str[23] && uuid_check(str, len) && Qnil != resolve_wab_uuid_class()) {
425
+ return rb_funcall(wab_uuid_clas, oj_new_id, 1, rb_str_new(str, len));
426
+ }
427
+ v = rb_str_new(str, len);
428
+ if (7 < len && 0 == strncasecmp("http://", str, 7)) {
429
+ int err = 0;
430
+ volatile VALUE uri = rb_protect(protect_uri, v, &err);
431
+
432
+ if (0 == err) {
433
+ return uri;
434
+ }
435
+ }
436
+ return oj_encode(v);
437
+ }
438
+
439
+ static void
440
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
441
+ pi->stack.head->val = cstr_to_rstr(str, len);
442
+ }
443
+
444
+ static void
445
+ add_num(ParseInfo pi, NumInfo ni) {
446
+ if (ni->infinity || ni->nan) {
447
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
448
+ }
449
+ pi->stack.head->val = oj_num_as_value(ni);
450
+ }
451
+
452
+ static VALUE
453
+ start_hash(ParseInfo pi) {
454
+ if (Qnil != pi->options.hash_class) {
455
+ return rb_class_new_instance(0, NULL, pi->options.hash_class);
456
+ }
457
+ return rb_hash_new();
458
+ }
459
+
460
+ static VALUE
461
+ calc_hash_key(ParseInfo pi, Val parent) {
462
+ volatile VALUE rkey = parent->key_val;
463
+
464
+ if (Qundef == rkey) {
465
+ rkey = rb_str_new(parent->key, parent->klen);
466
+ }
467
+ rkey = oj_encode(rkey);
468
+ rkey = rb_str_intern(rkey);
469
+
470
+ return rkey;
471
+ }
472
+
473
+ static void
474
+ hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len, const char *orig) {
475
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), cstr_to_rstr(str, len));
476
+ }
477
+
478
+ static void
479
+ hash_set_num(struct _ParseInfo *pi, Val parent, NumInfo ni) {
480
+ if (ni->infinity || ni->nan) {
481
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
482
+ }
483
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
484
+ }
485
+
486
+ static void
487
+ hash_set_value(ParseInfo pi, Val parent, VALUE value) {
488
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
489
+ }
490
+
491
+ static VALUE
492
+ start_array(ParseInfo pi) {
493
+ return rb_ary_new();
494
+ }
495
+
496
+ static void
497
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
498
+ rb_ary_push(stack_peek(&pi->stack)->val, cstr_to_rstr(str, len));
499
+ }
500
+
501
+ static void
502
+ array_append_num(ParseInfo pi, NumInfo ni) {
503
+ if (ni->infinity || ni->nan) {
504
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
505
+ }
506
+ rb_ary_push(stack_peek(&pi->stack)->val, oj_num_as_value(ni));
507
+ }
508
+
509
+ static void
510
+ array_append_value(ParseInfo pi, VALUE value) {
511
+ rb_ary_push(stack_peek(&pi->stack)->val, value);
512
+ }
513
+
514
+ void
515
+ oj_set_wab_callbacks(ParseInfo pi) {
516
+ pi->start_hash = start_hash;
517
+ pi->end_hash = noop_end;
518
+ pi->hash_key = noop_hash_key;
519
+ pi->hash_set_cstr = hash_set_cstr;
520
+ pi->hash_set_num = hash_set_num;
521
+ pi->hash_set_value = hash_set_value;
522
+ pi->start_array = start_array;
523
+ pi->end_array = noop_end;
524
+ pi->array_append_cstr = array_append_cstr;
525
+ pi->array_append_num = array_append_num;
526
+ pi->array_append_value = array_append_value;
527
+ pi->add_cstr = add_cstr;
528
+ pi->add_num = add_num;
529
+ pi->add_value = add_value;
530
+ pi->expect_value = 1;
531
+ }
532
+
533
+ VALUE
534
+ oj_wab_parse(int argc, VALUE *argv, VALUE self) {
535
+ struct _ParseInfo pi;
536
+
537
+ parse_info_init(&pi);
538
+ pi.options = oj_default_options;
539
+ pi.handler = Qnil;
540
+ pi.err_class = Qnil;
541
+ oj_set_wab_callbacks(&pi);
542
+
543
+ if (T_STRING == rb_type(*argv)) {
544
+ return oj_pi_parse(argc, argv, &pi, 0, 0, true);
545
+ } else {
546
+ return oj_pi_sparse(argc, argv, &pi, 0);
547
+ }
548
+ }
549
+
550
+ VALUE
551
+ oj_wab_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
552
+ struct _ParseInfo pi;
553
+
554
+ parse_info_init(&pi);
555
+ pi.options = oj_default_options;
556
+ pi.handler = Qnil;
557
+ pi.err_class = Qnil;
558
+ oj_set_wab_callbacks(&pi);
559
+
560
+ return oj_pi_parse(argc, argv, &pi, json, len, true);
561
+ }
562
+