oj 3.11.0 → 3.11.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/ext/oj/buf.h +34 -38
  4. data/ext/oj/cache8.c +59 -62
  5. data/ext/oj/cache8.h +8 -7
  6. data/ext/oj/circarray.c +33 -35
  7. data/ext/oj/circarray.h +11 -9
  8. data/ext/oj/code.c +170 -174
  9. data/ext/oj/code.h +21 -20
  10. data/ext/oj/compat.c +159 -166
  11. data/ext/oj/custom.c +802 -851
  12. data/ext/oj/dump.c +766 -778
  13. data/ext/oj/dump.h +49 -51
  14. data/ext/oj/dump_compat.c +1 -0
  15. data/ext/oj/dump_leaf.c +116 -157
  16. data/ext/oj/dump_object.c +609 -628
  17. data/ext/oj/dump_strict.c +318 -327
  18. data/ext/oj/encode.h +3 -4
  19. data/ext/oj/err.c +39 -25
  20. data/ext/oj/err.h +24 -15
  21. data/ext/oj/extconf.rb +2 -1
  22. data/ext/oj/fast.c +1008 -1038
  23. data/ext/oj/hash.c +62 -66
  24. data/ext/oj/hash.h +7 -6
  25. data/ext/oj/hash_test.c +450 -443
  26. data/ext/oj/mimic_json.c +413 -402
  27. data/ext/oj/object.c +559 -528
  28. data/ext/oj/odd.c +123 -128
  29. data/ext/oj/odd.h +27 -25
  30. data/ext/oj/oj.c +1131 -924
  31. data/ext/oj/oj.h +286 -298
  32. data/ext/oj/parse.c +938 -930
  33. data/ext/oj/parse.h +70 -69
  34. data/ext/oj/rails.c +836 -839
  35. data/ext/oj/rails.h +7 -7
  36. data/ext/oj/reader.c +135 -140
  37. data/ext/oj/reader.h +66 -79
  38. data/ext/oj/resolve.c +43 -43
  39. data/ext/oj/resolve.h +3 -2
  40. data/ext/oj/rxclass.c +67 -68
  41. data/ext/oj/rxclass.h +12 -10
  42. data/ext/oj/saj.c +451 -479
  43. data/ext/oj/scp.c +93 -103
  44. data/ext/oj/sparse.c +770 -730
  45. data/ext/oj/stream_writer.c +120 -149
  46. data/ext/oj/strict.c +71 -86
  47. data/ext/oj/string_writer.c +198 -243
  48. data/ext/oj/trace.c +29 -33
  49. data/ext/oj/trace.h +14 -11
  50. data/ext/oj/util.c +103 -103
  51. data/ext/oj/util.h +3 -2
  52. data/ext/oj/val_stack.c +47 -47
  53. data/ext/oj/val_stack.h +79 -86
  54. data/ext/oj/wab.c +291 -309
  55. data/lib/oj/bag.rb +1 -0
  56. data/lib/oj/easy_hash.rb +5 -4
  57. data/lib/oj/mimic.rb +0 -12
  58. data/lib/oj/version.rb +1 -1
  59. data/test/activerecord/result_test.rb +7 -2
  60. data/test/foo.rb +35 -32
  61. data/test/helper.rb +10 -0
  62. data/test/json_gem/json_generator_test.rb +15 -3
  63. data/test/json_gem/test_helper.rb +8 -0
  64. data/test/test_compat.rb +2 -2
  65. data/test/test_generate.rb +21 -0
  66. data/test/test_hash.rb +10 -0
  67. data/test/test_scp.rb +1 -1
  68. metadata +4 -2
data/ext/oj/compat.c CHANGED
@@ -1,295 +1,288 @@
1
1
  // Copyright (c) 2012 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
2
3
 
3
4
  #include <stdio.h>
4
5
 
5
- #include "oj.h"
6
+ #include "encode.h"
6
7
  #include "err.h"
8
+ #include "hash.h"
9
+ #include "oj.h"
7
10
  #include "parse.h"
8
11
  #include "resolve.h"
