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 +4 -4
- data/ext/oj/dump.c +2 -1
- data/ext/oj/dump.h +1 -0
- data/ext/oj/oj.c +59 -6
- data/ext/oj/oj.h +2 -0
- data/ext/oj/parse.h +1 -0
- data/ext/oj/wab.c +562 -0
- data/lib/oj/version.rb +1 -1
- data/pages/Modes.md +47 -40
- data/pages/WAB.md +13 -0
- data/test/perf_wab.rb +131 -0
- data/test/test_wab.rb +307 -0
- data/test/tests.rb +1 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1373424e4c2fd32c52eaea6561ddfff276babeb5
|
4
|
+
data.tar.gz: e35e9cd9d69496cc46838dcdf3da63a46908a657
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19cc74647936b9ca942b3e88fea44f93efd7d3bb6d703fca5c53ed7a3cefd3f12e0c48f32d71fdc2a264d68ad6dd2662edffddcee046ab3b65d5b1354487116b
|
7
|
+
data.tar.gz: 90ce6b08d284b3cded6aba7b946ce794751a1b85f6e5ff0f1e37337679263cf88641949f3fe5898a22db89bf5d71d9b7c36322de37a11ef31bf275965d69d364
|
data/ext/oj/dump.c
CHANGED
@@ -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
|
|
data/ext/oj/dump.h
CHANGED
@@ -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);
|
data/ext/oj/oj.c
CHANGED
@@ -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 (
|
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 :
|
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 :
|
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);
|
data/ext/oj/oj.h
CHANGED
@@ -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);
|
data/ext/oj/parse.h
CHANGED
@@ -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);
|
data/ext/oj/wab.c
ADDED
@@ -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
|
+
|