json 2.6.1 → 2.21.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/CHANGES.md +370 -17
  4. data/LEGAL +20 -0
  5. data/README.md +96 -213
  6. data/ext/json/ext/fbuffer/fbuffer.h +190 -118
  7. data/ext/json/ext/generator/extconf.rb +17 -2
  8. data/ext/json/ext/generator/generator.c +1532 -1074
  9. data/ext/json/ext/json.h +183 -0
  10. data/ext/json/ext/parser/extconf.rb +30 -25
  11. data/ext/json/ext/parser/parser.c +2837 -3258
  12. data/ext/json/ext/simd/conf.rb +24 -0
  13. data/ext/json/ext/simd/simd.h +208 -0
  14. data/ext/json/ext/vendor/fast_float_parser.h +814 -0
  15. data/ext/json/ext/vendor/fpconv.c +480 -0
  16. data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
  17. data/json.gemspec +48 -53
  18. data/lib/json/add/bigdecimal.rb +39 -10
  19. data/lib/json/add/complex.rb +29 -6
  20. data/lib/json/add/core.rb +2 -1
  21. data/lib/json/add/date.rb +27 -7
  22. data/lib/json/add/date_time.rb +26 -9
  23. data/lib/json/add/exception.rb +25 -7
  24. data/lib/json/add/ostruct.rb +32 -9
  25. data/lib/json/add/range.rb +33 -8
  26. data/lib/json/add/rational.rb +28 -6
  27. data/lib/json/add/regexp.rb +26 -8
  28. data/lib/json/add/set.rb +25 -6
  29. data/lib/json/add/string.rb +35 -0
  30. data/lib/json/add/struct.rb +29 -7
  31. data/lib/json/add/symbol.rb +34 -7
  32. data/lib/json/add/time.rb +29 -15
  33. data/lib/json/common.rb +746 -267
  34. data/lib/json/ext/generator/state.rb +104 -0
  35. data/lib/json/ext.rb +61 -5
  36. data/lib/json/generic_object.rb +7 -11
  37. data/lib/json/truffle_ruby/generator.rb +790 -0
  38. data/lib/json/version.rb +3 -7
  39. data/lib/json.rb +134 -24
  40. metadata +22 -26
  41. data/VERSION +0 -1
  42. data/ext/json/ext/generator/depend +0 -1
  43. data/ext/json/ext/generator/generator.h +0 -174
  44. data/ext/json/ext/parser/depend +0 -1
  45. data/ext/json/ext/parser/parser.h +0 -96
  46. data/ext/json/ext/parser/parser.rl +0 -977
  47. data/ext/json/extconf.rb +0 -3
  48. data/lib/json/pure/generator.rb +0 -479
  49. data/lib/json/pure/parser.rb +0 -337
  50. data/lib/json/pure.rb +0 -15
  51. /data/{LICENSE → COPYING} +0 -0
@@ -1,89 +1,75 @@
1
-
2
1
  #ifndef _FBUFFER_H_
3
2
  #define _FBUFFER_H_
4
3
 
5
- #include "ruby.h"
6
-
7
- #ifndef RHASH_SIZE
8
- #define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
9
- #endif
10
-
11
- #ifndef RFLOAT_VALUE
12
- #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
13
- #endif
14
-
15
- #ifndef RARRAY_LEN
16
- #define RARRAY_LEN(ARRAY) RARRAY(ARRAY)->len
17
- #endif
18
- #ifndef RSTRING_PTR
19
- #define RSTRING_PTR(string) RSTRING(string)->ptr
20
- #endif
21
- #ifndef RSTRING_LEN
22
- #define RSTRING_LEN(string) RSTRING(string)->len
23
- #endif
24
-
25
- #ifdef PRIsVALUE
26
- # define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
27
- # define RB_OBJ_STRING(obj) (obj)
28
- #else
29
- # define PRIsVALUE "s"
30
- # define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
31
- # define RB_OBJ_STRING(obj) StringValueCStr(obj)
32
- #endif
4
+ #include "../json.h"
5
+ #include "../vendor/jeaiii-ltoa.h"
33
6
 
