oj 3.15.0 → 3.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }