oj 3.15.0 → 3.16.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 929e5766d8a556ed9590574901fe8268979fd6c165d10e1a2a8601a594dd6897
4
- data.tar.gz: 966fedee6a16f73c004b56050474377ae48521f5858da962f0f0416b5636a4c3
3
+ metadata.gz: 3a6b15904386e7010fa071ecc27b8f3a6d5023c8162ea7042fc39e29c373a20a
4
+ data.tar.gz: b862e59cff0ba3a15eadec34fe807d5893c9c1d1e9fcce01ced66ab28e642181
5
5
  SHA512:
6
- metadata.gz: f458f616c33c48d84cb0e1d2c1c45e8b677609c19251345328721fe3b7b6c4c5aa1fd4906da2234b60889a6e3bb45ee0371e7de7565bb6d1aa7d924e231e36dd
7
- data.tar.gz: fbbf233fc1ce57739d2fe538c179a24282e4c043740c2b47cce29745e3847712afa7828d321b877a3c29ab7d2216e7185af9a85bb9c8eb96b6036f9e3e49c1d1
6
+ metadata.gz: f186fb40ebcc3792b4949f81ce3b2efb22bee9dce9b44785eeca37cc27e25b79e8afa4fc5872232001d28a3d0b279fc222d5f7d2f8aaa091fe3a135246139b64
7
+ data.tar.gz: 9c1536bfc15eb3fe02e92d803417ef1ccce5084036aaf8df5ae2b61995a429ab8ae00be5e9b7c79e11839d781c67523ed5bc534df36a40b59dcc61db6551158f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.3 - 2023-12-11
4
+
5
+ - Fixed the gemspec to allow earlier versions of the bigdecimal gem.
6
+
7
+ ## 3.16.2 - 2023-12-06
8
+
9
+ - Fixed documentation formatting.
10
+
11
+ - Added option to the "usual" parser to raise an error on an empty input string.
12
+
13
+ ## 3.16.1 - 2023-09-01
14
+
15
+ - Fixed exception type on number parsing. (thank you @jasonpenny)
16
+
17
+ ## 3.16.0 - 2023-08-16
18
+
19
+ - Added the `float_format` option.
20
+
21
+ - Expanded the `max_nesting` option to allow integer values as well as
22
+ the previous boolean (true or nil).
23
+
24
+ - Skip nesting tests with Truffle Ruby in the json gem tests.
25
+
26
+ ## 3.15.1 - 2023-07-30
27
+
28
+ - Add protection against some using `require 'oj/json`, an internal file.
29
+
30
+ - Fixed non-json errors when in compat mode.
31
+
3
32
  ## 3.15.0 - 2023-06-02
4
33
 
5
34
  - Added `omit_null_byte` option when dumping.
data/ext/oj/cache.c CHANGED
@@ -260,7 +260,8 @@ void cache_set_expunge_rate(Cache c, int rate) {
260
260
  c->xrate = (uint8_t)rate;
261
261
  }
262
262
 