34
- #ifdef HAVE_RUBY_ENCODING_H
35
- #include "ruby/encoding.h"
36
- #define FORCE_UTF8(obj) rb_enc_associate((obj), rb_utf8_encoding())
37
- #else
38
- #define FORCE_UTF8(obj)
39
- #endif
40
-
41
- /* We don't need to guard objects for rbx, so let's do nothing at all. */
42
- #ifndef RB_GC_GUARD
43
- #define RB_GC_GUARD(object)
44
- #endif
7
+ enum fbuffer_type {
8
+ FBUFFER_HEAP_ALLOCATED = 0,
9
+ FBUFFER_STACK_ALLOCATED = 1,
10
+ };
45
11
 
46
12
  typedef struct FBufferStruct {
47
- unsigned long initial_length;
13
+ enum fbuffer_type type;
14
+ size_t initial_length;
15
+ size_t len;
16
+ size_t capa;
17
+ #if JSON_DEBUG
18
+ size_t requested;
19
+ #endif
48
20
  char *ptr;
49
- unsigned long len;
50
- unsigned long capa;
21
+ VALUE io;
51
22
  } FBuffer;
52
23
 
24
+ #define FBUFFER_STACK_SIZE 512
25
+ #define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
53
26
  #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
54
27
 
55
- #define FBUFFER_PTR(fb) (fb->ptr)
56
- #define FBUFFER_LEN(fb) (fb->len)
57
- #define FBUFFER_CAPA(fb) (fb->capa)
28
+ #define FBUFFER_PTR(fb) ((fb)->ptr)
29
+ #define FBUFFER_LEN(fb) ((fb)->len)
30
+ #define FBUFFER_CAPA(fb) ((fb)->capa)
58
31
  #define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
59
32
 
60
- static FBuffer *fbuffer_alloc(unsigned long initial_length);
61
33
  static void fbuffer_free(FBuffer *fb);
62
34
  static void fbuffer_clear(FBuffer *fb);
63
- static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
64
- #ifdef JSON_GENERATOR
35
+ static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
65
36
  static void fbuffer_append_long(FBuffer *fb, long number);
37
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr);
38
+ static VALUE fbuffer_finalize(FBuffer *fb);
39
+
40
+ static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
41
+ {
42
+ if (RTEST(io)) {
43
+ JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
44
+ fb->io = io;
45
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
46
+ } else {
47
+ fb->type = FBUFFER_STACK_ALLOCATED;
48
+ fb->ptr = stack_buffer;
49
+ fb->capa = stack_buffer_size;
50
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
51
+ }
52
+ #if JSON_DEBUG
53
+ fb->requested = 0;
66
54
  #endif
67
- static void fbuffer_append_char(FBuffer *fb, char newchr);
68
- #ifdef JSON_GENERATOR
69
- static FBuffer *fbuffer_dup(FBuffer *fb);
70
- static VALUE fbuffer_to_s(FBuffer *fb);
71
- #endif
55
+ }
72
56
 
73
- static FBuffer *fbuffer_alloc(unsigned long initial_length)
57
+ static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
74
58
  {
75
- FBuffer *fb;
76
- if (initial_length <= 0) initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
77
- fb = ALLOC(FBuffer);
78
- memset((void *) fb, 0, sizeof(FBuffer));
79
- fb->initial_length = initial_length;
80
- return fb;
59
+ #if JSON_DEBUG
60
+ if (consumed > fb->requested) {
61
+ rb_bug("fbuffer: Out of bound write");
62
+ }
63
+ fb->requested = 0;
64
+ #endif
65
+ fb->len += consumed;
81
66
  }
82
67
 
83
68
  static void fbuffer_free(FBuffer *fb)
84
69
  {
85
- if (fb->ptr) ruby_xfree(fb->ptr);
86
- ruby_xfree(fb);
70
+ if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
71
+ JSON_SIZED_FREE_N(fb->ptr, fb->capa);
72
+ }
87
73
  }
88
74
 
89
75
  static void fbuffer_clear(FBuffer *fb)
@@ -91,97 +77,183 @@ static void fbuffer_clear(FBuffer *fb)
91
77
  fb->len = 0;
92
78
  }
93
79
 
