json 2.7.2 → 2.12.2

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/CHANGES.md +160 -17
  4. data/LEGAL +8 -0
  5. data/README.md +76 -211
  6. data/ext/json/ext/fbuffer/fbuffer.h +178 -95
  7. data/ext/json/ext/generator/extconf.rb +38 -2
  8. data/ext/json/ext/generator/generator.c +1311 -826
  9. data/ext/json/ext/generator/simd.h +112 -0
  10. data/ext/json/ext/parser/extconf.rb +6 -27
  11. data/ext/json/ext/parser/parser.c +1176 -1971
  12. data/ext/json/ext/vendor/fpconv.c +479 -0
  13. data/ext/json/ext/vendor/jeaiii-ltoa.h +267 -0
  14. data/json.gemspec +44 -49
  15. data/lib/json/add/bigdecimal.rb +2 -2
  16. data/lib/json/add/complex.rb +1 -1
  17. data/lib/json/add/core.rb +1 -1
  18. data/lib/json/add/date.rb +1 -1
  19. data/lib/json/add/date_time.rb +1 -1
  20. data/lib/json/add/exception.rb +1 -1
  21. data/lib/json/add/ostruct.rb +1 -1
  22. data/lib/json/add/range.rb +1 -1
  23. data/lib/json/add/rational.rb +1 -1
  24. data/lib/json/add/regexp.rb +1 -1
  25. data/lib/json/add/struct.rb +1 -1
  26. data/lib/json/add/symbol.rb +8 -4
  27. data/lib/json/add/time.rb +3 -10
  28. data/lib/json/common.rb +647 -241
  29. data/lib/json/ext/generator/state.rb +106 -0
  30. data/lib/json/ext.rb +35 -5
  31. data/lib/json/generic_object.rb +1 -1
  32. data/lib/json/{pure → truffle_ruby}/generator.rb +322 -145
  33. data/lib/json/version.rb +3 -7
  34. data/lib/json.rb +16 -21
  35. metadata +18 -22
  36. data/ext/json/ext/generator/depend +0 -1
  37. data/ext/json/ext/generator/generator.h +0 -177
  38. data/ext/json/ext/parser/depend +0 -1
  39. data/ext/json/ext/parser/parser.h +0 -96
  40. data/ext/json/ext/parser/parser.rl +0 -971
  41. data/ext/json/extconf.rb +0 -3
  42. data/lib/json/pure/parser.rb +0 -337
  43. data/lib/json/pure.rb +0 -15
  44. /data/{LICENSE → COPYING} +0 -0
@@ -1,89 +1,109 @@
1
-
2
1
  #ifndef _FBUFFER_H_
3
2
  #define _FBUFFER_H_
4
3
 
5
4
  #include "ruby.h"
6
-
7
- #ifndef RHASH_SIZE
8
- #define RHASH_SIZE(hsh) (RHASH(hsh)->tbl->num_entries)
5
+ #include "ruby/encoding.h"
6
+ #include "../vendor/jeaiii-ltoa.h"
7
+
8
+ /* shims */
9
+ /* This is the fallback definition from Ruby 3.4 */
10
+
11
+ #ifndef RBIMPL_STDBOOL_H
12
+ #if defined(__cplusplus)
13
+ # if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
14
+ # include <cstdbool>
15
+ # endif
16
+ #elif defined(HAVE_STDBOOL_H)
17
+ # include <stdbool.h>
18
+ #elif !defined(HAVE__BOOL)
19
+ typedef unsigned char _Bool;
20
+ # define bool _Bool
21
+ # define true ((_Bool)+1)
22
+ # define false ((_Bool)+0)
23
+ # define __bool_true_false_are_defined
9
24
  #endif
10
-
11
- #ifndef RFLOAT_VALUE
12
- #define RFLOAT_VALUE(val) (RFLOAT(val)->value)
13
25
  #endif
14
26
 
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
27
+ #ifndef RB_UNLIKELY
28
+ #define RB_UNLIKELY(expr) expr
23
29
  #endif
24
30
 
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)
31
+ #ifndef RB_LIKELY
32
+ #define RB_LIKELY(expr) expr
32
33
  #endif
33
34
 
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)
35
+ #ifndef MAYBE_UNUSED
36
+ # define MAYBE_UNUSED(x) x
39
37
  #endif
40
38
 
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)
39
+ #ifdef RUBY_DEBUG
40
+ #ifndef JSON_DEBUG
41
+ #define JSON_DEBUG RUBY_DEBUG
42
+ #endif
44
43
  #endif
45
44
 
45
+ enum fbuffer_type {
46
+ FBUFFER_HEAP_ALLOCATED = 0,
47
+ FBUFFER_STACK_ALLOCATED = 1,
48
+ };
49
+
46
50
  typedef struct FBufferStruct {
51
+ enum fbuffer_type type;
47
52
  unsigned long initial_length;
48
- char *ptr;
49
53
  unsigned long len;
50
54
  unsigned long capa;
55
+ #ifdef JSON_DEBUG
56
+ unsigned long requested;
57
+ #endif
58
+ char *ptr;
59
+ VALUE io;
51
60
  } FBuffer;