9
- #include "hash.h"
10
- #include "encode.h"
11
12
  #include "trace.h"
12
13
 
13
- static void
14
- hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
15
- const char *key = kval->key;
16
- int klen = kval->klen;
17
- Val parent = stack_peek(&pi->stack);
18
- volatile VALUE rkey = kval->key_val;
19
-
20
- if (Qundef == rkey &&
21
- Yes == pi->options.create_ok &&
22
- NULL != pi->options.create_id &&
23
- *pi->options.create_id == *key &&
24
- (int)pi->options.create_id_len == klen &&
25
- 0 == strncmp(pi->options.create_id, key, klen)) {
26
-
27
- parent->classname = oj_strndup(str, len);
28
- parent->clen = len;
14
+ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
15
+ const char * key = kval->key;
16
+ int klen = kval->klen;
17
+ Val parent = stack_peek(&pi->stack);
18
+ volatile VALUE rkey = kval->key_val;
19
+
20
+ if (Qundef == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
21
+ *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
22
+ 0 == strncmp(pi->options.create_id, key, klen)) {
23
+ parent->classname = oj_strndup(str, len);
24
+ parent->clen = len;
29
25
  } else {
30
- volatile VALUE rstr = rb_str_new(str, len);
31
-
32
- if (Qundef == rkey) {
33
- rkey = rb_str_new(key, klen);
34
- rstr = oj_encode(rstr);
35
- rkey = oj_encode(rkey);
36
- if (Yes == pi->options.sym_key) {
37
- rkey = rb_str_intern(rkey);
38
- }
39
- }
40
- if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
41
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
42
-
43
- if (Qnil != clas) {
44
- rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
45
- }
46
- }
47
- if (rb_cHash != rb_obj_class(parent->val)) {
48
- // The rb_hash_set would still work but the unit tests for the
49
- // json gem require the less efficient []= method be called to set
50
- // values. Even using the store method to set the values will fail
51
- // the unit tests.
52
- rb_funcall(parent->val, rb_intern("[]="), 2, rkey, rstr);
53
- } else {
54
- rb_hash_aset(parent->val, rkey, rstr);
55
- }
56
- if (Yes == pi->options.trace) {
57
- oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
58
- }
26
+ volatile VALUE rstr = rb_str_new(str, len);
27
+
28
+ if (Qundef == rkey) {
29
+ rkey = rb_str_new(key, klen);
30
+ rstr = oj_encode(rstr);
31
+ rkey = oj_encode(rkey);
32
+ if (Yes == pi->options.sym_key) {
33
+ rkey = rb_str_intern(rkey);
34
+ }
35
+ }
36
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
37
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
38
+
39
+ if (Qnil != clas) {
40
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
41
+ }
42
+ }
43
+ if (rb_cHash != rb_obj_class(parent->val)) {
44
+ // The rb_hash_set would still work but the unit tests for the
45
+ // json gem require the less efficient []= method be called to set
46
+ // values. Even using the store method to set the values will fail
47
+ // the unit tests.
48
+ rb_funcall(parent->val, rb_intern("[]="), 2, rkey, rstr);
49
+ } else {
50
+ rb_hash_aset(parent->val, rkey, rstr);
51
+ }
52
+ if (Yes == pi->options.trace) {
53
+ oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
54
+ }
59
55
  }
60
56
  }
61
57
 
