oj 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +9 -23
- data/ext/oj/dump.c +160 -14
- data/ext/oj/extconf.rb +0 -1
- data/ext/oj/object.c +27 -5
- data/ext/oj/oj.c +207 -1
- data/ext/oj/oj.h +25 -0
- data/ext/oj/parse.c +6 -4
- data/ext/oj/scp.c +2 -2
- data/lib/oj/version.rb +1 -1
- data/test/perf_object.rb +3 -1
- data/test/test_fast.rb +1 -0
- data/test/test_gc.rb +1 -0
- data/test/test_mimic.rb +1 -0
- data/test/test_saj.rb +1 -0
- data/test/test_scp.rb +1 -0
- data/test/test_writer.rb +148 -0
- data/test/tests.rb +4 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ac1090fe61f4056ff0fb5c838297e76f356fe8c
|
4
|
+
data.tar.gz: e75c5ddbce19b8846c1cac9f5dca6994194aa9e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f509d7b7dfbd8b848a0ce4b388e962853e3db6b7e2c050639a559a837964f767e1e279b53ef3cd29c7a6ceb569ac10522f9f3726f892f7a0c13e586688a28540
|
7
|
+
data.tar.gz: f7bdb0ae179ac0e796625445447f1f0dbce85ff3b2d3af1181e16fea2128077112056c9e104b15a06e4d13b48ff630bec7fe675e316ea16500b4869edbf69287
|
data/README.md
CHANGED
@@ -20,31 +20,18 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
|
|
20
20
|
|
21
21
|
[![Build Status](https://secure.travis-ci.org/ohler55/oj.png?branch=master)](http://travis-ci.org/ohler55/oj)
|
22
22
|
|
23
|
-
### Current Release 2.2.3
|
24
23
|
|
25
|
-
|
24
|
+
### Current Release 2.3.0
|
26
25
|
|
27
|
-
-
|
26
|
+
- JRuby is no longer supported.
|
28
27
|
|
29
|
-
|
28
|
+
- Thanks to Stefan Kaes the support for structs has been optimized.
|
30
29
|
|
31
|
-
-
|
30
|
+
- Better support for Rubinous
|
32
31
|
|
33
|
-
-
|
32
|
+
- Added option to disable GG during parsing.
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
- Made all VALUEs in parse volatile to avoid garbage collection while in use.
|
38
|
-
|
39
|
-
### Current Release 2.2.0
|
40
|
-
|
41
|
-
- All 1.8.x versions of Ruby now have require 'rational' called.
|
42
|
-
|
43
|
-
- Removed the temporary GC disable and implemented mark strategy instead.
|
44
|
-
|
45
|
-
- Added new character encoding mode to support the Rails 4 escape characters of &, <, and > as xss_safe mode. The :encoding option replaces the :ascii_only option.
|
46
|
-
|
47
|
-
- Change parsing of NaN to not use math.h which on older systems does not define NAN.
|
34
|
+
- Added StringWriter that allows building a JSON document one element at a time.
|
48
35
|
|
49
36
|
[Older release notes](http://www.ohler.com/dev/oj_misc/release_notes.html).
|
50
37
|
|
@@ -76,10 +63,9 @@ is the :object mode.
|
|
76
63
|
preference over the to_json() method. If neither the to_json() or to_hash()
|
77
64
|
methods exist then the Oj internal Object variable encoding is used.
|
78
65
|
|
79
|
-
Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, 2.0.0,
|
80
|
-
|
81
|
-
|
82
|
-
build with extensions enabled. Check the documenation for JRuby installs in your environment.
|
66
|
+
Oj is compatible with Ruby 1.8.7, 1.9.2, 1.9.3, 2.0.0, and RBX. Support for
|
67
|
+
JRuby has been removed as JRuby no longer supports C extensions and there are
|
68
|
+
bugs in the older versions that are not being fixed.
|
83
69
|
|
84
70
|
Oj is also compatible with Rails. Just make sure the Oj gem is installed and
|
85
71
|
[multi_json](https://github.com/intridea/multi_json) will pick it up and use it.
|
data/ext/oj/dump.c
CHANGED
@@ -84,10 +84,8 @@ static void dump_data_comp(VALUE obj, int depth, Out out);
|
|
84
84
|
static void dump_data_obj(VALUE obj, int depth, Out out);
|
85
85
|
static void dump_obj_comp(VALUE obj, int depth, Out out);
|
86
86
|
static void dump_obj_obj(VALUE obj, int depth, Out out);
|
87
|
-
#if HAS_RSTRUCT
|
88
87
|
static void dump_struct_comp(VALUE obj, int depth, Out out);
|
89
88
|
static void dump_struct_obj(VALUE obj, int depth, Out out);
|
90
|
-
#endif
|
91
89
|
#if HAS_IVAR_HELPERS
|
92
90
|
static int dump_attr_cb(ID key, VALUE value, Out out);
|
93
91
|
#endif
|
@@ -1484,7 +1482,6 @@ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
|
|
1484
1482
|
*out->cur = '\0';
|
1485
1483
|
}
|
1486
1484
|
|
1487
|
-
#if HAS_RSTRUCT
|
1488
1485
|
static void
|
1489
1486
|
dump_struct_comp(VALUE obj, int depth, Out out) {
|
1490
1487
|
if (rb_respond_to(obj, oj_to_hash_id)) {
|
@@ -1515,13 +1512,12 @@ static void
|
|
1515
1512
|
dump_struct_obj(VALUE obj, int depth, Out out) {
|
1516
1513
|
VALUE clas = rb_obj_class(obj);
|
1517
1514
|
const char *class_name = rb_class2name(clas);
|
1518
|
-
VALUE *vp;
|
1519
1515
|
int i;
|
1520
1516
|
int d2 = depth + 1;
|
1521
1517
|
int d3 = d2 + 1;
|
1522
1518
|
size_t len = strlen(class_name);
|
1523
1519
|
size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
|
1524
|
-
|
1520
|
+
|
1525
1521
|
if (out->end - out->cur <= (long)size) {
|
1526
1522
|
grow(out, size);
|
1527
1523
|
}
|
@@ -1540,20 +1536,40 @@ dump_struct_obj(VALUE obj, int depth, Out out) {
|
|
1540
1536
|
*out->cur++ = '"';
|
1541
1537
|
*out->cur++ = ',';
|
1542
1538
|
size = d3 * out->indent + 2;
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1539
|
+
#ifdef RSTRUCT_LEN
|
1540
|
+
{
|
1541
|
+
VALUE *vp;
|
1542
|
+
|
1543
|
+
for (i = (int)RSTRUCT_LEN(obj), vp = RSTRUCT_PTR(obj); 0 < i; i--, vp++) {
|
1544
|
+
if (out->end - out->cur <= (long)size) {
|
1545
|
+
grow(out, size);
|
1546
|
+
}
|
1547
|
+
fill_indent(out, d3);
|
1548
|
+
dump_val(*vp, d3, out);
|
1549
|
+
*out->cur++ = ',';
|
1550
|
+
}
|
1551
|
+
}
|
1552
|
+
#else
|
1553
|
+
{
|
1554
|
+
// This is a bit risky as a struct in C ruby is not the same as a Struct
|
1555
|
+
// class in interpreted Ruby so length() may not be defined.
|
1556
|
+
int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
|
1557
|
+
|
1558
|
+
for (i = 0; i < slen; i++) {
|
1559
|
+
if (out->end - out->cur <= (long)size) {
|
1560
|
+
grow(out, size);
|
1561
|
+
}
|
1562
|
+
fill_indent(out, d3);
|
1563
|
+
dump_val(rb_struct_aref(obj, INT2FIX(i)), d3, out);
|
1564
|
+
*out->cur++ = ',';
|
1546
1565
|
}
|
1547
|
-
fill_indent(out, d3);
|
1548
|
-
dump_val(*vp, d3, out);
|
1549
|
-
*out->cur++ = ',';
|
1550
1566
|
}
|
1567
|
+
#endif
|
1551
1568
|
out->cur--;
|
1552
1569
|
*out->cur++ = ']';
|
1553
1570
|
*out->cur++ = '}';
|
1554
1571
|
*out->cur = '\0';
|
1555
1572
|
}
|
1556
|
-
#endif
|
1557
1573
|
|
1558
1574
|
static void
|
1559
1575
|
dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
|
@@ -1669,7 +1685,7 @@ dump_val(VALUE obj, int depth, Out out) {
|
|
1669
1685
|
default: dump_data_obj(obj, depth, out); break;
|
1670
1686
|
}
|
1671
1687
|
break;
|
1672
|
-
|
1688
|
+
|
1673
1689
|
case T_STRUCT: // for Range
|
1674
1690
|
switch (out->opts->mode) {
|
1675
1691
|
case StrictMode: raise_strict(obj); break;
|
@@ -1679,7 +1695,7 @@ dump_val(VALUE obj, int depth, Out out) {
|
|
1679
1695
|
default: dump_struct_obj(obj, depth, out); break;
|
1680
1696
|
}
|
1681
1697
|
break;
|
1682
|
-
|
1698
|
+
|
1683
1699
|
#if (defined T_COMPLEX && defined RCOMPLEX)
|
1684
1700
|
case T_COMPLEX:
|
1685
1701
|
#endif
|
@@ -2010,3 +2026,133 @@ oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
|
|
2010
2026
|
}
|
2011
2027
|
fclose(f);
|
2012
2028
|
}
|
2029
|
+
|
2030
|
+
// string writer functions
|
2031
|
+
|
2032
|
+
static void
|
2033
|
+
key_check(StrWriter sw, const char *key) {
|
2034
|
+
DumpType type = sw->types[sw->depth];
|
2035
|
+
|
2036
|
+
if (0 == key && (ObjectNew == type || ObjectType == type)) {
|
2037
|
+
rb_raise(rb_eStandardError, "Can not push onto an Object without a key.");
|
2038
|
+
} else if (0 != key && (ArrayNew == type || ArrayType == type)) {
|
2039
|
+
rb_raise(rb_eStandardError, "No key is needed to push into an array.");
|
2040
|
+
}
|
2041
|
+
}
|
2042
|
+
|
2043
|
+
static void
|
2044
|
+
push_type(StrWriter sw, DumpType type) {
|
2045
|
+
if (sw->types_end <= sw->types + sw->depth + 1) {
|
2046
|
+
size_t size = (sw->types_end - sw->types) * 2;
|
2047
|
+
|
2048
|
+
REALLOC_N(sw->types, char, size);
|
2049
|
+
sw->types_end = sw->types + size;
|
2050
|
+
}
|
2051
|
+
sw->depth++;
|
2052
|
+
sw->types[sw->depth] = type;
|
2053
|
+
}
|
2054
|
+
|
2055
|
+
static void
|
2056
|
+
maybe_comma(StrWriter sw) {
|
2057
|
+
switch (sw->types[sw->depth]) {
|
2058
|
+
case ObjectNew:
|
2059
|
+
sw->types[sw->depth] = ObjectType;
|
2060
|
+
break;
|
2061
|
+
case ArrayNew:
|
2062
|
+
sw->types[sw->depth] = ArrayType;
|
2063
|
+
break;
|
2064
|
+
case ObjectType:
|
2065
|
+
case ArrayType:
|
2066
|
+
// Always have a few characters available in the out.buf.
|
2067
|
+
*sw->out.cur++ = ',';
|
2068
|
+
break;
|
2069
|
+
}
|
2070
|
+
}
|
2071
|
+
|
2072
|
+
void
|
2073
|
+
oj_str_writer_push_object(StrWriter sw, const char *key) {
|
2074
|
+
size_t size;
|
2075
|
+
|
2076
|
+
key_check(sw, key);
|
2077
|
+
size = sw->depth * sw->out.indent + 3;
|
2078
|
+
if (sw->out.end - sw->out.cur <= size) {
|
2079
|
+
grow(&sw->out, size);
|
2080
|
+
}
|
2081
|
+
maybe_comma(sw);
|
2082
|
+
fill_indent(&sw->out, sw->depth);
|
2083
|
+
if (0 != key) {
|
2084
|
+
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2085
|
+
*sw->out.cur++ = ':';
|
2086
|
+
}
|
2087
|
+
*sw->out.cur++ = '{';
|
2088
|
+
push_type(sw, ObjectNew);
|
2089
|
+
}
|
2090
|
+
|
2091
|
+
void
|
2092
|
+
oj_str_writer_push_array(StrWriter sw, const char *key) {
|
2093
|
+
size_t size;
|
2094
|
+
|
2095
|
+
key_check(sw, key);
|
2096
|
+
size = sw->depth * sw->out.indent + 3;
|
2097
|
+
if (sw->out.end - sw->out.cur <= size) {
|
2098
|
+
grow(&sw->out, size);
|
2099
|
+
}
|
2100
|
+
maybe_comma(sw);
|
2101
|
+
if (0 != key) {
|
2102
|
+
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2103
|
+
*sw->out.cur++ = ':';
|
2104
|
+
}
|
2105
|
+
*sw->out.cur++ = '[';
|
2106
|
+
push_type(sw, ArrayNew);
|
2107
|
+
}
|
2108
|
+
|
2109
|
+
void
|
2110
|
+
oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
|
2111
|
+
size_t size;
|
2112
|
+
|
2113
|
+
key_check(sw, key);
|
2114
|
+
size = sw->depth * sw->out.indent + 3;
|
2115
|
+
if (sw->out.end - sw->out.cur <= size) {
|
2116
|
+
grow(&sw->out, size);
|
2117
|
+
}
|
2118
|
+
maybe_comma(sw);
|
2119
|
+
if (0 != key) {
|
2120
|
+
dump_cstr(key, strlen(key), 0, 0, &sw->out);
|
2121
|
+
*sw->out.cur++ = ':';
|
2122
|
+
}
|
2123
|
+
dump_val(val, sw->depth, &sw->out);
|
2124
|
+
}
|
2125
|
+
|
2126
|
+
void
|
2127
|
+
oj_str_writer_pop(StrWriter sw) {
|
2128
|
+
size_t size;
|
2129
|
+
DumpType type = sw->types[sw->depth];
|
2130
|
+
|
2131
|
+
sw->depth--;
|
2132
|
+
if (0 > sw->depth) {
|
2133
|
+
rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
|
2134
|
+
}
|
2135
|
+
size = sw->depth * sw->out.indent + 2;
|
2136
|
+
if (sw->out.end - sw->out.cur <= size) {
|
2137
|
+
grow(&sw->out, size);
|
2138
|
+
}
|
2139
|
+
fill_indent(&sw->out, sw->depth);
|
2140
|
+
switch (type) {
|
2141
|
+
case ObjectNew:
|
2142
|
+
case ObjectType:
|
2143
|
+
*sw->out.cur++ = '}';
|
2144
|
+
break;
|
2145
|
+
case ArrayNew:
|
2146
|
+
case ArrayType:
|
2147
|
+
*sw->out.cur++ = ']';
|
2148
|
+
break;
|
2149
|
+
}
|
2150
|
+
}
|
2151
|
+
|
2152
|
+
void
|
2153
|
+
oj_str_writer_pop_all(StrWriter sw) {
|
2154
|
+
while (0 < sw->depth) {
|
2155
|
+
oj_str_writer_pop(sw);
|
2156
|
+
}
|
2157
|
+
}
|
2158
|
+
|
data/ext/oj/extconf.rb
CHANGED
@@ -24,7 +24,6 @@ dflags = {
|
|
24
24
|
'HAS_ENCODING_SUPPORT' => (('ruby' == type || 'rubinius' == type) &&
|
25
25
|
(('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
|
26
26
|
'HAS_NANO_TIME' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
|
27
|
-
'HAS_RSTRUCT' => ('ruby' == type || 'ree' == type || 'tcs-ruby' == type) ? 1 : 0,
|
28
27
|
'HAS_IVAR_HELPERS' => ('ruby' == type && !is_windows && (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
|
29
28
|
'HAS_EXCEPTION_MAGIC' => ('ruby' == type && ('1' == version[0] && '9' == version[1])) ? 0 : 1,
|
30
29
|
'HAS_PROC_WITH_BLOCK' => ('ruby' == type && (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
|
data/ext/oj/object.c
CHANGED
@@ -185,17 +185,39 @@ hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, volatile VALUE
|
|
185
185
|
int len = (int)RARRAY_LEN(value);
|
186
186
|
|
187
187
|
if (2 == klen && 'u' == key[1]) {
|
188
|
-
|
188
|
+
VALUE sc;
|
189
189
|
|
190
190
|
if (0 == len) {
|
191
191
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
|
192
192
|
return 1;
|
193
193
|
}
|
194
|
-
// If struct is not defined
|
195
|
-
// let this fail and raise an exception.
|
194
|
+
// If struct is not defined then we let this fail and raise an exception.
|
196
195
|
sc = rb_const_get(oj_struct_class, rb_to_id(*RARRAY_PTR(value)));
|
197
|
-
|
198
|
-
|
196
|
+
// Create a properly initialized struct instance without calling the initialize method.
|
197
|
+
parent->val = rb_obj_alloc(sc);
|
198
|
+
// If the JSON array has more entries than the struct class allows, we record an error.
|
199
|
+
#ifdef RSTRUCT_LEN
|
200
|
+
// MRI >= 1.9
|
201
|
+
if (len - 1 > RSTRUCT_LEN(parent->val)) {
|
202
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
|
203
|
+
} else {
|
204
|
+
MEMCPY(RSTRUCT_PTR(parent->val), RARRAY_PTR(value) + 1, VALUE, len - 1);
|
205
|
+
}
|
206
|
+
#else
|
207
|
+
{
|
208
|
+
// MRI < 1.9 or Rubinius
|
209
|
+
int slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
|
210
|
+
int i;
|
211
|
+
|
212
|
+
if (len - 1 > slen) {
|
213
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
|
214
|
+
} else {
|
215
|
+
for (i = 0; i < slen; i++) {
|
216
|
+
rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
#endif
|
199
221
|
return 1;
|
200
222
|
} else if (3 <= klen && '#' == key[1]) {
|
201
223
|
volatile VALUE *a;
|
data/ext/oj/oj.c
CHANGED
@@ -64,6 +64,7 @@ ID oj_hash_start_id;
|
|
64
64
|
ID oj_iconv_id;
|
65
65
|
ID oj_instance_variables_id;
|
66
66
|
ID oj_json_create_id;
|
67
|
+
ID oj_length_id;
|
67
68
|
ID oj_new_id;
|
68
69
|
ID oj_read_id;
|
69
70
|
ID oj_string_id;
|
@@ -84,12 +85,15 @@ VALUE oj_cstack_class;
|
|
84
85
|
VALUE oj_date_class;
|
85
86
|
VALUE oj_datetime_class;
|
86
87
|
VALUE oj_parse_error_class;
|
88
|
+
VALUE oj_stream_writer_class;
|
89
|
+
VALUE oj_string_writer_class;
|
87
90
|
VALUE oj_stringio_class;
|
88
91
|
VALUE oj_struct_class;
|
89
92
|
VALUE oj_time_class;
|
90
93
|
|
91
94
|
VALUE oj_slash_string;
|
92
95
|
|
96
|
+
static VALUE allow_gc_sym;
|
93
97
|
static VALUE ascii_only_sym;
|
94
98
|
static VALUE ascii_sym;
|
95
99
|
static VALUE auto_define_sym;
|
@@ -153,6 +157,7 @@ struct _Options oj_default_options = {
|
|
153
157
|
json_class, // create_id
|
154
158
|
10, // create_id_len
|
155
159
|
9, // sec_prec
|
160
|
+
Yes, // allow_gc
|
156
161
|
0, // dump_opts
|
157
162
|
};
|
158
163
|
|
@@ -173,6 +178,7 @@ static VALUE define_mimic_json(int argc, VALUE *argv, VALUE self);
|
|
173
178
|
* - bigdecimal_load: [:bigdecimal|:float|:auto] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
|
174
179
|
* - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
|
175
180
|
* - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
|
181
|
+
* - allow_gc: [true|false|nil] allow or prohibit GC during parsing, default is true (allow)
|
176
182
|
* @return [Hash] all current option settings.
|
177
183
|
*/
|
178
184
|
static VALUE
|
@@ -186,6 +192,7 @@ get_def_opts(VALUE self) {
|
|
186
192
|
rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
|
187
193
|
rb_hash_aset(opts, symbol_keys_sym, (Yes == oj_default_options.sym_key) ? Qtrue : ((No == oj_default_options.sym_key) ? Qfalse : Qnil));
|
188
194
|
rb_hash_aset(opts, bigdecimal_as_decimal_sym, (Yes == oj_default_options.bigdec_as_num) ? Qtrue : ((No == oj_default_options.bigdec_as_num) ? Qfalse : Qnil));
|
195
|
+
rb_hash_aset(opts, allow_gc_sym, (Yes == oj_default_options.allow_gc) ? Qtrue : ((No == oj_default_options.allow_gc) ? Qfalse : Qnil));
|
189
196
|
switch (oj_default_options.mode) {
|
190
197
|
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
|
191
198
|
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
|
@@ -244,6 +251,7 @@ get_def_opts(VALUE self) {
|
|
244
251
|
* :ruby Time.to_s formatted String
|
245
252
|
* @param [String|nil] :create_id create id for json compatible object encoding
|
246
253
|
* @param [Fixnum|nil] :second_precision number of digits after the decimal when dumping the seconds portion of time
|
254
|
+
* @param [true|false|nil] :allow_gc allow or prohibit GC during parsing, default is true (allow)
|
247
255
|
* @return [nil]
|
248
256
|
*/
|
249
257
|
static VALUE
|
@@ -254,6 +262,7 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
254
262
|
{ symbol_keys_sym, &oj_default_options.sym_key },
|
255
263
|
{ class_cache_sym, &oj_default_options.class_cache },
|
256
264
|
{ bigdecimal_as_decimal_sym, &oj_default_options.bigdec_as_num },
|
265
|
+
{ allow_gc_sym, &oj_default_options.allow_gc },
|
257
266
|
{ Qnil, 0 }
|
258
267
|
};
|
259
268
|
YesNoOpt o;
|
@@ -383,6 +392,7 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
383
392
|
{ symbol_keys_sym, &copts->sym_key },
|
384
393
|
{ class_cache_sym, &copts->class_cache },
|
385
394
|
{ bigdecimal_as_decimal_sym, &copts->bigdec_as_num },
|
395
|
+
{ allow_gc_sym, &copts->allow_gc },
|
386
396
|
{ Qnil, 0 }
|
387
397
|
};
|
388
398
|
YesNoOpt o;
|
@@ -817,6 +827,179 @@ to_stream(int argc, VALUE *argv, VALUE self) {
|
|
817
827
|
return Qnil;
|
818
828
|
}
|
819
829
|
|
830
|
+
static void
|
831
|
+
str_writer_free(void *ptr) {
|
832
|
+
StrWriter sw;
|
833
|
+
|
834
|
+
if (0 == ptr) {
|
835
|
+
return;
|
836
|
+
}
|
837
|
+
sw = (StrWriter)ptr;
|
838
|
+
xfree(sw->out.buf);
|
839
|
+
xfree(sw->types);
|
840
|
+
xfree(ptr);
|
841
|
+
}
|
842
|
+
|
843
|
+
/* Document-class: Oj::StringWriter
|
844
|
+
*
|
845
|
+
* Supports building a JSON document one element at a time. Build the document
|
846
|
+
* by pushing values into the document. Pushing an array or an object will
|
847
|
+
* create that element in the JSON document and subsequent pushes will add the
|
848
|
+
* elements to that array or object until a pop() is called. When complete
|
849
|
+
* calling to_s() will return the JSON document. Note tha calling to_s() before
|
850
|
+
* construction is complete will return the document in it's current state.
|
851
|
+
*/
|
852
|
+
|
853
|
+
/* call-seq: new(options)
|
854
|
+
*
|
855
|
+
* Creates a new StringWriter.
|
856
|
+
* @param [Hash] options formating options
|
857
|
+
*/
|
858
|
+
static VALUE
|
859
|
+
str_writer_new(int argc, VALUE *argv, VALUE self) {
|
860
|
+
StrWriter sw = ALLOC(struct _StrWriter);
|
861
|
+
|
862
|
+
sw->opts = oj_default_options;
|
863
|
+
sw->depth = 0;
|
864
|
+
sw->types = ALLOC_N(char, 256);
|
865
|
+
sw->types_end = sw->types + 256;
|
866
|
+
*sw->types = '\0';
|
867
|
+
sw->out.buf = ALLOC_N(char, 4096);
|
868
|
+
sw->out.end = sw->out.buf + 4086;
|
869
|
+
sw->out.allocated = 1;
|
870
|
+
sw->out.cur = sw->out.buf;
|
871
|
+
*sw->out.cur = '\0';
|
872
|
+
sw->out.circ_cnt = 0;
|
873
|
+
sw->out.hash_cnt = 0;
|
874
|
+
if (1 == argc) {
|
875
|
+
oj_parse_options(argv[0], &sw->opts);
|
876
|
+
}
|
877
|
+
sw->out.opts = &sw->opts;
|
878
|
+
sw->out.indent = sw->opts.indent;
|
879
|
+
|
880
|
+
return Data_Wrap_Struct(oj_string_writer_class, 0, str_writer_free, sw);
|
881
|
+
}
|
882
|
+
|
883
|
+
/* call-seq: push_object(key=nil)
|
884
|
+
*
|
885
|
+
* Pushes an object onto the JSON document. Future pushes will be to this object
|
886
|
+
* until a pop() is called.
|
887
|
+
* @param [String] key the key if adding to an object in the JSON document
|
888
|
+
*/
|
889
|
+
static VALUE
|
890
|
+
str_writer_push_object(int argc, VALUE *argv, VALUE self) {
|
891
|
+
switch (argc) {
|
892
|
+
case 0:
|
893
|
+
oj_str_writer_push_object((StrWriter)DATA_PTR(self), 0);
|
894
|
+
break;
|
895
|
+
case 1:
|
896
|
+
rb_check_type(argv[0], T_STRING);
|
897
|
+
oj_str_writer_push_object((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
|
898
|
+
break;
|
899
|
+
default:
|
900
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
|
901
|
+
break;
|
902
|
+
}
|
903
|
+
return Qnil;
|
904
|
+
}
|
905
|
+
|
906
|
+
/* call-seq: push_array(key=nil)
|
907
|
+
*
|
908
|
+
* Pushes an array onto the JSON document. Future pushes will be to this object
|
909
|
+
* until a pop() is called.
|
910
|
+
* @param [String] key the key if adding to an object in the JSON document
|
911
|
+
*/
|
912
|
+
static VALUE
|
913
|
+
str_writer_push_array(int argc, VALUE *argv, VALUE self) {
|
914
|
+
switch (argc) {
|
915
|
+
case 0:
|
916
|
+
oj_str_writer_push_array((StrWriter)DATA_PTR(self), 0);
|
917
|
+
break;
|
918
|
+
case 1:
|
919
|
+
rb_check_type(argv[0], T_STRING);
|
920
|
+
oj_str_writer_push_array((StrWriter)DATA_PTR(self), StringValuePtr(argv[0]));
|
921
|
+
break;
|
922
|
+
default:
|
923
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_object'.");
|
924
|
+
break;
|
925
|
+
}
|
926
|
+
return Qnil;
|
927
|
+
}
|
928
|
+
|
929
|
+
/* call-seq: push_value(value, key=nil)
|
930
|
+
*
|
931
|
+
* Pushes a value onto the JSON document.
|
932
|
+
* @param [Object] value value to add to the JSON document
|
933
|
+
* @param [String] key the key if adding to an object in the JSON document
|
934
|
+
*/
|
935
|
+
static VALUE
|
936
|
+
str_writer_push_value(int argc, VALUE *argv, VALUE self) {
|
937
|
+
switch (argc) {
|
938
|
+
case 1:
|
939
|
+
oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, 0);
|
940
|
+
break;
|
941
|
+
case 2:
|
942
|
+
rb_check_type(argv[1], T_STRING);
|
943
|
+
oj_str_writer_push_value((StrWriter)DATA_PTR(self), *argv, StringValuePtr(argv[1]));
|
944
|
+
break;
|
945
|
+
default:
|
946
|
+
rb_raise(rb_eArgError, "Wrong number of argument to 'push_value'.");
|
947
|
+
break;
|
948
|
+
}
|
949
|
+
return Qnil;
|
950
|
+
}
|
951
|
+
|
952
|
+
/* call-seq: pop()
|
953
|
+
*
|
954
|
+
* Pops up a level in the JSON document closing the array or object that is
|
955
|
+
* currently open.
|
956
|
+
*/
|
957
|
+
static VALUE
|
958
|
+
str_writer_pop(VALUE self) {
|
959
|
+
oj_str_writer_pop((StrWriter)DATA_PTR(self));
|
960
|
+
return Qnil;
|
961
|
+
}
|
962
|
+
|
963
|
+
/* call-seq: pop_all()
|
964
|
+
*
|
965
|
+
* Pops all level in the JSON document closing all the array or object that is
|
966
|
+
* currently open.
|
967
|
+
*/
|
968
|
+
static VALUE
|
969
|
+
str_writer_pop_all(VALUE self) {
|
970
|
+
oj_str_writer_pop_all((StrWriter)DATA_PTR(self));
|
971
|
+
|
972
|
+
return Qnil;
|
973
|
+
}
|
974
|
+
|
975
|
+
/* call-seq: reset()
|
976
|
+
*
|
977
|
+
* Reset the writer back to the empty state.
|
978
|
+
*/
|
979
|
+
static VALUE
|
980
|
+
str_writer_reset(VALUE self) {
|
981
|
+
StrWriter sw = (StrWriter)DATA_PTR(self);
|
982
|
+
|
983
|
+
sw->depth = 0;
|
984
|
+
*sw->types = '\0';
|
985
|
+
sw->out.cur = sw->out.buf;
|
986
|
+
*sw->out.cur = '\0';
|
987
|
+
|
988
|
+
return Qnil;
|
989
|
+
}
|
990
|
+
|
991
|
+
/* call-seq: to_s()
|
992
|
+
*
|
993
|
+
* Returns the JSON document string in what ever state the construction is at.
|
994
|
+
*/
|
995
|
+
static VALUE
|
996
|
+
str_writer_to_s(VALUE self) {
|
997
|
+
StrWriter sw = (StrWriter)DATA_PTR(self);
|
998
|
+
VALUE rstr = rb_str_new(sw->out.buf, sw->out.cur - sw->out.buf);
|
999
|
+
|
1000
|
+
return oj_encode(rstr);
|
1001
|
+
}
|
1002
|
+
|
820
1003
|
// Mimic JSON section
|
821
1004
|
|
822
1005
|
static VALUE
|
@@ -1195,14 +1378,35 @@ iconv_rescue(VALUE x) {
|
|
1195
1378
|
}
|
1196
1379
|
#endif
|
1197
1380
|
|
1381
|
+
static VALUE
|
1382
|
+
protect_require(VALUE x) {
|
1383
|
+
rb_require("bigdecimal");
|
1384
|
+
return Qnil;
|
1385
|
+
}
|
1386
|
+
|
1198
1387
|
void Init_oj() {
|
1388
|
+
int err = 0;
|
1389
|
+
|
1199
1390
|
Oj = rb_define_module("Oj");
|
1200
1391
|
|
1201
1392
|
oj_cstack_class = rb_define_class_under(Oj, "CStack", rb_cObject);
|
1202
1393
|
|
1394
|
+
oj_string_writer_class = rb_define_class_under(Oj, "StringWriter", rb_cObject);
|
1395
|
+
rb_define_module_function(oj_string_writer_class, "new", str_writer_new, -1);
|
1396
|
+
rb_define_method(oj_string_writer_class, "push_object", str_writer_push_object, -1);
|
1397
|
+
rb_define_method(oj_string_writer_class, "push_array", str_writer_push_array, -1);
|
1398
|
+
rb_define_method(oj_string_writer_class, "push_value", str_writer_push_value, -1);
|
1399
|
+
rb_define_method(oj_string_writer_class, "pop", str_writer_pop, 0);
|
1400
|
+
rb_define_method(oj_string_writer_class, "pop_all", str_writer_pop_all, 0);
|
1401
|
+
rb_define_method(oj_string_writer_class, "reset", str_writer_reset, 0);
|
1402
|
+
rb_define_method(oj_string_writer_class, "to_s", str_writer_to_s, 0);
|
1403
|
+
|
1404
|
+
//oj_stream_writer_class = rb_define_class_under(Oj, "StreamWriter", rb_cObject);
|
1405
|
+
|
1203
1406
|
rb_require("time");
|
1204
1407
|
rb_require("date");
|
1205
|
-
|
1408
|
+
// On Rubinius the require fails but can be done from a ruby file.
|
1409
|
+
rb_protect(protect_require, Qnil, &err);
|
1206
1410
|
#if NEEDS_RATIONAL
|
1207
1411
|
rb_require("rational");
|
1208
1412
|
#endif
|
@@ -1248,6 +1452,7 @@ void Init_oj() {
|
|
1248
1452
|
oj_iconv_id = rb_intern("iconv");
|
1249
1453
|
oj_instance_variables_id = rb_intern("instance_variables");
|
1250
1454
|
oj_json_create_id = rb_intern("json_create");
|
1455
|
+
oj_length_id = rb_intern("length");
|
1251
1456
|
oj_new_id = rb_intern("new");
|
1252
1457
|
oj_read_id = rb_intern("read");
|
1253
1458
|
oj_string_id = rb_intern("string");
|
@@ -1271,6 +1476,7 @@ void Init_oj() {
|
|
1271
1476
|
oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
|
1272
1477
|
oj_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
|
1273
1478
|
|
1479
|
+
allow_gc_sym = ID2SYM(rb_intern("allow_gc")); rb_gc_register_address(&allow_gc_sym);
|
1274
1480
|
ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&ascii_only_sym);
|
1275
1481
|
ascii_sym = ID2SYM(rb_intern("ascii")); rb_gc_register_address(&ascii_sym);
|
1276
1482
|
auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
|
data/ext/oj/oj.h
CHANGED
@@ -98,6 +98,13 @@ typedef enum {
|
|
98
98
|
AutoDec = 'a'
|
99
99
|
} BigLoad;
|
100
100
|
|
101
|
+
typedef enum {
|
102
|
+
ArrayNew = 'A',
|
103
|
+
ArrayType = 'a',
|
104
|
+
ObjectNew = 'O',
|
105
|
+
ObjectType = 'o',
|
106
|
+
} DumpType;
|
107
|
+
|
101
108
|
typedef struct _DumpOpts {
|
102
109
|
const char *indent;
|
103
110
|
const char *before_sep;
|
@@ -125,6 +132,7 @@ typedef struct _Options {
|
|
125
132
|
const char *create_id; // 0 or string
|
126
133
|
size_t create_id_len; // length of create_id
|
127
134
|
int sec_prec; // second precision when dumping time
|
135
|
+
char allow_gc; // allow GC during parse
|
128
136
|
DumpOpts dump_opts;
|
129
137
|
} *Options;
|
130
138
|
|
@@ -141,6 +149,14 @@ typedef struct _Out {
|
|
141
149
|
int allocated;
|
142
150
|
} *Out;
|
143
151
|
|
152
|
+
typedef struct _StrWriter {
|
153
|
+
struct _Out out;
|
154
|
+
struct _Options opts;
|
155
|
+
int depth;
|
156
|
+
char *types; // DumpType
|
157
|
+
char *types_end;
|
158
|
+
} *StrWriter;
|
159
|
+
|
144
160
|
enum {
|
145
161
|
STR_VAL = 0x00,
|
146
162
|
COL_VAL = 0x01,
|
@@ -182,6 +198,12 @@ extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
|
|
182
198
|
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
183
199
|
extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
|
184
200
|
|
201
|
+
extern void oj_str_writer_push_object(StrWriter sw, const char *key);
|
202
|
+
extern void oj_str_writer_push_array(StrWriter sw, const char *key);
|
203
|
+
extern void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key);
|
204
|
+
extern void oj_str_writer_pop(StrWriter sw);
|
205
|
+
extern void oj_str_writer_pop_all(StrWriter sw);
|
206
|
+
|
185
207
|
extern void oj_init_doc(void);
|
186
208
|
|
187
209
|
extern VALUE Oj;
|
@@ -198,6 +220,8 @@ extern VALUE oj_cstack_class;
|
|
198
220
|
extern VALUE oj_date_class;
|
199
221
|
extern VALUE oj_datetime_class;
|
200
222
|
extern VALUE oj_doc_class;
|
223
|
+
extern VALUE oj_stream_writer_class;
|
224
|
+
extern VALUE oj_string_writer_class;
|
201
225
|
extern VALUE oj_stringio_class;
|
202
226
|
extern VALUE oj_struct_class;
|
203
227
|
extern VALUE oj_time_class;
|
@@ -217,6 +241,7 @@ extern ID oj_hash_start_id;
|
|
217
241
|
extern ID oj_iconv_id;
|
218
242
|
extern ID oj_instance_variables_id;
|
219
243
|
extern ID oj_json_create_id;
|
244
|
+
extern ID oj_length_id;
|
220
245
|
extern ID oj_new_id;
|
221
246
|
extern ID oj_read_id;
|
222
247
|
extern ID oj_string_id;
|
data/ext/oj/parse.c
CHANGED
@@ -747,9 +747,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
|
|
747
747
|
if (oj_stringio_class == clas) {
|
748
748
|
s = rb_funcall2(input, oj_string_id, 0, 0);
|
749
749
|
pi->json = rb_string_value_cstr((VALUE*)&s);
|
750
|
-
#ifndef JRUBY_RUBY
|
751
750
|
#if !IS_WINDOWS
|
752
|
-
// JRuby gets confused with what is the real fileno.
|
753
751
|
} else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
|
754
752
|
int fd = FIX2INT(s);
|
755
753
|
ssize_t cnt;
|
@@ -769,7 +767,6 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
|
|
769
767
|
if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
|
770
768
|
pi->json += 3;
|
771
769
|
}
|
772
|
-
#endif
|
773
770
|
#endif
|
774
771
|
} else if (rb_respond_to(input, oj_read_id)) {
|
775
772
|
s = rb_funcall2(input, oj_read_id, 0, 0);
|
@@ -783,6 +780,9 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
|
|
783
780
|
} else {
|
784
781
|
pi->circ_array = 0;
|
785
782
|
}
|
783
|
+
if (No == pi->options.allow_gc) {
|
784
|
+
rb_gc_disable();
|
785
|
+
}
|
786
786
|
// GC can run at any time. When it runs any Object created by C will be
|
787
787
|
// freed. We protect against this by wrapping the value stack in a ruby
|
788
788
|
// data object and poviding a mark function for ruby objects on the
|
@@ -791,7 +791,9 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
|
|
791
791
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
792
792
|
result = stack_head_val(&pi->stack);
|
793
793
|
DATA_PTR(wrapped_stack) = 0;
|
794
|
-
|
794
|
+
if (No == pi->options.allow_gc) {
|
795
|
+
rb_gc_enable();
|
796
|
+
}
|
795
797
|
// proceed with cleanup
|
796
798
|
if (0 != pi->circ_array) {
|
797
799
|
oj_circ_array_free(pi->circ_array);
|
data/ext/oj/scp.c
CHANGED
@@ -102,7 +102,7 @@ noop_array_append_value(ParseInfo pi, VALUE value) {
|
|
102
102
|
|
103
103
|
static void
|
104
104
|
add_value(ParseInfo pi, VALUE val) {
|
105
|
-
|
105
|
+
rb_funcall((VALUE)pi->cbc, oj_add_value_id, 1, val);
|
106
106
|
}
|
107
107
|
|
108
108
|
static void
|
@@ -110,7 +110,7 @@ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
|
110
110
|
volatile VALUE rstr = rb_str_new(str, len);
|
111
111
|
|
112
112
|
rstr = oj_encode(rstr);
|
113
|
-
|
113
|
+
rb_funcall((VALUE)pi->cbc, oj_add_value_id, 1, rstr);
|
114
114
|
}
|
115
115
|
|
116
116
|
static void
|
data/lib/oj/version.rb
CHANGED
data/test/perf_object.rb
CHANGED
@@ -20,6 +20,7 @@ require 'files'
|
|
20
20
|
|
21
21
|
$circular = false
|
22
22
|
$indent = 0
|
23
|
+
$allow_gc = true
|
23
24
|
|
24
25
|
do_sample = false
|
25
26
|
do_files = false
|
@@ -35,6 +36,7 @@ opts = OptionParser.new
|
|
35
36
|
opts.on("-c", "circular options") { $circular = true }
|
36
37
|
|
37
38
|
opts.on("-x", "use sample instead of files") { do_sample = true }
|
39
|
+
opts.on("-g", "no GC during parsing") { $allow_gc = false }
|
38
40
|
|
39
41
|
opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
|
40
42
|
opts.on("-f", "load and dump as files Ruby object") { do_files = true }
|
@@ -86,7 +88,7 @@ else
|
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
89
|
-
Oj.default_options = { :mode => :object, :indent => $indent, :circular => $circular }
|
91
|
+
Oj.default_options = { :mode => :object, :indent => $indent, :circular => $circular, :allow_gc => $allow_gc }
|
90
92
|
#puts "json: #{$json.size}"
|
91
93
|
#puts "xml: #{$xml.size}"
|
92
94
|
#puts "marshal: #{$mars.size}"
|
data/test/test_fast.rb
CHANGED
data/test/test_gc.rb
CHANGED
data/test/test_mimic.rb
CHANGED
data/test/test_saj.rb
CHANGED
data/test/test_scp.rb
CHANGED
data/test/test_writer.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
|
5
|
+
# required. That can be set in the RUBYOPT environment variable.
|
6
|
+
# export RUBYOPT=-w
|
7
|
+
|
8
|
+
$VERBOSE = true
|
9
|
+
|
10
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
11
|
+
$: << File.join(File.dirname(__FILE__), "../ext")
|
12
|
+
|
13
|
+
require 'test/unit'
|
14
|
+
require 'stringio'
|
15
|
+
require 'date'
|
16
|
+
require 'bigdecimal'
|
17
|
+
require 'oj'
|
18
|
+
|
19
|
+
class OjWriter < ::Test::Unit::TestCase
|
20
|
+
|
21
|
+
def test_string_writer_empty_array
|
22
|
+
w = Oj::StringWriter.new(:indent => 0)
|
23
|
+
w.push_array()
|
24
|
+
w.pop()
|
25
|
+
assert_equal('[]', w.to_s)
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_string_writer_nested_array
|
30
|
+
w = Oj::StringWriter.new(:indent => 0)
|
31
|
+
w.push_array()
|
32
|
+
w.push_array()
|
33
|
+
w.pop()
|
34
|
+
w.push_array()
|
35
|
+
w.push_array()
|
36
|
+
w.pop()
|
37
|
+
w.pop()
|
38
|
+
w.push_array()
|
39
|
+
w.pop()
|
40
|
+
w.pop()
|
41
|
+
assert_equal('[[],[[]],[]]', w.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_string_writer_empty_object
|
45
|
+
w = Oj::StringWriter.new(:indent => 0)
|
46
|
+
w.push_object()
|
47
|
+
w.pop()
|
48
|
+
assert_equal('{}', w.to_s)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_string_writer_nested_object
|
52
|
+
w = Oj::StringWriter.new(:indent => 0)
|
53
|
+
w.push_object()
|
54
|
+
w.push_object("a1")
|
55
|
+
w.pop()
|
56
|
+
w.push_object("a2")
|
57
|
+
w.push_object("b")
|
58
|
+
w.pop()
|
59
|
+
w.pop()
|
60
|
+
w.push_object("a3")
|
61
|
+
w.pop()
|
62
|
+
w.pop()
|
63
|
+
assert_equal('{"a1":{},"a2":{"b":{}},"a3":{}}', w.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_string_writer_value_array
|
67
|
+
w = Oj::StringWriter.new(:indent => 0)
|
68
|
+
w.push_array()
|
69
|
+
w.push_value(7)
|
70
|
+
w.push_value(7.3)
|
71
|
+
w.push_value(true)
|
72
|
+
w.push_value(nil)
|
73
|
+
w.push_value("a string")
|
74
|
+
w.push_value({'a' => 65})
|
75
|
+
w.push_value([1,2])
|
76
|
+
w.pop()
|
77
|
+
assert_equal('[7,7.3,true,null,"a string",{"a":65},[1,2]]', w.to_s)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_string_writer_pop_excess
|
81
|
+
w = Oj::StringWriter.new(:indent => 0)
|
82
|
+
begin
|
83
|
+
w.pop()
|
84
|
+
rescue Exception
|
85
|
+
assert(true)
|
86
|
+
return
|
87
|
+
end
|
88
|
+
assert(false, "*** expected an exception")
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_string_writer_obj_no_key
|
92
|
+
w = Oj::StringWriter.new(:indent => 0)
|
93
|
+
w.push_object()
|
94
|
+
begin
|
95
|
+
w.push_value(59)
|
96
|
+
rescue Exception
|
97
|
+
assert(true)
|
98
|
+
return
|
99
|
+
end
|
100
|
+
assert(false, "*** expected an exception")
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_string_writer_array_with_key
|
104
|
+
w = Oj::StringWriter.new(:indent => 0)
|
105
|
+
w.push_array()
|
106
|
+
begin
|
107
|
+
w.push_value(59, 'x')
|
108
|
+
rescue Exception
|
109
|
+
assert(true)
|
110
|
+
return
|
111
|
+
end
|
112
|
+
assert(false, "*** expected an exception")
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_string_writer_deep
|
116
|
+
cnt = 1000
|
117
|
+
w = Oj::StringWriter.new(:indent => 0)
|
118
|
+
cnt.times do
|
119
|
+
w.push_array()
|
120
|
+
end
|
121
|
+
cnt.times do
|
122
|
+
w.pop()
|
123
|
+
end
|
124
|
+
# if no exception then passed
|
125
|
+
assert(true)
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_string_writer_pop_all
|
129
|
+
w = Oj::StringWriter.new(:indent => 0)
|
130
|
+
w.push_object()
|
131
|
+
w.push_object("a1")
|
132
|
+
w.pop()
|
133
|
+
w.push_array("a2")
|
134
|
+
w.push_value(3)
|
135
|
+
w.push_array()
|
136
|
+
w.pop_all()
|
137
|
+
assert_equal('{"a1":{},"a2":[3,[]]}', w.to_s)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_string_writer_reset
|
141
|
+
w = Oj::StringWriter.new(:indent => 0)
|
142
|
+
w.push_array()
|
143
|
+
w.pop()
|
144
|
+
w.reset()
|
145
|
+
assert_equal('', w.to_s)
|
146
|
+
end
|
147
|
+
|
148
|
+
end # OjWriter
|
data/test/tests.rb
CHANGED
@@ -134,6 +134,7 @@ class Juice < ::Test::Unit::TestCase
|
|
134
134
|
:time_format=>:unix,
|
135
135
|
:bigdecimal_as_decimal=>true,
|
136
136
|
:bigdecimal_load=>:auto,
|
137
|
+
:allow_gc=>true,
|
137
138
|
:create_id=>'json_class'}, opts)
|
138
139
|
end
|
139
140
|
|
@@ -150,6 +151,7 @@ class Juice < ::Test::Unit::TestCase
|
|
150
151
|
:time_format=>:unix,
|
151
152
|
:bigdecimal_as_decimal=>true,
|
152
153
|
:bigdecimal_load=>:auto,
|
154
|
+
:allow_gc=>true,
|
153
155
|
:create_id=>'json_class'}
|
154
156
|
o2 = {
|
155
157
|
:indent=>4,
|
@@ -163,6 +165,7 @@ class Juice < ::Test::Unit::TestCase
|
|
163
165
|
:time_format=>:ruby,
|
164
166
|
:bigdecimal_as_decimal=>false,
|
165
167
|
:bigdecimal_load=>:bigdecimal,
|
168
|
+
:allow_gc=>false,
|
166
169
|
:create_id=>nil}
|
167
170
|
o3 = { :indent => 4 }
|
168
171
|
Oj.default_options = o2
|
@@ -765,8 +768,7 @@ class Juice < ::Test::Unit::TestCase
|
|
765
768
|
Oj.default_options = { :mode => :object }
|
766
769
|
json = Oj.dump(1..7, :mode => :object, :indent => 0)
|
767
770
|
if 'rubinius' == $ruby
|
768
|
-
assert(%{{"^
|
769
|
-
%{{"^o":"Range","begin":1,"end":7,"excl":false}} == json)
|
771
|
+
assert(%{{"^O":"Range","begin":1,"end":7,"exclude_end?":false}} == json)
|
770
772
|
elsif 'jruby' == $ruby
|
771
773
|
assert(%{{"^O":"Range","begin":1,"end":7,"exclude_end?":false}} == json)
|
772
774
|
else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'The fastest JSON parser and object serializer. '
|
14
14
|
email: peter@ohler.com
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- test/test_saj.rb
|
94
94
|
- test/test_scp.rb
|
95
95
|
- test/test_strict.rb
|
96
|
+
- test/test_writer.rb
|
96
97
|
- test/tests.rb
|
97
98
|
- test/x.rb
|
98
99
|
- LICENSE
|