oj 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+