62
- static VALUE
63
- start_hash(ParseInfo pi) {
64
- volatile VALUE h;
58
+ static VALUE start_hash(ParseInfo pi) {
59
+ volatile VALUE h;
65
60
 
66
61
  if (Qnil != pi->options.hash_class) {
67
- h = rb_class_new_instance(0, NULL, pi->options.hash_class);
62
+ h = rb_class_new_instance(0, NULL, pi->options.hash_class);
68
63
  } else {
69
- h = rb_hash_new();
64
+ h = rb_hash_new();
70
65
  }
71
66
  if (Yes == pi->options.trace) {
72
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
67
+ oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
73
68
  }
74
69
  return h;
75
70
  }
76
71
 
77
- static void
78
- end_hash(struct _parseInfo *pi) {
79
- Val parent = stack_peek(&pi->stack);
72
+ static void end_hash(struct _parseInfo *pi) {
73
+ Val parent = stack_peek(&pi->stack);
80
74
 
81
75
  if (0 != parent->classname) {
82
- volatile VALUE clas;
83
-
84
- clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
85
- if (Qundef != clas) { // else an error
86
- ID creatable = rb_intern("json_creatable?");
87
-
88
- if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
89
- parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
90
- }
91
- }
92
- if (0 != parent->classname) {
93
- xfree((char*)parent->classname);
94
- parent->classname = 0;
95
- }
76
+ volatile VALUE clas;
77
+
78
+ clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
79
+ if (Qundef != clas) { // else an error
80
+ ID creatable = rb_intern("json_creatable?");
81
+
82
+ if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
83
+ parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
84
+ }
85
+ }
86
+ if (0 != parent->classname) {
87
+ xfree((char *)parent->classname);
88
+ parent->classname = 0;
89
+ }
96
90
  }
97
91
  if (Yes == pi->options.trace) {
98
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
92
+ oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
99
93
  }
100
94
  }
101
95
 
102
- static VALUE
103
- calc_hash_key(ParseInfo pi, Val parent) {
104
- volatile VALUE rkey = parent->key_val;
96
+ static VALUE calc_hash_key(ParseInfo pi, Val parent) {
97
+ volatile VALUE rkey = parent->key_val;
105
98
 
106
99
  if (Qundef == rkey) {
107
- rkey = rb_str_new(parent->key, parent->klen);
100
+ rkey = rb_str_new(parent->key, parent->klen);
108
101
  }
109
102
  rkey = oj_encode(rkey);
110
103
  if (Yes == pi->options.sym_key) {
111
- rkey = rb_str_intern(rkey);
104
+ rkey = rb_str_intern(rkey);
112
105
  }
113
106
  return rkey;
114
107
  }
115
108
 
116
- static void
117
- add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
118
- volatile VALUE rstr = rb_str_new(str, len);
109
+ static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
110
+ volatile VALUE rstr = rb_str_new(str, len);
119
111
 
120
112
  rstr = oj_encode(rstr);
121
113
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
122
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
114
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
123
115
 
124
- if (Qnil != clas) {
125
- pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
126
- return;
127
- }
116
+ if (Qnil != clas) {
117
+ pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
118
+ return;
119
+ }
128
120
  }
129
121
  pi->stack.head->val = rstr;
130
122
  if (Yes == pi->options.trace) {
131
- oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, rstr);
123
+ oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, rstr);
132
124
  }
133
125
  }
134
126
 
135
- static void
136
- add_num(ParseInfo pi, NumInfo ni) {
127
+ static void add_num(ParseInfo pi, NumInfo ni) {
137
128
  pi->stack.head->val = oj_num_as_value(ni);
138
129
  if (Yes == pi->options.trace) {
139
- oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, pi->stack.head->val);
130
+ oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, pi->stack.head->val);
140
131
  }
141
132
  }
142
133
 
143
- static void
144
- hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
145
- volatile VALUE rval = oj_num_as_value(ni);
134
+ static void hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
135
+ volatile VALUE rval = oj_num_as_value(ni);
146
136
 
147
137
  if (!oj_use_hash_alt && rb_cHash != rb_obj_class(parent->val)) {
148
- // The rb_hash_set would still work but the unit tests for the
149
- // json gem require the less efficient []= method be called to set
150
- // values. Even using the store method to set the values will fail
151
- // the unit tests.
152
- rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), rval);
138
+ // The rb_hash_set would still work but the unit tests for the
139
+ // json gem require the less efficient []= method be called to set
140
+ // values. Even using the store method to set the values will fail
141
+ // the unit tests.
142
+ rb_funcall(stack_peek(&pi->stack)->val,
143
+ rb_intern("[]="),
144
+ 2,
145
+ calc_hash_key(pi, parent),
146
+ rval);
153
147
  } else {
154
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
148
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
155
149
  }
