json 2.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b909ed90787afa31835c9926529808ca3c6b161755e1f6126b7fe203231fa0c2
4
- data.tar.gz: efd6a873e98f9e4b2b8c078210f141dfde5f3773884bd5851f228c184ff94ff7
3
+ metadata.gz: b76bc749fd1bc82cd84df8c7d14317305d5426e0962d0e034864b218e952e474
4
+ data.tar.gz: 8e8d9179b6ca7ce69c7a281691973dc9cb1e6b147a5812b96d02c3b41d8f8cec
5
5
  SHA512:
6
- metadata.gz: bff39363d5d7fa2f209d5bf97a5728738058bc83600b732d95487e263a5a3901b1e18d5c42d7d2be019ac55ef069a50cedc1d9a8d22b11e6061f2eef747dc94d
7
- data.tar.gz: 693e480f8f2489e26c43eef56ad8977ab3f00fada03318a48b88ac55e2b82b15ffae92fd05dd47d86686fae39169b079f59a68fffc6efaf44ee3503a45060e30
6
+ metadata.gz: a241873428f8de2106604f3a31484aa243b6f62c6c6f38f8e381669f937b19f03b35add48ddf4f1d24c5a9f469c2dbdc8bafca36d8f3bcf678313ad8d9ea11d3
7
+ data.tar.gz: 99d07dbf3dcacda3cf662ee05bc181aa3ca6e43ce8779b9f5edc454fba8e718abe876eb81b4918e0c0bb1a3b4286e1d85df086425e552743420942a8e0098f0e
data/CHANGES.md CHANGED
@@ -1,10 +1,66 @@
1
1
  # Changes
2
2
 
3
+ ### Unreleased
4
+
5
+ ### 2025-05-23 (2.12.2)
6
+
7
+ * Fix compiler optimization level.
8
+
9
+ ### 2025-05-23 (2.12.1)
10
+
11
+ * Fix a potential crash in large negative floating point number generation.
12
+ * Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
13
+
14
+ ### 2025-05-12 (2.12.0)
15
+
16
+ * Improve floating point generation to not use scientific notation as much.
17
+ * Include line and column in parser errors. Both in the message and as exception attributes.
18
+ * Handle non-string hash keys with broken `to_s` implementations.
19
+ * `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
20
+
21
+ ### 2025-04-25 (2.11.3)
22
+
23
+ * Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
24
+
25
+ ### 2025-04-24 (2.11.2)
26
+
27
+ * Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
28
+ It now emits a deprecation warning.
29
+
30
+ ### 2025-04-24 (2.11.1)
31
+
32
+ * Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
33
+ These were deprecated 16 years ago, but never emited warnings, only undocumented, so are
34
+ still used by a few gems.
35
+
36
+ ### 2025-04-24 (2.11.0)
37
+
38
+ * Optimize Integer generation to be ~1.8x faster.
39
+ * Optimize Float generation to be ~10x faster.
40
+ * Fix `JSON.load` proc argument to substitute the parsed object with the return value.
41
+ This better match `Marshal.load` behavior.
42
+ * Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
43
+ * Deprecate `JSON.load_default_options`.
44
+ * Deprecate `JSON.unsafe_load_default_options`.
45
+ * Deprecate `JSON.dump_default_options`.
46
+ * Deprecate `Kernel#j`
47
+ * Deprecate `Kernel#jj`
48
+ * Remove outdated `JSON.iconv`.
49
+ * Remove `Class#json_creatable?` monkey patch.
50
+ * Remove deprecated `JSON.restore` method.
51
+ * Remove deprecated `JSON.unparse` method.
52
+ * Remove deprecated `JSON.fast_unparse` method.
53
+ * Remove deprecated `JSON.pretty_unparse` method.
54
+ * Remove deprecated `JSON::UnparserError` constant.
55
+ * Remove outdated `JSON::MissingUnicodeSupport` constant.
56
+
3
57
  ### 2025-03-12 (2.10.2)
4
58
 
5
59
  * Fix a potential crash in the C extension parser.