52
61
 
62
+ #define FBUFFER_STACK_SIZE 512
63
+ #define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
53
64
  #define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
54
65
 
55
- #define FBUFFER_PTR(fb) (fb->ptr)
56
- #define FBUFFER_LEN(fb) (fb->len)
57
- #define FBUFFER_CAPA(fb) (fb->capa)
66
+ #define FBUFFER_PTR(fb) ((fb)->ptr)
67
+ #define FBUFFER_LEN(fb) ((fb)->len)
68
+ #define FBUFFER_CAPA(fb) ((fb)->capa)
58
69
  #define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
59
70
 
60
- static FBuffer *fbuffer_alloc(unsigned long initial_length);
61
71
  static void fbuffer_free(FBuffer *fb);
62
72
  static void fbuffer_clear(FBuffer *fb);
63
73
  static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
64
- #ifdef JSON_GENERATOR
65
74
  static void fbuffer_append_long(FBuffer *fb, long number);
75
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr);
76
+ static VALUE fbuffer_finalize(FBuffer *fb);
77
+
78
+ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
79
+ {
80
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
81
+ if (stack_buffer) {
82
+ fb->type = FBUFFER_STACK_ALLOCATED;
83
+ fb->ptr = stack_buffer;
84
+ fb->capa = stack_buffer_size;
85
+ }
86
+ #ifdef JSON_DEBUG
87
+ fb->requested = 0;
66
88
  #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
89
+ }
72
90
 
73
- static FBuffer *fbuffer_alloc(unsigned long initial_length)
91
+ static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
74
92
  {
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;
93
+ #ifdef JSON_DEBUG
94
+ if (consumed > fb->requested) {
95
+ rb_bug("fbuffer: Out of bound write");
96
+ }
97
+ fb->requested = 0;
98
+ #endif
99
+ fb->len += consumed;
81
100
  }
82
101
 
83
102
  static void fbuffer_free(FBuffer *fb)
84
103
  {
85
- if (fb->ptr) ruby_xfree(fb->ptr);
86
- ruby_xfree(fb);
104
+ if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
105
+ ruby_xfree(fb->ptr);
106
+ }
87
107
  }
88
108
 
89
109
  static void fbuffer_clear(FBuffer *fb)
@@ -91,20 +111,61 @@ static void fbuffer_clear(FBuffer *fb)
91
111
  fb->len = 0;
92
112
  }
93
113
 
94
- static void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
114
+ static void fbuffer_flush(FBuffer *fb)
115
+ {
116
+ rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
117
+ fbuffer_clear(fb);
118
+ }
119
+
120
+ static void fbuffer_realloc(FBuffer *fb, unsigned long required)
121
+ {
122
+ if (required > fb->capa) {
123
+ if (fb->type == FBUFFER_STACK_ALLOCATED) {
124
+ const char *old_buffer = fb->ptr;
125
+ fb->ptr = ALLOC_N(char, required);
126
+ fb->type = FBUFFER_HEAP_ALLOCATED;
127
+ MEMCPY(fb->ptr, old_buffer, char, fb->len);
128
+ } else {
129
+ REALLOC_N(fb->ptr, char, required);
130
+ }
131
+ fb->capa = required;
132
+ }
133
+ }
134
+
135
+ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
95
136
  {
137
+ if (RB_UNLIKELY(fb->io)) {
138
+ if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
139
+ fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
140
+ } else {
141
+ fbuffer_flush(fb);
142
+ }
143
+
144
+ if (RB_LIKELY(requested < fb->capa)) {
145
+ return;
146
+ }
147
+ }
148
+
96
149
  unsigned long required;
97
150
 
98
- if (!fb->ptr) {
151
+ if (RB_UNLIKELY(!fb->ptr)) {
99
152
  fb->ptr = ALLOC_N(char, fb->initial_length);
100
153
  fb->capa = fb->initial_length;
101
154
  }
102
155
 
103
156
  for (required = fb->capa; requested > required - fb->len; required <<= 1);
104
157
 
105
- if (required > fb->capa) {
106
- REALLOC_N(fb->ptr, char, required);
107
- fb->capa = required;
158
+ fbuffer_realloc(fb, required);
159
+ }
160
+
161
+ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
162
+ {
163
+ #ifdef JSON_DEBUG
164
+ fb->requested = requested;
165
+ #endif
166
+
167
+ if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
168
+ fbuffer_do_inc_capa(fb, requested);
108
169
  }
109
170
  }
110
171
 
@@ -113,11 +174,24 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
113
174
  if (len > 0) {
114
175
  fbuffer_inc_capa(fb, len);
115
176
  MEMCPY(fb->ptr + fb->len, newstr, char, len);
116
- fb->len += len;
177
+ fbuffer_consumed(fb, len);
117
178
  }
118
179
  }
119
180
 
120
- #ifdef JSON_GENERATOR
181
+ /* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
182
+ static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
183
+ {
184
+ #ifdef JSON_DEBUG
185
+ if (fb->requested < 1) {
186
+ rb_bug("fbuffer: unreserved write");
187
+ }
188
+ fb->requested--;
189
+ #endif
190
+
191
+ fb->ptr[fb->len] = chr;
192
+ fb->len++;
193
+ }
194
+
121
195
  static void fbuffer_append_str(FBuffer *fb, VALUE str)
122
196
  {
123
197
  const char *newstr = StringValuePtr(str);
@@ -127,61 +201,70 @@ static void fbuffer_append_str(FBuffer *fb, VALUE str)
127
201
 
128
202
  fbuffer_append(fb, newstr, len);
129
203
  }
130
- #endif
131
204
 
132
- static void fbuffer_append_char(FBuffer *fb, char newchr)
205
+ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
133
206
  {
134
207
  fbuffer_inc_capa(fb, 1);
135
208
  *(fb->ptr + fb->len) = newchr;
136
- fb->len++;
209
+ fbuffer_consumed(fb, 1);
137
210
  }
138
211
 
139
- #ifdef JSON_GENERATOR
140
- static void freverse(char *start, char *end)
212
+ static inline char *fbuffer_cursor(FBuffer *fb)
141
213
  {
142
- char c;
143
-
144
- while (end > start) {
145
- c = *end, *end-- = *start, *start++ = c;
146
- }
214
+ return fb->ptr + fb->len;
147
215
  }
148
216
 
149
- static long fltoa(long number, char *buf)
217
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
150
218
  {
151
- static char digits[] = "0123456789";
152
- long sign = number;
153
- char* tmp = buf;
154
-
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;
219
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
160
220
  }
161
221
 
222
+ /*
223
+ * Appends the decimal string representation of \a number into the buffer.
224
+ */
162
225
  static void fbuffer_append_long(FBuffer *fb, long number)
163
226
  {
164
- char buf[20];
165
- unsigned long len = fltoa(number, buf);
166
- fbuffer_append(fb, buf, len);
167
- }
168
-
169
- static FBuffer *fbuffer_dup(FBuffer *fb)
170
- {
171
- unsigned long len = fb->len;
172
- FBuffer *result;
227
+ /*
228
+ * The jeaiii_ultoa() function produces digits left-to-right,
229
+ * allowing us to write directly into the buffer, but we don't know
230
+ * the number of resulting characters.
231
+ *
232
+ * We do know, however, that the `number` argument is always in the
233
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
234
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
235
+ * generated is therefore 20 (including a potential sign character).
236
+ */
237
+
238
+ static const int MAX_CHARS_FOR_LONG = 20;
239
+
240
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
241
+
242
+ if (number < 0) {
243
+ fbuffer_append_reserved_char(fb, '-');
244
+
245
+ /*
246
+ * Since number is always > LONG_MIN, `-number` will not overflow
247
+ * and is always the positive abs() value.
248
+ */
249
+ number = -number;
250
+ }
173
251
 
174
- result = fbuffer_alloc(len);
175
- fbuffer_append(result, FBUFFER_PAIR(fb));
176
- return result;
252
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
253
+ fbuffer_advance_to(fb, end);
177
254
  }
178
255
 
179
- static VALUE fbuffer_to_s(FBuffer *fb)
256
+ static VALUE fbuffer_finalize(FBuffer *fb)
180
257
  {
181
- VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
182
- fbuffer_free(fb);
183
- FORCE_UTF8(result);
184
- return result;
258
+ if (fb->io) {
259
+ fbuffer_flush(fb);
260
+ fbuffer_free(fb);
261
+ rb_io_flush(fb->io);
262
+ return fb->io;
263
+ } else {
264
+ VALUE result = rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
265
+ fbuffer_free(fb);
266
+ return result;
267
+ }
185
268
  }
186
- #endif
269
+
187
270
  #endif
@@ -1,4 +1,40 @@
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
+ $defs << "-DJSON_GENERATOR"
9
+ $defs << "-DJSON_DEBUG" if ENV["JSON_DEBUG"]
10
+
11
+ if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
12
+ if RbConfig::CONFIG['host_cpu'] =~ /^(arm.*|aarch64.*)/
13
+ # Try to compile a small program using NEON instructions
14
+ if have_header('arm_neon.h')
15
+ have_type('uint8x16_t', headers=['arm_neon.h']) && try_compile(<<~'SRC')
16
+ #include <arm_neon.h>
17
+ int main() {
18
+ uint8x16_t test = vdupq_n_u8(32);
19
+ return 0;
20
+ }
21
+ SRC
22
+ $defs.push("-DJSON_ENABLE_SIMD")
23
+ end
24
+ end
25
+
26
+ if have_header('x86intrin.h') && have_type('__m128i', headers=['x86intrin.h']) && try_compile(<<~'SRC')
27
+ #include <x86intrin.h>
28
+ int main() {
29
+ __m128i test = _mm_set1_epi8(32);
30
+ return 0;
31
+ }
32
+ SRC
33
+ $defs.push("-DJSON_ENABLE_SIMD")
34
+ end
35
+
36
+ have_header('cpuid.h')
37
+ end
38
+
39
+ create_makefile 'json/ext/generator'
40
+ end