156
150
  if (Yes == pi->options.trace) {
157
- oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
151
+ oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
158
152
  }
159
153
  }
160
154
 
161
- static void
162
- hash_set_value(ParseInfo pi, Val parent, VALUE value) {
155
+ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
163
156
  if (rb_cHash != rb_obj_class(parent->val)) {
164
- // The rb_hash_set would still work but the unit tests for the
165
- // json gem require the less efficient []= method be called to set
166
- // values. Even using the store method to set the values will fail
167
- // the unit tests.
168
- rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), value);
157
+ // The rb_hash_set would still work but the unit tests for the
158
+ // json gem require the less efficient []= method be called to set
159
+ // values. Even using the store method to set the values will fail
160
+ // the unit tests.
161
+ rb_funcall(stack_peek(&pi->stack)->val,
162
+ rb_intern("[]="),
163
+ 2,
164
+ calc_hash_key(pi, parent),
165
+ value);
169
166
  } else {
170
- rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
167
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
171
168
  }
172
169
  if (Yes == pi->options.trace) {
173
- oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
170
+ oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
174
171
  }
175
172
  }
176
173
 
177
- static VALUE
178
- start_array(ParseInfo pi) {
174
+ static VALUE start_array(ParseInfo pi) {
179
175
  if (Qnil != pi->options.array_class) {
180
- return rb_class_new_instance(0, NULL, pi->options.array_class);
176
+ return rb_class_new_instance(0, NULL, pi->options.array_class);
181
177
  }
182
178
  if (Yes == pi->options.trace) {
183
- oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
179
+ oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
184
180
  }
185
181
  return rb_ary_new();
186
182
  }
187
183
 
188
- static void
189
- array_append_num(ParseInfo pi, NumInfo ni) {
190
- Val parent = stack_peek(&pi->stack);
191
- volatile VALUE rval = oj_num_as_value(ni);
184
+ static void array_append_num(ParseInfo pi, NumInfo ni) {
185
+ Val parent = stack_peek(&pi->stack);
186
+ volatile VALUE rval = oj_num_as_value(ni);
192
187
 
193
188
  if (!oj_use_array_alt && rb_cArray != rb_obj_class(parent->val)) {
194
- // The rb_ary_push would still work but the unit tests for the json
195
- // gem require the less efficient << method be called to push the
196
- // values.
197
- rb_funcall(parent->val, rb_intern("<<"), 1, rval);
189
+ // The rb_ary_push would still work but the unit tests for the json
190
+ // gem require the less efficient << method be called to push the
191
+ // values.
192
+ rb_funcall(parent->val, rb_intern("<<"), 1, rval);
198
193
  } else {
199
- rb_ary_push(parent->val, rval);
194
+ rb_ary_push(parent->val, rval);
200
195
  }
201
196
  if (Yes == pi->options.trace) {
202
- oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
197
+ oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
203
198
  }
204
199
  }
205
200
 
206
- static void
207
- array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
208
- volatile VALUE rstr = rb_str_new(str, len);
201
+ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
202
+ volatile VALUE rstr = rb_str_new(str, len);
209
203
 
210
204
  rstr = oj_encode(rstr);
211
205
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
212
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
206
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
213
207
 
214
- if (Qnil != clas) {
215
- rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
216
- return;
217
- }
208
+ if (Qnil != clas) {
209
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
210
+ return;
211
+ }
218
212
  }
219
213
  rb_ary_push(stack_peek(&pi->stack)->val, rstr);
220
214
  if (Yes == pi->options.trace) {
221
- oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
215
+ oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
222
216
  }
223
217
  }
224
218
 
225
- void
226
- oj_set_compat_callbacks(ParseInfo pi) {
219
+ void oj_set_compat_callbacks(ParseInfo pi) {
227
220
  oj_set_strict_callbacks(pi);
228
- pi->start_hash = start_hash;
229
- pi->end_hash = end_hash;
230
- pi->hash_set_cstr = hash_set_cstr;
231
- pi->hash_set_num = hash_set_num;
232
- pi->hash_set_value = hash_set_value;
233
- pi->add_num = add_num;
234
- pi->add_cstr = add_cstr;
221
+ pi->start_hash = start_hash;
222
+ pi->end_hash = end_hash;
223
+ pi->hash_set_cstr = hash_set_cstr;
224
+ pi->hash_set_num = hash_set_num;
225
+ pi->hash_set_value = hash_set_value;
226
+ pi->add_num = add_num;
227
+ pi->add_cstr = add_cstr;
235
228
  pi->array_append_cstr = array_append_cstr;
236
- pi->start_array = start_array;
237
- pi->array_append_num = array_append_num;
229
+ pi->start_array = start_array;
230
+ pi->array_append_num = array_append_num;
238
231
  }
239
232
 
240
233
  VALUE
241
234
  oj_compat_parse(int argc, VALUE *argv, VALUE self) {
242
- struct _parseInfo pi;
235
+ struct _parseInfo pi;
243
236
 
244
237
  parse_info_init(&pi);
245
- pi.options = oj_default_options;
246
- pi.handler = Qnil;
247
- pi.err_class = Qnil;
248
- pi.max_depth = 0;
249
- pi.options.allow_nan = Yes;
250
- pi.options.nilnil = Yes;
238
+ pi.options = oj_default_options;
239
+ pi.handler = Qnil;
240
+ pi.err_class = Qnil;
241
+ pi.max_depth = 0;
242
+ pi.options.allow_nan = Yes;
243
+ pi.options.nilnil = Yes;
251
244
  pi.options.empty_string = No;
252
245
  oj_set_compat_callbacks(&pi);
253
246
 
254
247
  if (T_STRING == rb_type(*argv)) {
255
- return oj_pi_parse(argc, argv, &pi, 0, 0, false);
248
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
256
249
  } else {
257
- return oj_pi_sparse(argc, argv, &pi, 0);
250
+ return oj_pi_sparse(argc, argv, &pi, 0);
258
251
  }
259
252
  }
260
253
 
261
254
  VALUE
262
255
  oj_compat_load(int argc, VALUE *argv, VALUE self) {
263
- struct _parseInfo pi;
256
+ struct _parseInfo pi;
264
257
 
265
258
  parse_info_init(&pi);
266
- pi.options = oj_default_options;
267
- pi.handler = Qnil;
268
- pi.err_class = Qnil;
269
- pi.max_depth = 0;
270
- pi.options.allow_nan = Yes;
271
- pi.options.nilnil = Yes;
259
+ pi.options = oj_default_options;
260
+ pi.handler = Qnil;
261
+ pi.err_class = Qnil;
262
+ pi.max_depth = 0;
263
+ pi.options.allow_nan = Yes;
264
+ pi.options.nilnil = Yes;
272
265
  pi.options.empty_string = Yes;
273
266
  oj_set_compat_callbacks(&pi);
274
267
 
275
268
  if (T_STRING == rb_type(*argv)) {
276
- return oj_pi_parse(argc, argv, &pi, 0, 0, false);
269
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
277
270
  } else {
278
- return oj_pi_sparse(argc, argv, &pi, 0);
271
+ return oj_pi_sparse(argc, argv, &pi, 0);
279
272
  }
280
273
  }
281
274
 
282
275
  VALUE
283
276
  oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
284
- struct _parseInfo pi;
277
+ struct _parseInfo pi;
285
278
 
286
279
  parse_info_init(&pi);
287
- pi.options = oj_default_options;
288
- pi.handler = Qnil;
289
- pi.err_class = Qnil;
290
- pi.max_depth = 0;
280
+ pi.options = oj_default_options;
281
+ pi.handler = Qnil;
282
+ pi.err_class = Qnil;
283
+ pi.max_depth = 0;
291
284
  pi.options.allow_nan = Yes;
292
- pi.options.nilnil = Yes;
285
+ pi.options.nilnil = Yes;
293
286
  oj_set_compat_callbacks(&pi);
294
287
 
295
288
  return oj_pi_parse(argc, argv, &pi, json, len, false);