94
- static void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
80
+ static void fbuffer_flush(FBuffer *fb)
95
81
  {
96
- unsigned long required;
82
+ rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
83
+ fbuffer_clear(fb);
84
+ }
85
+
86
+ static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
87
+ {
88
+ if (new_capa > fb->capa) {
89
+ if (fb->type == FBUFFER_STACK_ALLOCATED) {
90
+ const char *old_buffer = fb->ptr;
91
+ fb->ptr = ALLOC_N(char, new_capa);
92
+ fb->type = FBUFFER_HEAP_ALLOCATED;
93
+ MEMCPY(fb->ptr, old_buffer, char, fb->len);
94
+ } else {
95
+ JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa);
96
+ }
97
+ fb->capa = new_capa;
98
+ }
99
+ }
97
100
 
98
- if (!fb->ptr) {
99
- fb->ptr = ALLOC_N(char, fb->initial_length);
100
- fb->capa = fb->initial_length;
101
+ static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
102
+ {
103
+ if (RB_UNLIKELY(fb->io)) {
104
+ if (fb->capa != 0) {
105
+ fbuffer_flush(fb);
106
+ if (RB_LIKELY(requested < fb->capa)) {
107
+ return;
108
+ }
109
+ }
101
110
  }
102
111
 
103
- for (required = fb->capa; requested > required - fb->len; required <<= 1);
112
+ size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
113
+ size_t needed_capa = requested + fb->len;
104
114
 
105
- if (required > fb->capa) {
106
- REALLOC_N(fb->ptr, char, required);
107
- fb->capa = required;
115
+ while (new_capa < needed_capa) {
116
+ new_capa *= 2;
108
117
  }
118
+
119
+ fbuffer_realloc(fb, new_capa);
109
120
  }
110
121
 
111
- static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
122
+ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
112
123
  {
113
- if (len > 0) {
114
- fbuffer_inc_capa(fb, len);
115
- MEMCPY(fb->ptr + fb->len, newstr, char, len);
116
- fb->len += len;
124
+ #if JSON_DEBUG
125
+ fb->requested = requested;
126
+ #endif
127
+
128
+ if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
129
+ fbuffer_do_inc_capa(fb, requested);
117
130
  }
118
131
  }
119
132
 
120
- #ifdef JSON_GENERATOR
121
- static void fbuffer_append_str(FBuffer *fb, VALUE str)
133
+ static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
122
134
  {
123
- const char *newstr = StringValuePtr(str);
124
- unsigned long len = RSTRING_LEN(str);
135
+ size_t result = a * b;
136
+ if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
137
+ rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
138
+ }
139
+ return result;
140
+ }
125
141
 
126
- RB_GC_GUARD(str);
142
+ static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
143
+ {
144
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
145
+ fbuffer_consumed(fb, len);
146
+ }
127
147
 
128
- fbuffer_append(fb, newstr, len);
148
+ static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
149
+ {
150
+ if (len > 0) {
151
+ fbuffer_inc_capa(fb, len);
152
+ fbuffer_append_reserved(fb, newstr, len);
153
+ }
129
154
  }
130
- #endif
131
155
 
132
- static void fbuffer_append_char(FBuffer *fb, char newchr)
156
+ /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
157
+ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
133
158
  {
134
- fbuffer_inc_capa(fb, 1);
135
- *(fb->ptr + fb->len) = newchr;
159
+ #if JSON_DEBUG
160
+ if (fb->requested < 1) {
161
+ rb_bug("fbuffer: unreserved write");
162
+ }
163
+ fb->requested--;
164
+ #endif
165
+
166
+ fb->ptr[fb->len] = chr;
136
167
  fb->len++;
137
168
  }
138
169
 
139
- #ifdef JSON_GENERATOR
140
- static void freverse(char *start, char *end)
170
+ static void fbuffer_append_str(FBuffer *fb, VALUE str)
141
171
  {
142
- char c;
172
+ const char *ptr;
173
+ size_t len;
174
+ RSTRING_GETMEM(str, ptr, len);
143
175
 
144
- while (end > start) {
145
- c = *end, *end-- = *start, *start++ = c;
176
+ fbuffer_append(fb, ptr, len);
177
+ RB_GC_GUARD(str);
178
+ }
179
+
180
+ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
181
+ {
182
+ const char *ptr;
183
+ size_t len;
184
+ RSTRING_GETMEM(str, ptr, len);
185
+
186
+ fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
187
+ while (repeat) {
188
+ #if JSON_DEBUG
189
+ fb->requested = len;
190
+ #endif
191
+ fbuffer_append_reserved(fb, ptr, len);
192
+ repeat--;
146
193
  }
194
+ RB_GC_GUARD(str);
147
195
  }
148
196
 
149
- static long fltoa(long number, char *buf)
197
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
150
198
  {
151
- static char digits[] = "0123456789";
152
- long sign = number;
153
- char* tmp = buf;
199
+ fbuffer_inc_capa(fb, 1);
200
+ *(fb->ptr + fb->len) = newchr;
201
+ fbuffer_consumed(fb, 1);
202
+ }
154
203
 
155
- if (sign < 0) number = -number;
156
- do *tmp++ = digits[number % 10]; while (number /= 10);
157
- if (sign < 0) *tmp++ = '-';
158
- freverse(buf, tmp - 1);
159
- return tmp - buf;
204
+ static inline char *fbuffer_cursor(FBuffer *fb)
205
+ {
206
+ return fb->ptr + fb->len;
160
207
  }
161
208
 
162
- static void fbuffer_append_long(FBuffer *fb, long number)
209
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
163
210
  {
164
- char buf[20];
165
- unsigned long len = fltoa(number, buf);
166
- fbuffer_append(fb, buf, len);
211
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
167
212
  }
168
213
 
169
- static FBuffer *fbuffer_dup(FBuffer *fb)
214
+ /*
215
+ * Appends the decimal string representation of \a number into the buffer.
216
+ */
217
+ static void fbuffer_append_long(FBuffer *fb, long number)
170
218
  {
171
- unsigned long len = fb->len;
172
- FBuffer *result;
219
+ /*
220
+ * The jeaiii_ultoa() function produces digits left-to-right,
221
+ * allowing us to write directly into the buffer, but we don't know
222
+ * the number of resulting characters.
223
+ *
224
+ * We do know, however, that the `number` argument is always in the
225
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
226
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
227
+ * generated is therefore 20 (including a potential sign character).
228
+ */
229
+
230
+ static const int MAX_CHARS_FOR_LONG = 20;
231
+
232
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
233
+
234
+ if (number < 0) {
235
+ fbuffer_append_reserved_char(fb, '-');
236
+
237
+ /*
238
+ * Since number is always > LONG_MIN, `-number` will not overflow
239
+ * and is always the positive abs() value.
240
+ */
241
+ number = -number;
242
+ }
173
243
 
174
- result = fbuffer_alloc(len);
175
- fbuffer_append(result, FBUFFER_PAIR(fb));
176
- return result;
244
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
245
+ fbuffer_advance_to(fb, end);
177
246
  }
178
247
 
179
- static VALUE fbuffer_to_s(FBuffer *fb)
248
+ static VALUE fbuffer_finalize(FBuffer *fb)
180
249
  {
181
- VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
182
- fbuffer_free(fb);
183
- FORCE_UTF8(result);
184
- return result;
250
+ if (fb->io) {
251
+ fbuffer_flush(fb);
252
+ rb_io_flush(fb->io);
253
+ return fb->io;
254
+ } else {
255
+ return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
256
+ }
185
257
  }
186
- #endif
187
- #endif
258
+
259
+ #endif // _FBUFFER_H_
@@ -1,4 +1,19 @@
1
1
  require 'mkmf'
2
2
 
3
- $defs << "-DJSON_GENERATOR"
4
- create_makefile 'json/ext/generator'
3
+ if RUBY_ENGINE == 'truffleruby'
4
+ # The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension
5
+ File.write('Makefile', dummy_makefile("").join)
6
+ else
7
+ append_cflags("-std=c99")
8
+ have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
9
+ have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
10
+
11
+ $defs << "-DJSON_GENERATOR"
12
+ $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
13
+
14
+ if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
15
+ load __dir__ + "/../simd/conf.rb"
16
+ end
17
+
18
+ create_makefile 'json/ext/generator'
19
+ end