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 +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
|
+
|