263
- void cache_free(Cache c) {
263
+ void cache_free(void *data) {
264
+ Cache c = (Cache)data;
264
265
  uint64_t i;
265
266
 
266
267
  for (i = 0; i < c->size; i++) {
@@ -276,7 +277,8 @@ void cache_free(Cache c) {
276
277
  OJ_FREE(c);
277
278
  }
278
279
 
279
- void cache_mark(Cache c) {
280
+ void cache_mark(void *data) {
281
+ Cache c = (Cache)data;
280
282
  uint64_t i;
281
283
 
282
284
  #if !HAVE_PTHREAD_MUTEX_INIT
data/ext/oj/cache.h CHANGED
@@ -10,10 +10,11 @@
10
10
  #define CACHE_MAX_KEY 35
11
11
 
12
12
  struct _cache;
13
+ typedef struct _cache *Cache;
13
14
 
14
15
  extern struct _cache *cache_create(size_t size, VALUE (*form)(const char *str, size_t len), bool mark, bool locking);
15
- extern void cache_free(struct _cache *c);
16
- extern void cache_mark(struct _cache *c);
16
+ extern void cache_free(void *data);
17
+ extern void cache_mark(void *data);
17
18
  extern void cache_set_form(struct _cache *c, VALUE (*form)(const char *str, size_t len));
18
19
  extern VALUE cache_intern(struct _cache *c, const char *key, size_t len);
19
20
  extern void cache_set_expunge_rate(struct _cache *c, int rate);
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
- int neg = 0;
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 = 1;
193
+ neg = true;
194
194
  num = -num;
195
195
  }
196
196
  *b-- = '\0';
197
197
  if (0 < num) {
198
- for (; 0 < num; num /= 10, b--) {
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 *key = kval->key;
17
- int klen = kval->klen;
18
- Val parent = stack_peek(&pi->stack);
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 == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
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 *key = kval->key;
893
- int klen = kval->klen;
894
- Val parent = stack_peek(&pi->stack);
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 == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
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
- // volatile VALUE rstr = rb_utf8_str_new(str, len);
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
@@ -727,8 +727,11 @@ static void debug_raise(const char *orig, size_t cnt, int line) {
727
727
 
728
728
  void oj_dump_raw_json(VALUE obj, int depth, Out out) {
729
729
  if (oj_string_writer_class == rb_obj_class(obj)) {
730
- StrWriter sw = (StrWriter)DATA_PTR(obj);
731
- size_t len = sw->out.cur - sw->out.buf;
730
+ StrWriter sw;
731
+ size_t len;
732
+
733
+ sw = oj_str_writer_unwrap(obj);
734
+ len = sw->out.cur - sw->out.buf;
732
735
 
733
736
  if (0 < len) {
734
737
  len--;
@@ -1010,11 +1013,33 @@ static const char digits_table[] = "\
1010
1013
  80818283848586878889\
1011
1014
  90919293949596979899";
1012
1015
 
1016
+ char *oj_longlong_to_string(long long num, bool negative, char *buf) {
1017
+ while (100 <= num) {
1018
+ unsigned idx = num % 100 * 2;
1019
+ *buf-- = digits_table[idx + 1];
1020
+ *buf-- = digits_table[idx];
1021
+ num /= 100;
1022
+ }
1023
+ if (num < 10) {
1024
+ *buf-- = num + '0';
1025
+ } else {
1026
+ *buf-- = digits_table[num * 2 + 1];
1027
+ *buf-- = digits_table[num * 2];
1028
+ }
1029
+
1030
+ if (negative) {
1031
+ *buf = '-';
1032
+ } else {
1033
+ buf++;
1034
+ }
1035
+ return buf;
1036
+ }
1037
+
1013
1038
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1014
1039
  char buf[32];
1015
1040
  char *b = buf + sizeof(buf) - 1;
1016
1041
  long long num = NUM2LL(obj);
1017
- int neg = 0;
1042
+ bool neg = false;
1018
1043
  size_t cnt = 0;
1019
1044
  bool dump_as_string = false;
1020
1045
 
@@ -1023,7 +1048,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1023
1048
  dump_as_string = true;
1024
1049
  }
1025
1050
  if (0 > num) {
1026
- neg = 1;
1051
+ neg = true;
1027
1052
  num = -num;
1028
1053
  }
1029
1054
  *b-- = '\0';
@@ -1032,24 +1057,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1032
1057
  *b-- = '"';
1033
1058
  }
1034
1059
  if (0 < num) {
1035
- while (100 <= num) {
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
- }
1060
+ b = oj_longlong_to_string(num, neg, b);
1053
1061
  } else {
1054
1062
  *b = '0';
1055
1063
  }
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
- for (; 0 < num; num /= 10, b--) {
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
- VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
855
- VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
856
- VALUE generator = rb_const_get(ext, rb_intern("Generator"));
857
- VALUE state_class = rb_const_get(generator, rb_intern("State"));
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
- if (state_class == rb_obj_class(state)) {
860
- rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
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
- // When JSON.dump is called then an ArgumentError is expected and the
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..-1] if type.start_with?('tcs-')
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; // number of leaves/branches in the doc
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
- for (; 0 < num; num /= 10, b--) {
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++; // skip quote character
576
+ h++; // skip quote character
580
577
  t++;
581
578
  value = h;
582
579
  for (; '"' != *h; h++, t++) {
@@ -783,11 +780,10 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
783
780
  }
784
781
  }
785
782
  #endif
786
- doc->json = json;
787
- self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
788
- doc->self = self;
789
- DATA_PTR(doc->self) = doc;
790
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
783
+ doc->json = json;
784
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
785
+ doc->self = self;
786
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
791
787
  if (given || 0 != ex) {
792
788
  DATA_PTR(doc->self) = NULL;
793
789
  // TBD is this needed?
@@ -1612,7 +1608,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1612
1608
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1613
1609
  */
1614
1610
  static VALUE doc_size(VALUE self) {
1615
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1611
+ Doc d;
1612
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1613
+ return ULONG2NUM(d->size);
1616
1614
  }
1617
1615
 
1618
1616
  /* @overload close() => nil
data/ext/oj/intern.c CHANGED
@@ -85,20 +85,31 @@ static VALUE form_attr(const char *str, size_t len) {
85
85
  return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
86
86
  }
87
87
 
88
+ static const rb_data_type_t oj_cache_type = {
89
+ "Oj/cache",
90
+ {
91
+ cache_mark,
92
+ cache_free,
93
+ NULL,
94
+ },
95
+ 0,
96
+ 0,
97
+ };
98
+
88
99
  void oj_hash_init(void) {
89
100
  VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
90
101
  rb_undef_alloc_func(cache_class);
91
102
 
92
103
  struct _cache *str_cache = cache_create(0, form_str, true, true);
93
- str_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, str_cache);
104
+ str_cache_obj = TypedData_Wrap_Struct(cache_class, &oj_cache_type, str_cache);
94
105
  rb_gc_register_address(&str_cache_obj);
95
106
 
96
107
  struct _cache *sym_cache = cache_create(0, form_sym, true, true);
97
- sym_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, sym_cache);
108
+ sym_cache_obj = TypedData_Wrap_Struct(cache_class, &oj_cache_type, sym_cache);
98
109
  rb_gc_register_address(&sym_cache_obj);
99
110
 
100
111
  struct _cache *attr_cache = cache_create(0, form_attr, false, true);
101
- attr_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, attr_cache);
112
+ attr_cache_obj = TypedData_Wrap_Struct(cache_class, &oj_cache_type, attr_cache);
102
113
  rb_gc_register_address(&attr_cache_obj);
103
114
 
104
115
  memset(class_hash.slots, 0, sizeof(class_hash.slots));
@@ -118,17 +129,23 @@ oj_str_intern(const char *key, size_t len) {
118
129
  #if HAVE_RB_ENC_INTERNED_STR && 0
119
130
  return rb_enc_interned_str(key, len, rb_utf8_encoding());
120
131
  #else
121
- return cache_intern(DATA_PTR(str_cache_obj), key, len);
132
+ Cache c;
133
+ TypedData_Get_Struct(str_cache_obj, struct _cache, &oj_cache_type, c);
134
+ return cache_intern(c, key, len);
122
135
  #endif
123
136
  }
124
137
 
125
138
  VALUE
126
139
  oj_sym_intern(const char *key, size_t len) {
127
- return cache_intern(DATA_PTR(sym_cache_obj), key, len);
140
+ Cache c;
141
+ TypedData_Get_Struct(sym_cache_obj, struct _cache, &oj_cache_type, c);
142
+ return cache_intern(c, key, len);
128
143
  }
129
144
 
130
145
  ID oj_attr_intern(const char *key, size_t len) {
131
- return cache_intern(DATA_PTR(attr_cache_obj), key, len);
146
+ Cache c;
147
+ TypedData_Get_Struct(attr_cache_obj, struct _cache, &oj_cache_type, c);
148
+ return cache_intern(c, key, len);
132
149
  }
133
150
 
134
151
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
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;
@@ -427,7 +425,7 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
427
425
  * - *:object_nl* [_String_] String placed after a JSON object
428
426
  * - *:array_nl* [_String_] String placed after a JSON array
429
427
  * - *:ascii_only* [_Boolean_] if not nil or false then use only ascii characters in the output.
430
- * Note JSON.generate does support this even if it is not documented.
428
+ * Note JSON.generate does support this even if it is not documented.
431
429
  *
432
430
  * Returns [_String_] generated JSON.
433
431
  */
@@ -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) {
@@ -608,9 +605,9 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
608
605
  * - *source* [_String_|IO] source to parse
609
606
  * - *opts* [_Hash_] options
610
607
  * - *:symbolize* [Boolean] _names flag indicating JSON object keys should be Symbols instead of
611
- * Strings
608
+ * Strings
612
609
  * - *:create_additions* [Boolean] flag indicating a key matching +create_id+ in a JSON object
613
- * should trigger the creation of Ruby Object
610
+ * should trigger the creation of Ruby Object
614
611
  *
615
612
  * Returns [Object]
616
613
  * @see create_id=
data/ext/oj/object.c CHANGED
@@ -83,8 +83,9 @@ static int parse_num(const char *str, const char *end, int cnt) {
83
83
 
84
84
  VALUE
85
85
  oj_parse_xml_time(const char *str, int len) {
86
- VALUE args[8];
87
- const char *end = str + len;
86
+ VALUE args[7];
87
+ const char *end = str + len;
88
+ const char *orig = str;
88
89
  int n;
89
90
 
90
91
  // year
@@ -144,7 +145,9 @@ oj_parse_xml_time(const char *str, int len) {
144
145
  char c = *str++;
145
146
 
146
147
  if ('.' == c) {
147
- long long nsec = 0;
148
+ unsigned long long num = 0;
149
+ unsigned long long den = 1;
150
+ const unsigned long long last_den_limit = ULLONG_MAX / 10;
148
151
 
149
152
  for (; str < end; str++) {
150
153
  c = *str;
@@ -152,9 +155,14 @@ oj_parse_xml_time(const char *str, int len) {
152
155
  str++;
153
156
  break;
154
157
  }
155
- nsec = nsec * 10 + (c - '0');
158
+ if (den > last_den_limit) {
159
+ // bail to Time.parse if there are more fractional digits than a ULLONG rational can hold
160
+ return rb_funcall(rb_cTime, oj_parse_id, 1, rb_str_new(orig, len));
161
+ }
162
+ num = num * 10 + (c - '0');
163
+ den *= 10;
156
164
  }
157
- args[5] = rb_float_new((double)n + ((double)nsec + 0.5) / 1000000000.0);
165
+ args[5] = rb_funcall(INT2NUM(n), oj_plus_id, 1, rb_rational_new(ULL2NUM(num), ULL2NUM(den)));
158
166
  } else {
159
167
  args[5] = rb_ll2inum(n);
160
168
  }