6
60
  * Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` unadvertently changed it.
7
61
  * Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
62
+ * Ensure parser error snippets are valid UTF-8.
63
+ * Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
8
64
 
9
65
  ### 2025-02-10 (2.10.1)
10
66
 
data/README.md CHANGED
@@ -233,6 +233,19 @@ the `pp` library's `pp` methods.
233
233
 
234
234
  ## Development
235
235
 
236
+ ### Prerequisites
237
+
238
+ 1. Clone the repository
239
+ 2. Install dependencies with `bundle install`
240
+
241
+ ### Testing
242
+
243
+ The full test suite can be run with:
244
+
245
+ ```bash
246
+ bundle exec rake test
247
+ ```
248
+
236
249
  ### Release
237
250
 
238
251
  Update the `lib/json/version.rb` file.
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "ruby.h"
5
5
  #include "ruby/encoding.h"
6
+ #include "../vendor/jeaiii-ltoa.h"
6
7
 
7
8
  /* shims */
8
9
  /* This is the fallback definition from Ruby 3.4 */
@@ -35,6 +36,12 @@ typedef unsigned char _Bool;
35
36
  # define MAYBE_UNUSED(x) x
36
37
  #endif
37
38
 
39
+ #ifdef RUBY_DEBUG
40
+ #ifndef JSON_DEBUG
41
+ #define JSON_DEBUG RUBY_DEBUG
42
+ #endif
43
+ #endif
44
+
38
45
  enum fbuffer_type {
39
46
  FBUFFER_HEAP_ALLOCATED = 0,
40
47
  FBUFFER_STACK_ALLOCATED = 1,
@@ -45,6 +52,9 @@ typedef struct FBufferStruct {
45
52
  unsigned long initial_length;
46
53
  unsigned long len;
47
54
  unsigned long capa;
55
+ #ifdef JSON_DEBUG
56
+ unsigned long requested;
57
+ #endif
48
58
  char *ptr;
49
59
  VALUE io;
50
60
  } FBuffer;
@@ -73,6 +83,20 @@ static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *
73
83
  fb->ptr = stack_buffer;
74
84
  fb->capa = stack_buffer_size;
75
85
  }
86
+ #ifdef JSON_DEBUG
87
+ fb->requested = 0;
88
+ #endif
89
+ }
90
+
91
+ static inline void fbuffer_consumed(FBuffer *fb, unsigned long consumed)
92
+ {
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;
76
100
  }
77
101
 
78
102
  static void fbuffer_free(FBuffer *fb)
@@ -136,6 +160,10 @@ static void fbuffer_do_inc_capa(FBuffer *fb, unsigned long requested)
136
160
 
137
161
  static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
138
162
  {
163
+ #ifdef JSON_DEBUG
164
+ fb->requested = requested;
165
+ #endif
166
+
139
167
  if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
140
168
  fbuffer_do_inc_capa(fb, requested);
141
169
  }
@@ -146,10 +174,24 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
146
174
  if (len > 0) {
147
175
  fbuffer_inc_capa(fb, len);
148
176
  MEMCPY(fb->ptr + fb->len, newstr, char, len);
149
- fb->len += len;
177
+ fbuffer_consumed(fb, len);
150
178
  }
151
179
  }
152
180
 
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
+
153
195
  static void fbuffer_append_str(FBuffer *fb, VALUE str)
154
196
  {
155
197
  const char *newstr = StringValuePtr(str);
@@ -164,28 +206,51 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
164
206
  {
165
207
  fbuffer_inc_capa(fb, 1);
166
208
  *(fb->ptr + fb->len) = newchr;
167
- fb->len++;
209
+ fbuffer_consumed(fb, 1);
168
210
  }
169
211
 
170
- static long fltoa(long number, char *buf)
212
+ static inline char *fbuffer_cursor(FBuffer *fb)
171
213
  {
172
- static const char digits[] = "0123456789";
173
- long sign = number;
174
- char* tmp = buf;
214
+ return fb->ptr + fb->len;
215
+ }
175
216
 
176
- if (sign < 0) number = -number;
177
- do *tmp-- = digits[number % 10]; while (number /= 10);
178
- if (sign < 0) *tmp-- = '-';
179
- return buf - tmp;
217
+ static inline void fbuffer_advance_to(FBuffer *fb, char *end)
218
+ {
219
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
180
220
  }
181
221
 
182
- #define LONG_BUFFER_SIZE 20
222
+ /*
223
+ * Appends the decimal string representation of \a number into the buffer.
224
+ */
183
225
  static void fbuffer_append_long(FBuffer *fb, long number)
184
226
  {
185
- char buf[LONG_BUFFER_SIZE];
186
- char *buffer_end = buf + LONG_BUFFER_SIZE;
187
- long len = fltoa(number, buffer_end - 1);
188
- fbuffer_append(fb, buffer_end - len, len);
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
+ }
251
+
252
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
253
+ fbuffer_advance_to(fb, end);
189
254
  }
190
255
 
191
256
  static VALUE fbuffer_finalize(FBuffer *fb)
@@ -6,5 +6,35 @@ if RUBY_ENGINE == 'truffleruby'
6
6
  else
7
7
  append_cflags("-std=c99")
8
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
+
9
39
  create_makefile 'json/ext/generator'
10
40
  end