oj 3.15.0 → 3.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/ext/oj/code.c +3 -10
- data/ext/oj/compat.c +5 -18
- data/ext/oj/custom.c +5 -13
- data/ext/oj/dump.c +25 -20
- data/ext/oj/dump.h +1 -4
- data/ext/oj/dump_compat.c +16 -16
- data/ext/oj/extconf.rb +4 -2
- data/ext/oj/fast.c +3 -6
- data/ext/oj/mimic_json.c +0 -3
- data/ext/oj/oj.c +33 -9
- data/ext/oj/oj.h +23 -30
- data/ext/oj/parse.c +3 -5
- data/ext/oj/parse.h +16 -14
- data/ext/oj/parser.h +2 -2
- data/ext/oj/rails.c +0 -1
- data/ext/oj/reader.c +1 -3
- data/ext/oj/saj.c +1 -1
- data/ext/oj/string_writer.c +0 -1
- data/lib/oj/active_support_helper.rb +2 -3
- data/lib/oj/json.rb +159 -149
- data/lib/oj/mimic.rb +3 -1
- data/lib/oj/version.rb +1 -1
- data/test/foo.rb +3 -4
- data/test/json_gem/json_common_interface_test.rb +4 -2
- data/test/json_gem/json_generator_test.rb +1 -1
- data/test/perf_dump.rb +3 -3
- data/test/prec.rb +4 -4
- data/test/test_compat.rb +19 -1
- data/test/test_parser_debug.rb +1 -1
- data/test/test_various.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: affe42de16d26dfe301f38df7c0bc8ceee39ab25e5a6bd14bd7af6671cc3b422
|
4
|
+
data.tar.gz: ee1d7227f864b197aca62cb767f9202798b6d3d13cf0b5f89eae3f3a36450dbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dbf6a0b5fef2b179dba864b595113f73ac27a952307559422bf1feda19fcc008a5e43a142d1bb3eb0cc4125ff18f35730208c7f82b11089ff90690e7b1f17d0
|
7
|
+
data.tar.gz: be09c04fa551288e2708c9151d0c966a09c3768ba15442cc54b8b4dd5850e2228b45608c06c8251c4e2ca32333353713f9c1feff81e4d34a48d1c6bf725014f6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.16.0 - 2023-08-16
|
4
|
+
|
5
|
+
- Added the `float_format` option.
|
6
|
+
|
7
|
+
- Expanded the `max_nesting` option to allow integer values as well as
|
8
|
+
the previous boolean (true or nil).
|
9
|
+
|
10
|
+
- Skip nesting tests with Truffle Ruby in the json gem tests.
|
11
|
+
|
12
|
+
## 3.15.1 - 2023-07-30
|
13
|
+
|
14
|
+
- Add protection against some using `require 'oj/json`, an internal file.
|
15
|
+
|
16
|
+
- Fixed non-json errors when in compat mode.
|
17
|
+
|
3
18
|
## 3.15.0 - 2023-06-02
|
4
19
|
|
5
20
|
- Added `omit_null_byte` option when dumping.
|
data/ext/oj/code.c
CHANGED
@@ -185,24 +185,17 @@ void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out, bool with_class) {
|
|
185
185
|
} else {
|
186
186
|
char buf[32];
|
187
187
|
char *b = buf + sizeof(buf) - 1;
|
188
|
-
|
188
|
+
bool neg = false;
|
189
189
|
long num = attrs->num;
|
190
190
|
size_t cnt = 0;
|
191
191
|
|
192
192
|
if (0 > num) {
|
193
|
-
neg =
|
193
|
+
neg = true;
|
194
194
|
num = -num;
|
195
195
|
}
|
196
196
|
*b-- = '\0';
|
197
197
|
if (0 < num) {
|
198
|
-
|
199
|
-
*b = (num % 10) + '0';
|
200
|
-
}
|
201
|
-
if (neg) {
|
202
|
-
*b = '-';
|
203
|
-
} else {
|
204
|
-
b++;
|
205
|
-
}
|
198
|
+
b = oj_longlong_to_string(num, neg, b);
|
206
199
|
} else {
|
207
200
|
*b = '0';
|
208
201
|
}
|
data/ext/oj/compat.c
CHANGED
@@ -13,32 +13,19 @@
|
|
13
13
|
#include "trace.h"
|
14
14
|
|
15
15
|
static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
16
|
-
const char
|
17
|
-
int
|
18
|
-
Val
|
19
|
-
volatile VALUE rkey = kval->key_val;
|
16
|
+
const char *key = kval->key;
|
17
|
+
int klen = kval->klen;
|
18
|
+
Val parent = stack_peek(&pi->stack);
|
20
19
|
|
21
|
-
if (Qundef ==
|
20
|
+
if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
|
22
21
|
*pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
|
23
22
|
0 == strncmp(pi->options.create_id, key, klen)) {
|
24
23
|
parent->classname = oj_strndup(str, len);
|
25
24
|
parent->clen = len;
|
26
25
|
} else {
|
27
26
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
27
|
+
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
28
28
|
|
29
|
-
if (Qundef == rkey) {
|
30
|
-
if (Yes != pi->options.cache_keys) {
|
31
|
-
if (Yes == pi->options.sym_key) {
|
32
|
-
rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
|
33
|
-
} else {
|
34
|
-
rkey = rb_utf8_str_new(key, klen);
|
35
|
-
}
|
36
|
-
} else if (Yes == pi->options.sym_key) {
|
37
|
-
rkey = oj_sym_intern(key, klen);
|
38
|
-
} else {
|
39
|
-
rkey = oj_str_intern(key, klen);
|
40
|
-
}
|
41
|
-
}
|
42
29
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
43
30
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
44
31
|
|
data/ext/oj/custom.c
CHANGED
@@ -889,12 +889,11 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
889
889
|
///// load functions /////
|
890
890
|
|
891
891
|
static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
892
|
-
const char
|
893
|
-
int
|
894
|
-
Val
|
895
|
-
volatile VALUE rkey = kval->key_val;
|
892
|
+
const char *key = kval->key;
|
893
|
+
int klen = kval->klen;
|
894
|
+
Val parent = stack_peek(&pi->stack);
|
896
895
|
|
897
|
-
if (Qundef ==
|
896
|
+
if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
|
898
897
|
*pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
|
899
898
|
0 == strncmp(pi->options.create_id, key, klen)) {
|
900
899
|
parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
|
@@ -907,15 +906,8 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
907
906
|
}
|
908
907
|
} else {
|
909
908
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
910
|
-
|
909
|
+
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
911
910
|
|
912
|
-
if (Qundef == rkey) {
|
913
|
-
if (Yes == pi->options.sym_key) {
|
914
|
-
rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
|
915
|
-
} else {
|
916
|
-
rkey = rb_utf8_str_new(key, klen);
|
917
|
-
}
|
918
|
-
}
|
919
911
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
920
912
|
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
|
921
913
|
|
data/ext/oj/dump.c
CHANGED
@@ -1010,11 +1010,33 @@ static const char digits_table[] = "\
|
|
1010
1010
|
80818283848586878889\
|
1011
1011
|
90919293949596979899";
|
1012
1012
|
|
1013
|
+
char *oj_longlong_to_string(long long num, bool negative, char *buf) {
|
1014
|
+
while (100 <= num) {
|
1015
|
+
unsigned idx = num % 100 * 2;
|
1016
|
+
*buf-- = digits_table[idx + 1];
|
1017
|
+
*buf-- = digits_table[idx];
|
1018
|
+
num /= 100;
|
1019
|
+
}
|
1020
|
+
if (num < 10) {
|
1021
|
+
*buf-- = num + '0';
|
1022
|
+
} else {
|
1023
|
+
*buf-- = digits_table[num * 2 + 1];
|
1024
|
+
*buf-- = digits_table[num * 2];
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
if (negative) {
|
1028
|
+
*buf = '-';
|
1029
|
+
} else {
|
1030
|
+
buf++;
|
1031
|
+
}
|
1032
|
+
return buf;
|
1033
|
+
}
|
1034
|
+
|
1013
1035
|
void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
1014
1036
|
char buf[32];
|
1015
1037
|
char *b = buf + sizeof(buf) - 1;
|
1016
1038
|
long long num = NUM2LL(obj);
|
1017
|
-
|
1039
|
+
bool neg = false;
|
1018
1040
|
size_t cnt = 0;
|
1019
1041
|
bool dump_as_string = false;
|
1020
1042
|
|
@@ -1023,7 +1045,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1023
1045
|
dump_as_string = true;
|
1024
1046
|
}
|
1025
1047
|
if (0 > num) {
|
1026
|
-
neg =
|
1048
|
+
neg = true;
|
1027
1049
|
num = -num;
|
1028
1050
|
}
|
1029
1051
|
*b-- = '\0';
|
@@ -1032,24 +1054,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1032
1054
|
*b-- = '"';
|
1033
1055
|
}
|
1034
1056
|
if (0 < num) {
|
1035
|
-
|
1036
|
-
unsigned idx = num % 100 * 2;
|
1037
|
-
*b-- = digits_table[idx + 1];
|
1038
|
-
*b-- = digits_table[idx];
|
1039
|
-
num /= 100;
|
1040
|
-
}
|
1041
|
-
if (num < 10) {
|
1042
|
-
*b-- = num + '0';
|
1043
|
-
} else {
|
1044
|
-
*b-- = digits_table[num * 2 + 1];
|
1045
|
-
*b-- = digits_table[num * 2];
|
1046
|
-
}
|
1047
|
-
|
1048
|
-
if (neg) {
|
1049
|
-
*b = '-';
|
1050
|
-
} else {
|
1051
|
-
b++;
|
1052
|
-
}
|
1057
|
+
b = oj_longlong_to_string(num, neg, b);
|
1053
1058
|
} else {
|
1054
1059
|
*b = '0';
|
1055
1060
|
}
|
data/ext/oj/dump.h
CHANGED
@@ -93,10 +93,7 @@ inline static void dump_ulong(unsigned long num, Out out) {
|
|
93
93
|
|
94
94
|
*b-- = '\0';
|
95
95
|
if (0 < num) {
|
96
|
-
|
97
|
-
*b = (num % 10) + '0';
|
98
|
-
}
|
99
|
-
b++;
|
96
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
100
97
|
} else {
|
101
98
|
*b = '0';
|
102
99
|
}
|
data/ext/oj/dump_compat.c
CHANGED
@@ -851,13 +851,18 @@ static DumpFunc compat_funcs[] = {
|
|
851
851
|
};
|
852
852
|
|
853
853
|
static void set_state_depth(VALUE state, int depth) {
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
854
|
+
if (0 == rb_const_defined(rb_cObject, rb_intern("JSON"))) {
|
855
|
+
rb_require("oj/json");
|
856
|
+
}
|
857
|
+
{
|
858
|
+
VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
|
859
|
+
VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
|
860
|
+
VALUE generator = rb_const_get(ext, rb_intern("Generator"));
|
861
|
+
VALUE state_class = rb_const_get(generator, rb_intern("State"));
|
858
862
|
|
859
|
-
|
860
|
-
|
863
|
+
if (state_class == rb_obj_class(state)) {
|
864
|
+
rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
|
865
|
+
}
|
861
866
|
}
|
862
867
|
}
|
863
868
|
|
@@ -865,20 +870,15 @@ void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
|
|
865
870
|
int type = rb_type(obj);
|
866
871
|
|
867
872
|
TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
|
873
|
+
// The max_nesting logic is that an empty Array or Hash is assumed to have
|
874
|
+
// content so the max_nesting should fail but a non-collection value is
|
875
|
+
// okay. That means a check for a collectable value is needed before
|
876
|
+
// raising.
|
868
877
|
if (out->opts->dump_opts.max_depth <= depth) {
|
869
|
-
|
870
|
-
// limit is the depth inclusive. If JSON.generate is called then a
|
871
|
-
// NestingError is expected and the limit is inclusive. Worse than
|
872
|
-
// that there are unit tests for both.
|
873
|
-
if (CALLER_DUMP == out->caller) {
|
878
|
+
if (RUBY_T_ARRAY == type || RUBY_T_HASH == type) {
|
874
879
|
if (0 < out->argc) {
|
875
880
|
set_state_depth(*out->argv, depth);
|
876
881
|
}
|
877
|
-
rb_raise(rb_eArgError, "Too deeply nested.");
|
878
|
-
} else if (out->opts->dump_opts.max_depth < depth) {
|
879
|
-
if (0 < out->argc) {
|
880
|
-
set_state_depth(*out->argv, depth - 1);
|
881
|
-
}
|
882
882
|
raise_json_err("Too deeply nested", "NestingError");
|
883
883
|
}
|
884
884
|
}
|
data/ext/oj/extconf.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mkmf'
|
2
4
|
require 'rbconfig'
|
3
5
|
|
@@ -6,7 +8,7 @@ dir_config(extension_name)
|
|
6
8
|
|
7
9
|
parts = RUBY_DESCRIPTION.split(' ')
|
8
10
|
type = parts[0]
|
9
|
-
type = type[4
|
11
|
+
type = type[4..] if type.start_with?('tcs-')
|
10
12
|
is_windows = RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
|
11
13
|
platform = RUBY_PLATFORM
|
12
14
|
version = RUBY_VERSION.split('.')
|
@@ -56,7 +58,7 @@ dflags.each do |k, v|
|
|
56
58
|
end
|
57
59
|
|
58
60
|
$CPPFLAGS += ' -Wall'
|
59
|
-
#puts "*** $CPPFLAGS: #{$CPPFLAGS}"
|
61
|
+
# puts "*** $CPPFLAGS: #{$CPPFLAGS}"
|
60
62
|
# Adding the __attribute__ flag only works with gcc compilers and even then it
|
61
63
|
# does not work to check args with varargs so just remove the check.
|
62
64
|
CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
|
data/ext/oj/fast.c
CHANGED
@@ -40,7 +40,7 @@ typedef struct _doc {
|
|
40
40
|
Leaf *where; // points to current location
|
41
41
|
Leaf where_path[MAX_STACK]; // points to head of path
|
42
42
|
char *json;
|
43
|
-
unsigned long size;
|
43
|
+
unsigned long size; // number of leaves/branches in the doc
|
44
44
|
VALUE self;
|
45
45
|
Batch batches;
|
46
46
|
struct _batch batch0;
|
@@ -114,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
|
|
114
114
|
char *b = buf + sizeof(buf) - 1;
|
115
115
|
|
116
116
|
*b-- = '\0';
|
117
|
-
|
118
|
-
*b = (num % 10) + '0';
|
119
|
-
}
|
120
|
-
b++;
|
117
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
121
118
|
if ('\0' == *b) {
|
122
119
|
b--;
|
123
120
|
*b = '0';
|
@@ -576,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
|
|
576
573
|
char *h = pi->s; // head
|
577
574
|
char *t = h; // tail
|
578
575
|
|
579
|
-
h++;
|
576
|
+
h++; // skip quote character
|
580
577
|
t++;
|
581
578
|
value = h;
|
582
579
|
for (; '"' != *h; h++, t++) {
|
data/ext/oj/mimic_json.c
CHANGED
@@ -209,7 +209,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
|
|
209
209
|
|
210
210
|
oj_out_init(&out);
|
211
211
|
|
212
|
-
out.caller = CALLER_DUMP;
|
213
212
|
copts.escape_mode = JXEsc;
|
214
213
|
copts.mode = CompatMode;
|
215
214
|
|
@@ -368,7 +367,6 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
|
|
368
367
|
oj_out_init(&out);
|
369
368
|
|
370
369
|
out.omit_nil = copts->dump_opts.omit_nil;
|
371
|
-
out.caller = CALLER_GENERATE;
|
372
370
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
373
371
|
// it is.
|
374
372
|
copts->dump_opts.nan_dump = RaiseNan;
|
@@ -574,7 +572,6 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
|
|
574
572
|
if (T_HASH != rb_type(ropts)) {
|
575
573
|
rb_raise(rb_eArgError, "options must be a hash.");
|
576
574
|
}
|
577
|
-
|
578
575
|
rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
|
579
576
|
v = rb_hash_lookup(ropts, oj_max_nesting_sym);
|
580
577
|
if (Qtrue == v) {
|
data/ext/oj/oj.c
CHANGED
@@ -123,6 +123,7 @@ static VALUE escape_mode_sym;
|
|
123
123
|
static VALUE integer_range_sym;
|
124
124
|
static VALUE fast_sym;
|
125
125
|
static VALUE float_prec_sym;
|
126
|
+
static VALUE float_format_sym;
|
126
127
|
static VALUE float_sym;
|
127
128
|
static VALUE huge_sym;
|
128
129
|
static VALUE ignore_sym;
|
@@ -232,7 +233,7 @@ struct _options oj_default_options = {
|
|
232
233
|
NULL, // tail
|
233
234
|
{'\0'}, // err
|
234
235
|
},
|
235
|
-
NULL,
|
236
|
+
NULL,
|
236
237
|
};
|
237
238
|
|
238
239
|
/* Document-method: default_options()
|
@@ -267,6 +268,8 @@ struct _options oj_default_options = {
|
|
267
268
|
*seconds portion of time
|
268
269
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
269
270
|
*indicates use Ruby
|
271
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
272
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
270
273
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
|
271
274
|
* - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
|
272
275
|
* - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
|
@@ -378,6 +381,7 @@ static VALUE get_def_opts(VALUE self) {
|
|
378
381
|
oj_safe_sym,
|
379
382
|
(Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
|
380
383
|
rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
|
384
|
+
rb_hash_aset(opts, float_format_sym, rb_str_new_cstr(oj_default_options.float_fmt));
|
381
385
|
rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
|
382
386
|
rb_hash_aset(
|
383
387
|
opts,
|
@@ -519,6 +523,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
519
523
|
*load.
|
520
524
|
* - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
|
521
525
|
*seconds portion of time.
|
526
|
+
* - *:float_format* [_String_] the C printf format string for printing floats. Default follows
|
527
|
+
* the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
|
522
528
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
|
523
529
|
*indicates use Ruby.
|
524
530
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
|
@@ -617,7 +623,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
617
623
|
if (set_yesno_options(k, v, copts)) {
|
618
624
|
return ST_CONTINUE;
|
619
625
|
}
|
620
|
-
|
621
626
|
if (oj_indent_sym == k) {
|
622
627
|
switch (rb_type(v)) {
|
623
628
|
case T_NIL:
|
@@ -757,7 +762,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
757
762
|
if (Qnil == v) {
|
758
763
|
return ST_CONTINUE;
|
759
764
|
}
|
760
|
-
|
761
765
|
copts->compat_bigdec = (Qtrue == v);
|
762
766
|
} else if (oj_decimal_class_sym == k) {
|
763
767
|
if (rb_cFloat == v) {
|
@@ -949,6 +953,25 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
949
953
|
return ST_CONTINUE;
|
950
954
|
}
|
951
955
|
copts->sym_key = (Qtrue == v) ? Yes : No;
|
956
|
+
|
957
|
+
} else if (oj_max_nesting_sym == k) {
|
958
|
+
if (Qtrue == v) {
|
959
|
+
copts->dump_opts.max_depth = 100;
|
960
|
+
} else if (Qfalse == v || Qnil == v) {
|
961
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
962
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
963
|
+
copts->dump_opts.max_depth = NUM2INT(v);
|
964
|
+
if (0 >= copts->dump_opts.max_depth) {
|
965
|
+
copts->dump_opts.max_depth = MAX_DEPTH;
|
966
|
+
}
|
967
|
+
}
|
968
|
+
} else if (float_format_sym == k) {
|
969
|
+
rb_check_type(v, T_STRING);
|
970
|
+
if (6 < (int)RSTRING_LEN(v)) {
|
971
|
+
rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
|
972
|
+
}
|
973
|
+
strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
974
|
+
copts->float_fmt[RSTRING_LEN(v)] = '\0';
|
952
975
|
}
|
953
976
|
return ST_CONTINUE;
|
954
977
|
}
|
@@ -957,7 +980,6 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
957
980
|
if (T_HASH != rb_type(ropts)) {
|
958
981
|
return;
|
959
982
|
}
|
960
|
-
|
961
983
|
rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
|
962
984
|
oj_parse_opt_match_string(&copts->str_rx, ropts);
|
963
985
|
|
@@ -1294,9 +1316,8 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1294
1316
|
|
1295
1317
|
oj_out_init(arg.out);
|
1296
1318
|
|
1297
|
-
arg.out->omit_nil
|
1319
|
+
arg.out->omit_nil = copts.dump_opts.omit_nil;
|
1298
1320
|
arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
|
1299
|
-
arg.out->caller = CALLER_DUMP;
|
1300
1321
|
|
1301
1322
|
return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
|
1302
1323
|
}
|
@@ -1308,8 +1329,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
|
|
1308
1329
|
* will be called. The mode is set to :compat.
|
1309
1330
|
* - *obj* [_Object_] Object to serialize as an JSON document String
|
1310
1331
|
* - *options* [_Hash_]
|
1311
|
-
* - *:max_nesting* [_boolean_] It true nesting is limited to 100.
|
1312
|
-
*
|
1332
|
+
* - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100. If a Fixnum nesting
|
1333
|
+
* is set to the provided value. The option to detect circular references is available but is not
|
1334
|
+
* compatible with the json gem., default is false or unlimited.
|
1313
1335
|
* - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
|
1314
1336
|
* used as appropriate, default is true.
|
1315
1337
|
* - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
|
@@ -1343,7 +1365,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
1343
1365
|
|
1344
1366
|
oj_out_init(&out);
|
1345
1367
|
|
1346
|
-
out.omit_nil
|
1368
|
+
out.omit_nil = copts.dump_opts.omit_nil;
|
1347
1369
|
out.omit_null_byte = copts.dump_opts.omit_null_byte;
|
1348
1370
|
// For obj.to_json or generate nan is not allowed but if called from dump
|
1349
1371
|
// it is.
|
@@ -1929,6 +1951,8 @@ void Init_oj(void) {
|
|
1929
1951
|
rb_gc_register_address(&integer_range_sym);
|
1930
1952
|
fast_sym = ID2SYM(rb_intern("fast"));
|
1931
1953
|
rb_gc_register_address(&fast_sym);
|
1954
|
+
float_format_sym = ID2SYM(rb_intern("float_format"));
|
1955
|
+
rb_gc_register_address(&float_format_sym);
|
1932
1956
|
float_prec_sym = ID2SYM(rb_intern("float_precision"));
|
1933
1957
|
rb_gc_register_address(&float_prec_sym);
|
1934
1958
|
float_sym = ID2SYM(rb_intern("float"));
|
data/ext/oj/oj.h
CHANGED
@@ -103,13 +103,6 @@ typedef enum {
|
|
103
103
|
FILE_IO = 'f',
|
104
104
|
} StreamWriterType;
|
105
105
|
|
106
|
-
typedef enum {
|
107
|
-
CALLER_DUMP = 'd',
|
108
|
-
CALLER_TO_JSON = 't',
|
109
|
-
CALLER_GENERATE = 'g',
|
110
|
-
// Add the fast versions if necessary. Maybe unparse as well if needed.
|
111
|
-
} DumpCaller;
|
112
|
-
|
113
106
|
typedef struct _dumpOpts {
|
114
107
|
bool use;
|
115
108
|
char indent_str[16];
|
@@ -188,23 +181,22 @@ typedef struct _rOptTable {
|
|
188
181
|
} *ROptTable;
|
189
182
|
|
190
183
|
typedef struct _out {
|
191
|
-
char
|
192
|
-
char
|
193
|
-
char
|
194
|
-
char
|
195
|
-
Cache8
|
196
|
-
slot_t
|
197
|
-
int
|
198
|
-
int
|
199
|
-
Options
|
200
|
-
uint32_t
|
201
|
-
bool
|
202
|
-
bool
|
203
|
-
bool
|
204
|
-
int
|
205
|
-
VALUE
|
206
|
-
|
207
|
-
ROptTable ropts;
|
184
|
+
char stack_buffer[4096];
|
185
|
+
char *buf;
|
186
|
+
char *end;
|
187
|
+
char *cur;
|
188
|
+
Cache8 circ_cache;
|
189
|
+
slot_t circ_cnt;
|
190
|
+
int indent;
|
191
|
+
int depth; // used by dump_hash
|
192
|
+
Options opts;
|
193
|
+
uint32_t hash_cnt;
|
194
|
+
bool allocated;
|
195
|
+
bool omit_nil;
|
196
|
+
bool omit_null_byte;
|
197
|
+
int argc;
|
198
|
+
VALUE *argv;
|
199
|
+
ROptTable ropts;
|
208
200
|
} *Out;
|
209
201
|
|
210
202
|
typedef struct _strWriter {
|
@@ -262,12 +254,13 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
|
|
262
254
|
extern bool oj_hash_has_key(VALUE hash, VALUE key);
|
263
255
|
extern void oj_parse_options(VALUE ropts, Options copts);
|
264
256
|
|
265
|
-
extern void
|
266
|
-
extern void
|
267
|
-
extern void
|
268
|
-
extern void
|
269
|
-
extern void
|
270
|
-
extern void
|
257
|
+
extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
|
258
|
+
extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
|
259
|
+
extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
|
260
|
+
extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
|
261
|
+
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
262
|
+
extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
|
263
|
+
extern char *oj_longlong_to_string(long long num, bool negative, char *buf);
|
271
264
|
|
272
265
|
extern void oj_str_writer_push_key(StrWriter sw, const char *key);
|
273
266
|
extern void oj_str_writer_push_object(StrWriter sw, const char *key);
|
data/ext/oj/parse.c
CHANGED
@@ -426,6 +426,7 @@ static void read_num(ParseInfo pi) {
|
|
426
426
|
struct _numInfo ni;
|
427
427
|
Val parent = stack_peek(&pi->stack);
|
428
428
|
|
429
|
+
ni.pi = pi;
|
429
430
|
ni.str = pi->cur;
|
430
431
|
ni.i = 0;
|
431
432
|
ni.num = 0;
|
@@ -709,10 +710,7 @@ void oj_parse2(ParseInfo pi) {
|
|
709
710
|
case '[': array_start(pi); break;
|
710
711
|
case ']': array_end(pi); break;
|
711
712
|
case ',': comma(pi); break;
|
712
|
-
case '"':
|
713
|
-
read_str(pi);
|
714
|
-
break;
|
715
|
-
// case '+':
|
713
|
+
case '"': read_str(pi); break;
|
716
714
|
case '+':
|
717
715
|
if (CompatMode == pi->options.mode) {
|
718
716
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
@@ -877,7 +875,7 @@ oj_num_as_value(NumInfo ni) {
|
|
877
875
|
double d = strtod(ni->str, &end);
|
878
876
|
|
879
877
|
if ((long)ni->len != (long)(end - ni->str)) {
|
880
|
-
rb_raise(
|
878
|
+
rb_raise(ni->pi->err_class, "Invalid float");
|
881
879
|
}
|
882
880
|
rnum = rb_float_new(d);
|
883
881
|
}
|
data/ext/oj/parse.h
CHANGED
@@ -16,22 +16,24 @@
|
|
16
16
|
#include "val_stack.h"
|
17
17
|
|
18
18
|
struct _rxClass;
|
19
|
+
struct _parseInfo;
|
19
20
|
|
20
21
|
typedef struct _numInfo {
|
21
|
-
int64_t
|
22
|
-
int64_t
|
23
|
-
int64_t
|
24
|
-
int64_t
|
25
|
-
const char
|
26
|
-
size_t
|
27
|
-
long
|
28
|
-
|
29
|
-
int
|
30
|
-
int
|
31
|
-
int
|
32
|
-
int
|
33
|
-
int
|
34
|
-
int
|
22
|
+
int64_t i;
|
23
|
+
int64_t num;
|
24
|
+
int64_t div;
|
25
|
+
int64_t di;
|
26
|
+
const char *str;
|
27
|
+
size_t len;
|
28
|
+
long exp;
|
29
|
+
struct _parseInfo *pi;
|
30
|
+
int big;
|
31
|
+
int infinity;
|
32
|
+
int nan;
|
33
|
+
int neg;
|
34
|
+
int has_exp;
|
35
|
+
int no_big;
|
36
|
+
int bigdec_load;
|
35
37
|
} *NumInfo;
|
36
38
|
|
37
39
|
typedef struct _parseInfo {
|
data/ext/oj/parser.h
CHANGED
@@ -32,9 +32,9 @@ typedef struct _num {
|
|
32
32
|
long double dub;
|
33
33
|
int64_t fixnum; // holds all digits
|
34
34
|
uint32_t len;
|
35
|
-
int16_t div;
|
35
|
+
int16_t div; // 10^div
|
36
36
|
int16_t exp;
|
37
|
-
uint8_t shift;
|
37
|
+
uint8_t shift; // shift of fixnum to get decimal
|
38
38
|
bool neg;
|
39
39
|
bool exp_neg;
|
40
40
|
// for numbers as strings, reuse buf
|
data/ext/oj/rails.c
CHANGED
data/ext/oj/reader.c
CHANGED
@@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) {
|
|
101
101
|
} else {
|
102
102
|
shift = reader->pro - reader->head - 1; // leave one character so we can backup one
|
103
103
|
}
|
104
|
-
if (0 >= shift) {
|
104
|
+
if (0 >= shift) { /* no space left so allocate more */
|
105
105
|
const char *old = reader->head;
|
106
106
|
size_t size = reader->end - reader->head + BUF_PAD;
|
107
107
|
|
@@ -164,7 +164,6 @@ static VALUE partial_io_cb(VALUE rbuf) {
|
|
164
164
|
}
|
165
165
|
str = StringValuePtr(rstr);
|
166
166
|
cnt = RSTRING_LEN(rstr);
|
167
|
-
// printf("*** partial read %lu bytes, str: '%s'\n", cnt, str);
|
168
167
|
strcpy(reader->tail, str);
|
169
168
|
reader->read_end = reader->tail + cnt;
|
170
169
|
|
@@ -185,7 +184,6 @@ static VALUE io_cb(VALUE rbuf) {
|
|
185
184
|
}
|
186
185
|
str = StringValuePtr(rstr);
|
187
186
|
cnt = RSTRING_LEN(rstr);
|
188
|
-
// printf("*** read %lu bytes, str: '%s'\n", cnt, str);
|
189
187
|
strcpy(reader->tail, str);
|
190
188
|
reader->read_end = reader->tail + cnt;
|
191
189
|
|
data/ext/oj/saj.c
CHANGED
@@ -587,7 +587,7 @@ static void saj_parse(VALUE handler, char *json) {
|
|
587
587
|
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
588
588
|
pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 * 3)); /* let 3/4ths of the stack be used only */
|
589
589
|
} else {
|
590
|
-
pi.stack_min = 0;
|
590
|
+
pi.stack_min = 0; /* indicates not to check stack limit */
|
591
591
|
}
|
592
592
|
}
|
593
593
|
#endif
|
data/ext/oj/string_writer.c
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/time'
|
2
4
|
|
3
5
|
module Oj
|
4
|
-
|
5
6
|
# Exists only to handle the ActiveSupport::TimeWithZone.
|
6
7
|
class ActiveSupportHelper
|
7
|
-
|
8
8
|
def self.createTimeWithZone(utc, zone)
|
9
9
|
ActiveSupport::TimeWithZone.new(utc - utc.gmt_offset, ActiveSupport::TimeZone[zone])
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
12
|
end
|
14
13
|
|
15
14
|
Oj.register_odd(ActiveSupport::TimeWithZone, Oj::ActiveSupportHelper, :createTimeWithZone, :utc, 'time_zone.name')
|
data/lib/oj/json.rb
CHANGED
@@ -1,178 +1,188 @@
|
|
1
1
|
require 'ostruct'
|
2
2
|
require 'oj/state'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
4
|
+
if defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
5
|
+
# There are enough people that try to use both the json gen and oj in mimic
|
6
|
+
# mode so don't display the warning.
|
7
|
+
|
8
|
+
# warn "WARNING: oj/json is a compatability shim used by Oj. Requiring the file explicitly is not recommended."
|
9
|
+
end
|
10
|
+
|
11
|
+
unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
12
|
+
module JSON
|
13
|
+
NaN = 0.0/0.0 unless defined?(::JSON::NaN)
|
14
|
+
Infinity = 1.0/0.0 unless defined?(::JSON::Infinity)
|
15
|
+
MinusInfinity = -1.0/0.0 unless defined?(::JSON::MinusInfinity)
|
16
|
+
# Taken from the unit test. Note that items like check_circular? are not
|
17
|
+
# present.
|
18
|
+
PRETTY_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
19
|
+
:allow_nan => false,
|
20
|
+
:array_nl => "\n",
|
21
|
+
:ascii_only => false,
|
22
|
+
:buffer_initial_length => 1024,
|
23
|
+
:depth => 0,
|
24
|
+
:indent => " ",
|
25
|
+
:max_nesting => 100,
|
26
|
+
:object_nl => "\n",
|
27
|
+
:space => " ",
|
28
|
+
:space_before => "",
|
29
|
+
}) unless defined?(::JSON::PRETTY_STATE_PROTOTYPE)
|
30
|
+
SAFE_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
31
|
+
:allow_nan => false,
|
32
|
+
:array_nl => "",
|
33
|
+
:ascii_only => false,
|
34
|
+
:buffer_initial_length => 1024,
|
35
|
+
:depth => 0,
|
36
|
+
:indent => "",
|
37
|
+
:max_nesting => 100,
|
38
|
+
:object_nl => "",
|
39
|
+
:space => "",
|
40
|
+
:space_before => "",
|
41
|
+
}) unless defined?(::JSON::SAFE_STATE_PROTOTYPE)
|
42
|
+
FAST_STATE_PROTOTYPE = Ext::Generator::State.from_state({
|
43
|
+
:allow_nan => false,
|
44
|
+
:array_nl => "",
|
45
|
+
:ascii_only => false,
|
46
|
+
:buffer_initial_length => 1024,
|
47
|
+
:depth => 0,
|
48
|
+
:indent => "",
|
49
|
+
:max_nesting => 0,
|
50
|
+
:object_nl => "",
|
51
|
+
:space => "",
|
52
|
+
:space_before => "",
|
53
|
+
}) unless defined?(::JSON::FAST_STATE_PROTOTYPE)
|
54
|
+
|
55
|
+
def self.dump_default_options
|
56
|
+
Oj::MimicDumpOption.new
|
55
57
|
end
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
def self.dump_default_options=(h)
|
60
|
+
m = Oj::MimicDumpOption.new
|
61
|
+
h.each do |k, v|
|
62
|
+
m[k] = v
|
63
|
+
end
|
64
|
+
end
|
61
65
|
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
def self.parser=(p)
|
67
|
+
@@parser = p
|
68
|
+
end
|
65
69
|
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
def self.parser()
|
71
|
+
@@parser
|
72
|
+
end
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
def self.generator=(g)
|
75
|
+
@@generator = g
|
76
|
+
end
|
73
77
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
raise TypeError.new("already initialized") unless @source.nil?
|
78
|
+
def self.generator()
|
79
|
+
@@generator
|
80
|
+
end
|
78
81
|
|
79
|
-
|
80
|
-
|
82
|
+
module Ext
|
83
|
+
class Parser
|
84
|
+
def initialize(src)
|
85
|
+
raise TypeError.new("already initialized") unless @source.nil?
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
@source = src
|
88
|
+
end
|
84
89
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
def parse()
|
89
|
-
raise TypeError.new("already initialized") if @source.nil?
|
90
|
+
def source()
|
91
|
+
raise TypeError.new("already initialized") if @source.nil?
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end # Parser
|
95
|
-
end # Ext
|
96
|
-
|
97
|
-
State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
|
98
|
-
|
99
|
-
begin
|
100
|
-
send(:remove_const, :Parser)
|
101
|
-
rescue
|
102
|
-
end
|
103
|
-
Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
|
104
|
-
self.parser = ::JSON::Ext::Parser
|
105
|
-
self.generator = ::JSON::Ext::Generator
|
106
|
-
|
107
|
-
# Taken directly from the json gem. Shamelessly copied. It is similar in
|
108
|
-
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
109
|
-
class GenericObject < OpenStruct
|
110
|
-
class << self
|
111
|
-
alias [] new
|
112
|
-
|
113
|
-
def json_creatable?
|
114
|
-
@json_creatable
|
115
|
-
end
|
93
|
+
@source
|
94
|
+
end
|
116
95
|
|
117
|
-
|
96
|
+
def parse()
|
97
|
+
raise TypeError.new("already initialized") if @source.nil?
|
118
98
|
|
119
|
-
|
120
|
-
|
121
|
-
data.delete JSON.create_id
|
122
|
-
self[data]
|
123
|
-
end
|
99
|
+
JSON.parse(@source)
|
100
|
+
end
|
124
101
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
102
|
+
end # Parser
|
103
|
+
end # Ext
|
104
|
+
|
105
|
+
State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
|
106
|
+
|
107
|
+
begin
|
108
|
+
send(:remove_const, :Parser)
|
109
|
+
rescue
|
110
|
+
# ignore and move on
|
111
|
+
end
|
112
|
+
Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
|
113
|
+
self.parser = ::JSON::Ext::Parser
|
114
|
+
self.generator = ::JSON::Ext::Generator
|
115
|
+
|
116
|
+
# Taken directly from the json gem. Shamelessly copied. It is similar in
|
117
|
+
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
118
|
+
class GenericObject < OpenStruct
|
119
|
+
class << self
|
120
|
+
alias [] new
|
121
|
+
|
122
|
+
def json_creatable?
|
123
|
+
@json_creatable
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_writer :json_creatable
|
127
|
+
|
128
|
+
def json_create(data)
|
129
|
+
data = data.dup
|
130
|
+
data.delete JSON.create_id
|
131
|
+
self[data]
|
132
|
+
end
|
133
|
+
|
134
|
+
def from_hash(object)
|
135
|
+
case
|
136
|
+
when object.respond_to?(:to_hash)
|
137
|
+
result = new
|
138
|
+
object.to_hash.each do |key, value|
|
139
|
+
result[key] = from_hash(value)
|
140
|
+
end
|
141
|
+
result
|
142
|
+
when object.respond_to?(:to_ary)
|
143
|
+
object.to_ary.map { |a| from_hash(a) }
|
144
|
+
else
|
145
|
+
object
|
131
146
|
end
|
132
|
-
result
|
133
|
-
when object.respond_to?(:to_ary)
|
134
|
-
object.to_ary.map { |a| from_hash(a) }
|
135
|
-
else
|
136
|
-
object
|
137
147
|
end
|
138
|
-
end
|
139
148
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
149
|
+
def load(source, proc = nil, opts = {})
|
150
|
+
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
|
151
|
+
result.nil? ? new : result
|
152
|
+
end
|
144
153
|
|
145
|
-
|
146
|
-
|
147
|
-
|
154
|
+
def dump(obj, *args)
|
155
|
+
::JSON.dump(obj, *args)
|
156
|
+
end
|
148
157
|
|
149
|
-
|
158
|
+
end # self
|
150
159
|
|
151
|
-
|
160
|
+
self.json_creatable = false
|
152
161
|
|
153
|
-
|
154
|
-
|
155
|
-
|
162
|
+
def to_hash
|
163
|
+
table
|
164
|
+
end
|
156
165
|
|
157
|
-
|
158
|
-
|
159
|
-
|
166
|
+
def [](name)
|
167
|
+
__send__(name)
|
168
|
+
end unless method_defined?(:[])
|
160
169
|
|
161
|
-
|
162
|
-
|
163
|
-
|
170
|
+
def []=(name, value)
|
171
|
+
__send__("#{name}=", value)
|
172
|
+
end unless method_defined?(:[]=)
|
164
173
|
|
165
|
-
|
166
|
-
|
167
|
-
|
174
|
+
def |(other)
|
175
|
+
self.class[other.to_hash.merge(to_hash)]
|
176
|
+
end
|
168
177
|
|
169
|
-
|
170
|
-
|
171
|
-
|
178
|
+
def as_json(*)
|
179
|
+
{ JSON.create_id => self.class.name }.merge to_hash
|
180
|
+
end
|
172
181
|
|
173
|
-
|
174
|
-
|
182
|
+
def to_json(*a)
|
183
|
+
as_json.to_json(*a)
|
184
|
+
end
|
175
185
|
end
|
176
|
-
|
177
|
-
|
178
|
-
end
|
186
|
+
|
187
|
+
end # JSON
|
188
|
+
end
|
data/lib/oj/mimic.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
1
3
|
require 'bigdecimal'
|
2
4
|
begin
|
3
5
|
require 'ostruct'
|
@@ -266,7 +268,7 @@ module Oj
|
|
266
268
|
end
|
267
269
|
end
|
268
270
|
def self.json_create(h)
|
269
|
-
if usec = h.delete('u')
|
271
|
+
if (usec = h.delete('u'))
|
270
272
|
h['n'] = usec * 1000
|
271
273
|
end
|
272
274
|
if instance_methods.include?(:tv_nsec)
|
data/lib/oj/version.rb
CHANGED
data/test/foo.rb
CHANGED
@@ -5,11 +5,10 @@ $LOAD_PATH << '.'
|
|
5
5
|
$LOAD_PATH << File.join(__dir__, '../lib')
|
6
6
|
$LOAD_PATH << File.join(__dir__, '../ext')
|
7
7
|
|
8
|
+
require 'json'
|
8
9
|
require 'oj'
|
9
|
-
|
10
|
-
GC.stress = true
|
10
|
+
require 'oj/json'
|
11
11
|
|
12
12
|
Oj.mimic_JSON
|
13
13
|
|
14
|
-
|
15
|
-
pp JSON('{ "a": 1, "b": 2 }', :object_class => JSON::GenericObject)
|
14
|
+
JSON.parse("[]")
|
@@ -128,8 +128,10 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
|
|
128
128
|
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
129
129
|
assert_equal too_deep, JSON.dump(eval(too_deep))
|
130
130
|
assert_kind_of String, Marshal.dump(eval(too_deep))
|
131
|
-
|
132
|
-
|
131
|
+
if RUBY_ENGINE != 'truffleruby'
|
132
|
+
assert_raise(ArgumentError) { JSON.dump(eval(too_deep), 100) }
|
133
|
+
assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
|
134
|
+
end
|
133
135
|
assert_equal too_deep, JSON.dump(eval(too_deep), 101)
|
134
136
|
assert_kind_of String, Marshal.dump(eval(too_deep), 101)
|
135
137
|
output = StringIO.new
|
@@ -352,7 +352,7 @@ class JSONGeneratorTest < Test::Unit::TestCase
|
|
352
352
|
too_deep_ary = eval too_deep
|
353
353
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary }
|
354
354
|
assert_raise(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
|
355
|
-
ok = JSON.generate too_deep_ary, :max_nesting =>
|
355
|
+
ok = JSON.generate too_deep_ary, :max_nesting => 102
|
356
356
|
assert_equal too_deep, ok
|
357
357
|
ok = JSON.generate too_deep_ary, :max_nesting => nil
|
358
358
|
assert_equal too_deep, ok
|
data/test/perf_dump.rb
CHANGED
@@ -24,11 +24,11 @@ opts.parse(ARGV)
|
|
24
24
|
@obj = {
|
25
25
|
'a' => 'Alpha', # string
|
26
26
|
'b' => true, # boolean
|
27
|
-
'c' =>
|
28
|
-
'd' => [ true, [false, [-
|
27
|
+
'c' => 12_345, # number
|
28
|
+
'd' => [ true, [false, [-123_456_789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
|
29
29
|
'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
|
30
30
|
'f' => nil, # nil
|
31
|
-
'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
31
|
+
'h' => { 'a' => { 'b' => { 'c' => { 'd' => { 'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
|
32
32
|
'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
|
33
33
|
}
|
34
34
|
|
data/test/prec.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'oj'
|
4
4
|
|
5
|
-
extras = {'locationLng' => -97.14690769100295}
|
5
|
+
extras = { 'locationLng' => -97.14690769100295 }
|
6
6
|
|
7
|
-
Oj.default_options = {float_precision: 17}
|
7
|
+
Oj.default_options = { float_precision: 17 }
|
8
8
|
|
9
9
|
encoded = Oj.dump(extras)
|
10
10
|
puts encoded
|
@@ -15,8 +15,8 @@ require 'active_record'
|
|
15
15
|
Oj::Rails.set_encoder()
|
16
16
|
Oj::Rails.set_decoder()
|
17
17
|
|
18
|
-
Oj.default_options = {float_precision: 17}
|
19
|
-
# Using Oj rails encoder, gets the correct value: {'locationLng':-97.14690769100295}
|
18
|
+
Oj.default_options = { float_precision: 17 }
|
19
|
+
# Using Oj rails encoder, gets the correct value: { 'locationLng':-97.14690769100295 }
|
20
20
|
encoded = ActiveSupport::JSON.encode(extras)
|
21
21
|
puts encoded
|
22
22
|
puts ActiveSupport::JSON.decode(encoded)
|
data/test/test_compat.rb
CHANGED
@@ -467,10 +467,28 @@ class CompatJuice < Minitest::Test
|
|
467
467
|
end
|
468
468
|
|
469
469
|
def test_arg_passing
|
470
|
-
json = Oj.to_json(Argy.new(), :max_nesting=> 40)
|
470
|
+
json = Oj.to_json(Argy.new(), :max_nesting => 40)
|
471
471
|
assert_equal(%|{"args":"[{:max_nesting=>40}]"}|, json)
|
472
472
|
end
|
473
473
|
|
474
|
+
def test_max_nesting
|
475
|
+
assert_raises() { Oj.to_json([[[[[]]]]], :max_nesting => 3) }
|
476
|
+
assert_raises() { Oj.dump([[[[[]]]]], :max_nesting => 3, :mode=>:compat) }
|
477
|
+
|
478
|
+
assert_raises() { Oj.to_json([[]], :max_nesting => 1) }
|
479
|
+
assert_equal('[[]]', Oj.to_json([[]], :max_nesting => 2))
|
480
|
+
|
481
|
+
assert_raises() { Oj.dump([[]], :max_nesting => 1, :mode=>:compat) }
|
482
|
+
assert_equal('[[]]', Oj.dump([[]], :max_nesting => 2, :mode=>:compat))
|
483
|
+
|
484
|
+
assert_raises() { Oj.to_json([[3]], :max_nesting => 1) }
|
485
|
+
assert_equal('[[3]]', Oj.to_json([[3]], :max_nesting => 2))
|
486
|
+
|
487
|
+
assert_raises() { Oj.dump([[3]], :max_nesting => 1, :mode=>:compat) }
|
488
|
+
assert_equal('[[3]]', Oj.dump([[3]], :max_nesting => 2, :mode=>:compat))
|
489
|
+
|
490
|
+
end
|
491
|
+
|
474
492
|
def test_bad_unicode
|
475
493
|
assert_raises() { Oj.to_json("\xE4xy") }
|
476
494
|
end
|
data/test/test_parser_debug.rb
CHANGED
data/test/test_various.rb
CHANGED
@@ -105,6 +105,7 @@ class Juice < Minitest::Test
|
|
105
105
|
allow_gc: false,
|
106
106
|
quirks_mode: false,
|
107
107
|
allow_invalid_unicode: true,
|
108
|
+
float_format: '%0.13g',
|
108
109
|
float_precision: 13,
|
109
110
|
mode: :strict,
|
110
111
|
escape_mode: :ascii,
|
@@ -416,6 +417,11 @@ class Juice < Minitest::Test
|
|
416
417
|
}
|
417
418
|
end
|
418
419
|
|
420
|
+
def test_dump_float
|
421
|
+
json = Oj.dump(1.23e-2, :mode => :null, :float_format => '%0.4f')
|
422
|
+
assert_equal('0.0123', json)
|
423
|
+
end
|
424
|
+
|
419
425
|
# Class
|
420
426
|
def test_class_null
|
421
427
|
json = Oj.dump(Juice, :mode => :null)
|
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: 3.
|
4
|
+
version: 3.16.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: 2023-
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|