oj 2.0.0 → 2.0.1
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.
- data/README.md +5 -3
- data/ext/oj/dump.c +52 -17
- data/ext/oj/load.c +12 -0
- data/ext/oj/oj.c +33 -0
- data/ext/oj/oj.h +1 -0
- data/lib/oj/version.rb +1 -1
- data/test/test_fast.rb +0 -1
- data/test/tests.rb +48 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -32,11 +32,13 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
32
32
|
|
33
33
|
## <a name="release">Release Notes</a>
|
34
34
|
|
35
|
-
### Release 2.0.
|
35
|
+
### Release 2.0.1
|
36
36
|
|
37
|
-
-
|
37
|
+
- BigDecimals now dump to a string in compat mode thanks to cgriego.
|
38
38
|
|
39
|
-
-
|
39
|
+
- High precision time (nano time) can be turned off for better compatibility with other JSON parsers.
|
40
|
+
|
41
|
+
- Times before 1970 now encode correctly.
|
40
42
|
|
41
43
|
## <a name="description">Description</a>
|
42
44
|
|
data/ext/oj/dump.c
CHANGED
@@ -928,6 +928,7 @@ dump_time(VALUE obj, Out out) {
|
|
928
928
|
char *b = buf + sizeof(buf) - 1;
|
929
929
|
long size;
|
930
930
|
char *dot = b - 10;
|
931
|
+
int neg = 0;
|
931
932
|
#if HAS_RB_TIME_TIMESPEC
|
932
933
|
struct timespec ts = rb_time_timespec(obj);
|
933
934
|
time_t sec = ts.tv_sec;
|
@@ -941,13 +942,40 @@ dump_time(VALUE obj, Out out) {
|
|
941
942
|
#endif
|
942
943
|
#endif
|
943
944
|
|
945
|
+
if (0 > sec) {
|
946
|
+
neg = 1;
|
947
|
+
sec = -sec;
|
948
|
+
if (0 < nsec) {
|
949
|
+
nsec = 1000000000 - nsec;
|
950
|
+
#ifndef JRUBY_RUBY
|
951
|
+
sec--;
|
952
|
+
#endif
|
953
|
+
}
|
954
|
+
}
|
944
955
|
*b-- = '\0';
|
945
|
-
|
946
|
-
|
956
|
+
if (0 < out->opts->sec_prec) {
|
957
|
+
if (9 > out->opts->sec_prec) {
|
958
|
+
int i;
|
959
|
+
|
960
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
961
|
+
dot++;
|
962
|
+
nsec = (nsec + 5) / 10;
|
963
|
+
}
|
964
|
+
}
|
965
|
+
for (; dot < b; b--, nsec /= 10) {
|
966
|
+
*b = '0' + (nsec % 10);
|
967
|
+
}
|
968
|
+
*b-- = '.';
|
969
|
+
}
|
970
|
+
if (0 == sec) {
|
971
|
+
*b-- = '0';
|
972
|
+
} else {
|
973
|
+
for (; 0 < sec; b--, sec /= 10) {
|
974
|
+
*b = '0' + (sec % 10);
|
975
|
+
}
|
947
976
|
}
|
948
|
-
|
949
|
-
|
950
|
-
*b = '0' + (sec % 10);
|
977
|
+
if (neg) {
|
978
|
+
*b-- = '-';
|
951
979
|
}
|
952
980
|
b++;
|
953
981
|
size = sizeof(buf) - (b - buf) - 1;
|
@@ -1012,7 +1040,7 @@ dump_xml_time(VALUE obj, Out out) {
|
|
1012
1040
|
tzmin = (int)(tm->tm_gmtoff / 60) - (tzhour * 60);
|
1013
1041
|
}
|
1014
1042
|
#endif
|
1015
|
-
if (0 == nsec) {
|
1043
|
+
if (0 == nsec || 0 == out->opts->sec_prec) {
|
1016
1044
|
if (0 == tzhour && 0 == tzmin) {
|
1017
1045
|
sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ",
|
1018
1046
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
@@ -1026,11 +1054,23 @@ dump_xml_time(VALUE obj, Out out) {
|
|
1026
1054
|
dump_cstr(buf, 25, 0, 0, out);
|
1027
1055
|
}
|
1028
1056
|
} else {
|
1029
|
-
|
1057
|
+
char format[64] = "%04d-%02d-%02dT%02d:%02d:%02d.%09ld%c%02d:%02d";
|
1058
|
+
int len = 35;
|
1059
|
+
|
1060
|
+
if (9 > out->opts->sec_prec) {
|
1061
|
+
int i;
|
1062
|
+
|
1063
|
+
format[32] = '0' + out->opts->sec_prec;
|
1064
|
+
for (i = 9 - out->opts->sec_prec; 0 < i; i--) {
|
1065
|
+
nsec = (nsec + 5) / 10;
|
1066
|
+
len--;
|
1067
|
+
}
|
1068
|
+
}
|
1069
|
+
sprintf(buf, format,
|
1030
1070
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
1031
1071
|
tm->tm_hour, tm->tm_min, tm->tm_sec, nsec,
|
1032
1072
|
tzsign, tzhour, tzmin);
|
1033
|
-
dump_cstr(buf,
|
1073
|
+
dump_cstr(buf, len, 0, 0, out);
|
1034
1074
|
}
|
1035
1075
|
}
|
1036
1076
|
|
@@ -1074,14 +1114,8 @@ dump_data_comp(VALUE obj, Out out) {
|
|
1074
1114
|
} else {
|
1075
1115
|
VALUE rstr;
|
1076
1116
|
|
1077
|
-
|
1078
|
-
|
1079
|
-
rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1080
|
-
dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
|
1081
|
-
} else {
|
1082
|
-
rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1083
|
-
dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1084
|
-
}
|
1117
|
+
rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1118
|
+
dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1085
1119
|
}
|
1086
1120
|
}
|
1087
1121
|
|
@@ -1146,7 +1180,8 @@ dump_obj_comp(VALUE obj, int depth, Out out) {
|
|
1146
1180
|
if (oj_bigdecimal_class == clas) {
|
1147
1181
|
VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
|
1148
1182
|
|
1149
|
-
dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
|
1183
|
+
//dump_raw(StringValuePtr(rstr), RSTRING_LEN(rstr), out);
|
1184
|
+
dump_cstr(StringValuePtr(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
1150
1185
|
} else {
|
1151
1186
|
Odd odd = oj_get_odd(clas);
|
1152
1187
|
|
data/ext/oj/load.c
CHANGED
@@ -825,7 +825,12 @@ static VALUE
|
|
825
825
|
read_time(ParseInfo pi) {
|
826
826
|
time_t v = 0;
|
827
827
|
long v2 = 0;
|
828
|
+
int neg = 0;
|
828
829
|
|
830
|
+
if ('-' == *pi->s) {
|
831
|
+
pi->s++;
|
832
|
+
neg = 1;
|
833
|
+
}
|
829
834
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
830
835
|
v = v * 10 + (*pi->s - '0');
|
831
836
|
}
|
@@ -840,6 +845,13 @@ read_time(ParseInfo pi) {
|
|
840
845
|
v2 *= 10;
|
841
846
|
}
|
842
847
|
}
|
848
|
+
if (neg) {
|
849
|
+
v = -v;
|
850
|
+
if (0 < v2) {
|
851
|
+
v--;
|
852
|
+
v2 = 1000000000 - v2;
|
853
|
+
}
|
854
|
+
}
|
843
855
|
#if HAS_NANO_TIME
|
844
856
|
return rb_time_nano_new(v, v2);
|
845
857
|
#else
|
data/ext/oj/oj.c
CHANGED
@@ -91,6 +91,7 @@ static VALUE mode_sym;
|
|
91
91
|
static VALUE null_sym;
|
92
92
|
static VALUE object_sym;
|
93
93
|
static VALUE ruby_sym;
|
94
|
+
static VALUE sec_prec_sym;
|
94
95
|
static VALUE strict_sym;
|
95
96
|
static VALUE symbol_keys_sym;
|
96
97
|
static VALUE time_format_sym;
|
@@ -128,6 +129,7 @@ struct _Options oj_default_options = {
|
|
128
129
|
UnixTime, // time_format
|
129
130
|
json_class, // create_id
|
130
131
|
65536, // max_stack
|
132
|
+
9, // sec_prec
|
131
133
|
0, // dump_opts
|
132
134
|
};
|
133
135
|
|
@@ -157,6 +159,7 @@ oj_get_odd(VALUE clas) {
|
|
157
159
|
* - time_format: [:unix|:xmlschema|:ruby] time format when dumping in :compat mode
|
158
160
|
* - create_id: [String|nil] create id for json compatible object encoding, default is 'json_create'
|
159
161
|
* - max_stack: [Fixnum|nil] maximum json size to allocate on the stack, default is 65536
|
162
|
+
* - second_precision: [Fixnum|nil] number of digits after the decimal when dumping the seconds portion of time
|
160
163
|
* @return [Hash] all current option settings.
|
161
164
|
*/
|
162
165
|
static VALUE
|
@@ -164,6 +167,7 @@ get_def_opts(VALUE self) {
|
|
164
167
|
VALUE opts = rb_hash_new();
|
165
168
|
|
166
169
|
rb_hash_aset(opts, indent_sym, INT2FIX(oj_default_options.indent));
|
170
|
+
rb_hash_aset(opts, sec_prec_sym, INT2FIX(oj_default_options.sec_prec));
|
167
171
|
rb_hash_aset(opts, max_stack_sym, INT2FIX(oj_default_options.max_stack));
|
168
172
|
rb_hash_aset(opts, circular_sym, (Yes == oj_default_options.circular) ? Qtrue : ((No == oj_default_options.circular) ? Qfalse : Qnil));
|
169
173
|
rb_hash_aset(opts, auto_define_sym, (Yes == oj_default_options.auto_define) ? Qtrue : ((No == oj_default_options.auto_define) ? Qfalse : Qnil));
|
@@ -210,6 +214,7 @@ get_def_opts(VALUE self) {
|
|
210
214
|
* :ruby Time.to_s formatted String
|
211
215
|
* @param [String|nil] :create_id create id for json compatible object encoding
|
212
216
|
* @param [Fixnum|nil] :max_stack maximum size to allocate on the stack for a JSON String
|
217
|
+
* @param [Fixnum|nil] :second_precision number of digits after the decimal when dumping the seconds portion of time
|
213
218
|
* @return [nil]
|
214
219
|
*/
|
215
220
|
static VALUE
|
@@ -230,6 +235,19 @@ set_def_opts(VALUE self, VALUE opts) {
|
|
230
235
|
Check_Type(v, T_FIXNUM);
|
231
236
|
oj_default_options.indent = FIX2INT(v);
|
232
237
|
}
|
238
|
+
v = rb_hash_aref(opts, sec_prec_sym);
|
239
|
+
if (Qnil != v) {
|
240
|
+
int n;
|
241
|
+
|
242
|
+
Check_Type(v, T_FIXNUM);
|
243
|
+
n = FIX2INT(v);
|
244
|
+
if (0 > n) {
|
245
|
+
n = 0;
|
246
|
+
} else if (9 < n) {
|
247
|
+
n = 9;
|
248
|
+
}
|
249
|
+
oj_default_options.sec_prec = n;
|
250
|
+
}
|
233
251
|
v = rb_hash_aref(opts, max_stack_sym);
|
234
252
|
if (Qnil != v) {
|
235
253
|
int i;
|
@@ -323,6 +341,20 @@ parse_options(VALUE ropts, Options copts) {
|
|
323
341
|
}
|
324
342
|
copts->indent = NUM2INT(v);
|
325
343
|
}
|
344
|
+
if (Qnil != (v = rb_hash_lookup(ropts, sec_prec_sym))) {
|
345
|
+
int n;
|
346
|
+
|
347
|
+
if (rb_cFixnum != rb_obj_class(v)) {
|
348
|
+
rb_raise(rb_eArgError, ":second_precision must be a Fixnum.");
|
349
|
+
}
|
350
|
+
n = NUM2INT(v);
|
351
|
+
if (0 > n) {
|
352
|
+
n = 0;
|
353
|
+
} else if (9 < n) {
|
354
|
+
n = 9;
|
355
|
+
}
|
356
|
+
copts->sec_prec = n;
|
357
|
+
}
|
326
358
|
if (Qnil != (v = rb_hash_lookup(ropts, mode_sym))) {
|
327
359
|
if (object_sym == v) {
|
328
360
|
copts->mode = ObjectMode;
|
@@ -1009,6 +1041,7 @@ void Init_oj() {
|
|
1009
1041
|
null_sym = ID2SYM(rb_intern("null")); rb_gc_register_address(&null_sym);
|
1010
1042
|
object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
|
1011
1043
|
ruby_sym = ID2SYM(rb_intern("ruby")); rb_gc_register_address(&ruby_sym);
|
1044
|
+
sec_prec_sym = ID2SYM(rb_intern("second_precision"));rb_gc_register_address(&sec_prec_sym);
|
1012
1045
|
strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
|
1013
1046
|
symbol_keys_sym = ID2SYM(rb_intern("symbol_keys")); rb_gc_register_address(&symbol_keys_sym);
|
1014
1047
|
time_format_sym = ID2SYM(rb_intern("time_format")); rb_gc_register_address(&time_format_sym);
|
data/ext/oj/oj.h
CHANGED
@@ -110,6 +110,7 @@ typedef struct _Options {
|
|
110
110
|
char time_format; // TimeFormat
|
111
111
|
const char *create_id; // 0 or string
|
112
112
|
size_t max_stack; // max size to allocate on the stack
|
113
|
+
int sec_prec; // second precision when dumping time
|
113
114
|
DumpOpts dump_opts;
|
114
115
|
} *Options;
|
115
116
|
|
data/lib/oj/version.rb
CHANGED
data/test/test_fast.rb
CHANGED
data/test/tests.rb
CHANGED
@@ -110,6 +110,7 @@ class Juice < ::Test::Unit::TestCase
|
|
110
110
|
def test0_get_options
|
111
111
|
opts = Oj.default_options()
|
112
112
|
assert_equal({ :indent=>0,
|
113
|
+
:second_precision=>9,
|
113
114
|
:circular=>false,
|
114
115
|
:auto_define=>true,
|
115
116
|
:symbol_keys=>false,
|
@@ -123,6 +124,7 @@ class Juice < ::Test::Unit::TestCase
|
|
123
124
|
def test0_set_options
|
124
125
|
orig = {
|
125
126
|
:indent=>0,
|
127
|
+
:second_precision=>9,
|
126
128
|
:circular=>false,
|
127
129
|
:auto_define=>true,
|
128
130
|
:symbol_keys=>false,
|
@@ -133,6 +135,7 @@ class Juice < ::Test::Unit::TestCase
|
|
133
135
|
:create_id=>'json_class'}
|
134
136
|
o2 = {
|
135
137
|
:indent=>4,
|
138
|
+
:second_precision=>7,
|
136
139
|
:circular=>true,
|
137
140
|
:auto_define=>false,
|
138
141
|
:symbol_keys=>true,
|
@@ -265,6 +268,22 @@ class Juice < ::Test::Unit::TestCase
|
|
265
268
|
json = Oj.dump(t, :mode => :compat)
|
266
269
|
assert_equal(%{1325775487.123456000}, json)
|
267
270
|
end
|
271
|
+
def test_unix_time_compat_precision
|
272
|
+
t = Time.xmlschema("2012-01-05T23:58:07.123456789+09:00")
|
273
|
+
#t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
|
274
|
+
json = Oj.dump(t, :mode => :compat, :second_precision => 5)
|
275
|
+
assert_equal(%{1325775487.12346}, json)
|
276
|
+
end
|
277
|
+
def test_unix_time_compat_early
|
278
|
+
t = Time.xmlschema("1954-01-05T00:00:00.123456789+00:00")
|
279
|
+
json = Oj.dump(t, :mode => :compat, :second_precision => 5)
|
280
|
+
assert_equal(%{-504575999.87654}, json)
|
281
|
+
end
|
282
|
+
def test_unix_time_compat_1970
|
283
|
+
t = Time.xmlschema("1970-01-01T00:00:00.123456789+00:00")
|
284
|
+
json = Oj.dump(t, :mode => :compat, :second_precision => 5)
|
285
|
+
assert_equal(%{0.12346}, json)
|
286
|
+
end
|
268
287
|
def test_ruby_time_compat
|
269
288
|
t = Time.xmlschema("2012-01-05T23:58:07.123456000+09:00")
|
270
289
|
json = Oj.dump(t, :mode => :compat, :time_format => :ruby)
|
@@ -309,6 +328,25 @@ class Juice < ::Test::Unit::TestCase
|
|
309
328
|
assert_equal(%{"2012-01-05T23:58:07%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
|
310
329
|
end
|
311
330
|
end
|
331
|
+
def test_xml_time_compat_precision
|
332
|
+
begin
|
333
|
+
t = Time.new(2012, 1, 5, 23, 58, 7.123456789, 32400)
|
334
|
+
json = Oj.dump(t, :mode => :compat, :time_format => :xmlschema, :second_precision => 5)
|
335
|
+
assert_equal(%{"2012-01-05T23:58:07.12346+09:00"}, json)
|
336
|
+
rescue Exception
|
337
|
+
# some Rubies (1.8.7) do not allow the timezome to be set
|
338
|
+
t = Time.local(2012, 1, 5, 23, 58, 7, 123456)
|
339
|
+
json = Oj.dump(t, :mode => :compat, :time_format => :xmlschema, :second_precision => 5)
|
340
|
+
tz = t.utc_offset
|
341
|
+
# Ruby does not handle a %+02d properly so...
|
342
|
+
sign = '+'
|
343
|
+
if 0 > tz
|
344
|
+
sign = '-'
|
345
|
+
tz = -tz
|
346
|
+
end
|
347
|
+
assert_equal(%{"2012-01-05T23:58:07.12346%s%02d:%02d"} % [sign, tz / 3600, tz / 60 % 60], json)
|
348
|
+
end
|
349
|
+
end
|
312
350
|
def test_xml_time_compat_zulu
|
313
351
|
begin
|
314
352
|
t = Time.new(2012, 1, 5, 23, 58, 7.0, 0)
|
@@ -327,6 +365,11 @@ class Juice < ::Test::Unit::TestCase
|
|
327
365
|
Oj.default_options = { :mode => :object }
|
328
366
|
dump_and_load(t, false)
|
329
367
|
end
|
368
|
+
def test_time_object_early
|
369
|
+
t = Time.xmlschema("1954-01-05T00:00:00.123456")
|
370
|
+
Oj.default_options = { :mode => :object }
|
371
|
+
dump_and_load(t, false)
|
372
|
+
end
|
330
373
|
|
331
374
|
# Class
|
332
375
|
def test_class_strict
|
@@ -666,10 +709,14 @@ class Juice < ::Test::Unit::TestCase
|
|
666
709
|
Oj.default_options = {:mode => mode}
|
667
710
|
end
|
668
711
|
def test_bigdecimal_compat
|
712
|
+
orig = BigDecimal.new('80.51')
|
713
|
+
json = Oj.dump(orig, :mode => :compat)
|
714
|
+
bg = Oj.load(json, :mode => :compat)
|
715
|
+
assert_equal(orig.to_s, bg)
|
669
716
|
orig = BigDecimal.new('3.14159265358979323846')
|
670
717
|
json = Oj.dump(orig, :mode => :compat)
|
671
718
|
bg = Oj.load(json, :mode => :compat)
|
672
|
-
assert_equal(orig, bg)
|
719
|
+
assert_equal(orig.to_s, bg)
|
673
720
|
end
|
674
721
|
def test_bigdecimal_object
|
675
722
|
mode = Oj.default_options[:mode]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-15 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'The fastest JSON parser and object serializer. '
|
15
15
|
email: peter@ohler.com
|