summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/json-2.21.1
diff options
context:
space:
mode:
authorBen Sanders <ben@sanders.life>2026-08-02 09:49:25 -0400
committerBen Sanders <ben@sanders.life>2026-08-02 09:49:25 -0400
commitb41479c91e6685511c1f8ba8586106b322073b62 (patch)
treec9f1345999d754ae0a533849966427e792d9e68d /vendor/bundle/ruby/3.4.0/gems/json-2.21.1
parentcfaceeb9a6d1295f5754be211858155cd309acc2 (diff)
added statscounter code
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/json-2.21.1')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/BSDL22
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/CHANGES.md810
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/COPYING56
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/LEGAL20
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/README.md308
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/fbuffer/fbuffer.h259
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/Makefile273
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/extconf.rb19
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/generator.c2066
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/json.h183
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/Makefile273
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/extconf.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/parser.c2917
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/conf.rb24
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/simd.h208
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fast_float_parser.h814
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fpconv.c480
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/jeaiii-ltoa.h267
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/json.gemspec62
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json.rb693
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/bigdecimal.rb58
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/complex.rb51
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/core.rb13
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date_time.rb67
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/exception.rb49
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/ostruct.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/range.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/rational.rb49
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/regexp.rb48
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/set.rb48
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/string.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/struct.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/symbol.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/time.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/common.rb1182
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext.rb71
-rwxr-xr-xvendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator.bundlebin0 -> 98464 bytes
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator/state.rb104
-rwxr-xr-xvendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/parser.bundlebin0 -> 100048 bytes
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/generic_object.rb67
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/truffle_ruby/generator.rb790
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/version.rb5
43 files changed, 12746 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/BSDL b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/BSDL
new file mode 100644
index 0000000..66d9359
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/BSDL
@@ -0,0 +1,22 @@
+Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/CHANGES.md b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/CHANGES.md
new file mode 100644
index 0000000..b13642a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/CHANGES.md
@@ -0,0 +1,810 @@
+# Changes
+
+### Unreleased
+
+### 2026-07-13 (2.21.1)
+
+* Fix a compilation issue on Window and Microsoft Visual C++.
+
+### 2026-07-12 (2.21.0)
+
+* `JSON.generate` now accept a `sort_keys` option, which takes either a boolean or a block.
+* Added `#empty?` and `#partial_value?` methods on `JSON::ResumableParser`.
+* Numerous correctness and performance fixes for `JSON::ResumableParser`.
+* Avoid triggering Ruby's `float out of range` warning when parsing out of range numbers.
+* Declare C types with Ruby 4.1 `RUBY_TYPED_THREAD_SAFE_FREE`.
+
+### 2026-06-23 (2.20.0)
+
+* Both C and Java parsers are no longer recursive, so parsing very deep documents with `max_nesting: false` will no longer
+ result in `SystemStackError stack level too deep` errors.
+ * The `:max_nesting` option still defaults to `100`.
+* Optimized floating point number parsing further by replacing the ryu algorithm by a port of Eisel-Lemire Fast Float.
+* Added `JSON::ResumableParser` to parse streams of JSON documents. Not yet available on JRuby.
+* Deprecate default support of JavaScript comments in the parser and add `allow_comments: true` parsing option.
+* Integrate with Ruby 4.1 `ruby_sized_xfree`.
+
+### 2026-06-11 (2.19.9)
+
+* Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
+ with `JSON.generate(object, io)`. [CVE-2026-54696].
+
+### 2026-06-03 (2.19.8)
+
+* Fix 1-byte buffer overread on EOS errors.
+* Handle invalid types passed as `max_nesting` option.
+
+### 2026-05-28 (2.19.7)
+
+* Fix some more edge cases with out of range floats.
+* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
+* Add missing write barriers in `State#dup`.
+* Further validate generator `depth` config.
+
+### 2026-05-28 (2.19.6)
+
+* Cleanly handle overly large `depth` generator argument.
+* Add missing write barrier in `ParserConfig`.
+
+### 2026-05-04 (2.19.5)
+
+* Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
+
+### 2026-04-19 (2.19.4)
+
+* Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
+
+### 2026-03-25 (2.19.3)
+
+* Fix handling of unescaped control characters preceeded by a backslash.
+
+### 2026-03-18 (2.19.2)
+
+* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
+
+### 2026-03-08 (2.19.1)
+
+* Fix a compiler dependent GC bug introduced in `2.18.0`.
+
+### 2026-03-06 (2.19.0)
+
+* Fix `allow_blank` parsing option to no longer allow invalid types (e.g. `load([], allow_blank: true)` now raise a type error).
+* Add `allow_invalid_escape` parsing option to ignore backslashes that aren't followed by one of the valid escape characters.
+
+### 2026-02-03 (2.18.1)
+
+* Fix a potential crash in very specific circumstance if GC triggers during a call to `to_json`
+ without first invoking a user defined `#to_json` method.
+
+### 2025-12-11 (2.18.0)
+
+* Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
+
+### 2026-03-18 (2.17.1.2) - Security Backport
+
+* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
+
+### 2025-12-04 (2.17.1)
+
+* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.
+
+### 2025-12-03 (2.17.0)
+
+* Improve `JSON.load` and `JSON.unsafe_load` to allow passing options as second argument.
+* Fix the parser to no longer ignore invalid escapes in strings.
+ Only `\"`, `\\`, `\b`, `\f`, `\n`, `\r`, `\t` and `\u` are valid JSON escapes.
+* Fixed `JSON::Coder` to use the depth it was initialized with.
+* On TruffleRuby, fix the generator to not call `to_json` on the return value of `as_json` for `Float::NAN`.
+* Fixed handling of `state.depth`: when `to_json` changes `state.depth` but does not restore it, it is reset
+ automatically to its initial value.
+ In particular, when a `NestingError` is raised, `depth` is no longer equal to `max_nesting` after the call to
+ generate, and is reset to its initial value. Similarly when `to_json` raises an exception.
+
+### 2025-11-07 (2.16.0)
+
+* Deprecate `JSON::State#[]` and `JSON::State#[]=`. Consider using `JSON::Coder` instead.
+* `JSON::Coder` now also yields to the block when encountering strings with invalid encoding.
+* Fix GeneratorError messages to be UTF-8 encoded.
+* Fix memory leak when `Exception` is raised, or `throw` is used during JSON generation.
+* Optimized floating point number parsing by integrating the ryu algorithm (thanks to Josef Šimánek).
+* Optimized numbers parsing using SWAR (thanks to Scott Myron).
+* Optimized parsing of pretty printed documents using SWAR (thanks to Scott Myron).
+
+### 2026-03-18 (2.15.2.1) - Security Backport
+
+* Fix a format string injection vulnerability in `JSON.parse(doc, allow_duplicate_key: false)`. `CVE-2026-33210`.
+
+### 2025-10-25 (2.15.2)
+
+* Fix `JSON::Coder` to have one dedicated depth counter per invocation.
+ After encountering a circular reference in `JSON::Coder#dump`, any further `#dump` call would raise `JSON::NestingError`.
+
+### 2025-10-07 (2.15.1)
+
+* Fix incorrect escaping in the JRuby extension when encoding shared strings.
+
+### 2025-09-22 (2.15.0)
+
+* `JSON::Coder` callback now receive a second argument to convey whether the object is a hash key.
+* Tuned the floating point number generator to not use scientific notation as aggressively.
+
+### 2025-09-18 (2.14.1)
+
+* Fix `IndexOutOfBoundsException` in the JRuby extension when encoding shared strings.
+
+### 2025-09-18 (2.14.0)
+
+* Add new `allow_duplicate_key` generator options. By default a warning is now emitted when a duplicated key is encountered.
+ In `json 3.0` an error will be raised.
+ ```ruby
+ >> Warning[:deprecated] = true
+ >> puts JSON.generate({ foo: 1, "foo" => 2 })
+ (irb):2: warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
+ This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
+ {"foo":1,"foo":2}
+ >> JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
+ detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
+ ```
+* Fix `JSON.generate` `strict: true` mode to also restrict hash keys.
+* Fix `JSON::Coder` to also invoke block for hash keys that aren't strings nor symbols.
+* Fix `JSON.unsafe_load` usage with proc
+* Fix the parser to more consistently reject invalid UTF-16 surogate pairs.
+* Stop defining `String.json_create`, `String#to_json_raw`, `String#to_json_raw_object` when `json/add` isn't loaded.
+
+### 2025-07-28 (2.13.2)
+
+* Improve duplicate key warning and errors to include the key name and point to the right caller.
+
+### 2025-07-24 (2.13.1)
+
+* Fix support for older compilers without `__builtin_cpu_supports`.
+
+### 2025-07-17 (2.13.0)
+
+* Add new `allow_duplicate_key` parsing options. By default a warning is now emitted when a duplicated key is encountered.
+ In `json 3.0` an error will be raised.
+* Optimize parsing further using SIMD to scan strings.
+
+### 2025-05-23 (2.12.2)
+
+* Fix compiler optimization level.
+
+### 2025-05-23 (2.12.1)
+
+* Fix a potential crash in large negative floating point number generation.
+* Fix for JSON.pretty_generate to use passed state object's generate instead of state class as the required parameters aren't available.
+
+### 2025-05-12 (2.12.0)
+
+* Improve floating point generation to not use scientific notation as much.
+* Include line and column in parser errors. Both in the message and as exception attributes.
+* Handle non-string hash keys with broken `to_s` implementations.
+* `JSON.generate` now uses SSE2 (x86) or NEON (arm64) instructions when available to escape strings.
+
+### 2025-04-25 (2.11.3)
+
+* Fix a regression in `JSON.pretty_generate` that could cause indentation to be off once some `#to_json` has been called.
+
+### 2025-04-24 (2.11.2)
+
+* Add back `JSON::PRETTY_STATE_PROTOTYPE`. This constant was private API but is used by popular gems like `multi_json`.
+ It now emits a deprecation warning.
+
+### 2025-04-24 (2.11.1)
+
+* Add back `JSON.restore`, `JSON.unparse`, `JSON.fast_unparse` and `JSON.pretty_unparse`.
+ These were deprecated 16 years ago, but never emitted warnings, only undocumented, so are
+ still used by a few gems.
+
+### 2025-04-24 (2.11.0)
+
+* Optimize Integer generation to be ~1.8x faster.
+* Optimize Float generation to be ~10x faster.
+* Fix `JSON.load` proc argument to substitute the parsed object with the return value.
+ This better match `Marshal.load` behavior.
+* Deprecate `JSON.fast_generate` (it's not any faster, so pointless).
+* Deprecate `JSON.load_default_options`.
+* Deprecate `JSON.unsafe_load_default_options`.
+* Deprecate `JSON.dump_default_options`.
+* Deprecate `Kernel#j`
+* Deprecate `Kernel#jj`
+* Remove outdated `JSON.iconv`.
+* Remove `Class#json_creatable?` monkey patch.
+* Remove deprecated `JSON.restore` method.
+* Remove deprecated `JSON.unparse` method.
+* Remove deprecated `JSON.fast_unparse` method.
+* Remove deprecated `JSON.pretty_unparse` method.
+* Remove deprecated `JSON::UnparserError` constant.
+* Remove outdated `JSON::MissingUnicodeSupport` constant.
+
+### 2025-03-12 (2.10.2)
+
+* Fix a potential crash in the C extension parser.
+* Raise a ParserError on all incomplete unicode escape sequence. This was the behavior until `2.10.0` inadvertently changed it.
+* Ensure document snippets that are included in parser errors don't include truncated multibyte characters.
+* Ensure parser error snippets are valid UTF-8.
+* Fix `JSON::GeneratorError#detailed_message` on Ruby < 3.2
+
+### 2025-02-10 (2.10.1)
+
+* Fix a compatibility issue with `MultiJson.dump(obj, pretty: true)`: `no implicit conversion of false into Proc (TypeError)`.
+
+### 2025-02-10 (2.10.0)
+
+* `strict: true` now accept symbols as values. Previously they'd only be accepted as hash keys.
+* The C extension Parser has been entirely reimplemented from scratch.
+* Introduced `JSON::Coder` as a new API allowing to customize how non native types are serialized in a non-global way.
+* Introduced `JSON::Fragment` to allow assembling cached fragments in a safe way.
+* The Java implementation of the generator received many optimizations.
+
+### 2024-12-18 (2.9.1)
+
+* Fix support for Solaris 10.
+
+### 2024-12-03 (2.9.0)
+
+* Fix C implementation of `script_safe` escaping to not confuse some other 3 wide characters with `\u2028` and `\u2029`.
+ e.g. `JSON.generate(["倩", "瀨"], script_safe: true)` would generate the wrong JSON.
+* `JSON.dump(object, some_io)` now write into the IO in chunks while previously it would buffer the entire JSON before writing.
+* `JSON::GeneratorError` now has a `#invalid_object` attribute, making it easier to understand why an object tree cannot be serialized.
+* Numerous improvements to the JRuby extension.
+
+### 2024-11-14 (2.8.2)
+
+* `JSON.load_file` explicitly read the file as UTF-8.
+
+### 2024-11-06 (2.8.1)
+
+* Fix the java packages to include the extension.
+
+### 2024-11-06 (2.8.0)
+
+* Emit a deprecation warning when `JSON.load` create custom types without the `create_additions` option being explicitly enabled.
+ * Prefer to use `JSON.unsafe_load(string)` or `JSON.load(string, create_additions: true)`.
+* Emit a deprecation warning when serializing valid UTF-8 strings encoded in `ASCII_8BIT` aka `BINARY`.
+* Bump required Ruby version to 2.7.
+* Add support for optionally parsing trailing commas, via `allow_trailing_comma: true`, which in cunjunction with the
+ pre-existing support for comments, make it suitable to parse `jsonc` documents.
+* Many performance improvements to `JSON.parse` and `JSON.load`, up to `1.7x` faster on real world documents.
+* Some minor performance improvements to `JSON.dump` and `JSON.generate`.
+* `JSON.pretty_generate` no longer includes newlines inside empty object and arrays.
+
+### 2024-11-04 (2.7.6)
+
+* Fix a regression in JSON.generate when dealing with Hash keys that are string subclasses, call `to_json` on them.
+
+### 2024-10-25 (2.7.5)
+
+* Fix a memory leak when `#to_json` methods raise an exception.
+* Gracefully handle formatting configs being set to `nil` instead of `""`.
+* Workaround another issue caused by conflicting versions of both `json_pure` and `json` being loaded.
+
+### 2024-10-25 (2.7.4)
+
+* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
+ This bug would cause some gems with native extension to fail during compilation.
+* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
+* Make `json_pure` Ractor compatible.
+
+### 2024-10-24 (2.7.3)
+
+* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
+* Limit the size of ParserError exception messages, only include up to 32 bytes of the unparsable source.
+* Fix json-pure's `Object#to_json` to accept non-state arguments.
+* Fix multiline comment support in `json-pure`.
+* Fix `JSON.parse` to no longer mutate the argument encoding when passed an ASCII-8BIT string.
+* Fix `String#to_json` to raise on invalid encoding in `json-pure`.
+* Delete code that was based on CVTUTF.
+* Use the pure-Ruby generator on TruffleRuby.
+* Fix `strict` mode in `json-pure` to not break on Integer.
+
+### 2024-04-04 (2.7.2)
+
+* Use rb_sym2str instead of SYM2ID #561
+* Fix memory leak when exception is raised during JSON generation #574
+* Remove references to "19" methods in JRuby #576
+* Make OpenStruct support as optional by @hsbt in #565
+* Autoload JSON::GenericObject to avoid require ostruct warning in Ruby 3.4 #577
+* Warn to install ostruct if json couldn't load it by @hsbt #578
+
+### 2023-12-05 (2.7.1)
+
+* JSON.dump: handle unenclosed hashes regression #554
+* Overload kwargs in JSON.dump #556
+* [DOC] RDoc for additions #557
+* Fix JSON.dump overload combination #558
+
+### 2023-12-01 (2.7.0)
+
+* Add a strict option to Generator #519
+* `escape_slash` option was renamed as `script_safe` and now also escape U+2028 and U+2029. `escape_slash` is now an alias of `script_safe` #525
+* Remove unnecessary initialization of create_id in JSON.parse() #454
+* Improvements to Hash#to_json in pure implementation generator #203
+* Use ruby_xfree to free buffers #518
+* Fix "unexpected token" offset for Infinity #507
+* Avoid using deprecated BigDecimal.new on JRuby #546
+* Removed code for Ruby 1.8 #540
+* Rename JSON::ParseError to JSON:ParserError #530
+* Call super in included hook #486
+* JRuby requires a minimum of Java 8 #516
+* Always indent even if empty #517
+
+### 2022-11-30 (2.6.3)
+
+* bugfix json/pure mixing escaped with literal unicode raises Encoding::CompatibilityError #483
+* Stop including the parser source __LINE__ in exceptions #470
+
+### 2022-11-17 (2.6.2)
+
+* Remove unknown keyword arg from DateTime.parse #488
+* Ignore java artifacts by @hsbt #489
+* Fix parser bug for empty string allocation #496
+
+### 2021-10-24 (2.6.1)
+
+* Restore version.rb with 2.6.1
+
+### 2021-10-14 (2.6.0)
+
+* Use `rb_enc_interned_str` if available to reduce allocations in `freeze: true` mode. #451.
+* Bump required_ruby_version to 2.3.
+* Fix compatibility with `GC.compact`.
+* Fix some compilation warnings. #469
+
+## 2020-12-22 (2.5.1)
+
+* Restore the compatibility for constants of JSON class.
+
+## 2020-12-22 (2.5.0)
+
+* Ready to Ractor-safe at Ruby 3.0.
+
+## 2020-12-17 (2.4.1)
+
+* Restore version.rb with 2.4.1
+
+## 2020-12-15 (2.4.0)
+
+* Implement a freeze: parser option #447
+* Fix an issue with generate_pretty and empty objects in the Ruby and Java implementations #449
+* Fix JSON.load_file doc #448
+* Fix pure parser with unclosed arrays / objects #425
+* bundle the LICENSE file in the gem #444
+* Add an option to escape forward slash character #405
+* RDoc for JSON #439 #446 #442 #434 #433 #430
+
+## 2020-06-30 (2.3.1)
+
+* Spelling and grammar fixes for comments. Pull request #191 by Josh
+ Kline.
+* Enhance generic JSON and #generate docs. Pull request #347 by Victor
+ Shepelev.
+* Add :nodoc: for GeneratorMethods. Pull request #349 by Victor Shepelev.
+* Baseline changes to help (JRuby) development. Pull request #371 by Karol
+ Bucek.
+* Add metadata for rubygems.org. Pull request #379 by Alexandre ZANNI.
+* Remove invalid JSON.generate description from JSON module rdoc. Pull
+ request #384 by Jeremy Evans.
+* Test with TruffleRuby in CI. Pull request #402 by Benoit Daloze.
+* Rdoc enhancements. Pull request #413 by Burdette Lamar.
+* Fixtures/ are not being tested... Pull request #416 by Marc-André
+ Lafortune.
+* Use frozen string for hash key. Pull request #420 by Marc-André
+ Lafortune.
+* Added :call-seq: to RDoc for some methods. Pull request #422 by Burdette
+ Lamar.
+* Small typo fix. Pull request #423 by Marc-André Lafortune.
+
+## 2019-12-11 (2.3.0)
+ * Fix default of `create_additions` to always be `false` for `JSON(user_input)`
+ and `JSON.parse(user_input, nil)`.
+ Note that `JSON.load` remains with default `true` and is meant for internal
+ serialization of trusted data. [CVE-2020-10663]
+ * Fix passing args all #to_json in json/add/*.
+ * Fix encoding issues
+ * Fix issues of keyword vs positional parameter
+ * Fix JSON::Parser against bigdecimal updates
+ * Bug fixes to JRuby port
+
+## 2019-02-21 (2.2.0)
+ * Adds support for 2.6 BigDecimal and ruby standard library Set datetype.
+
+## 2017-04-18 (2.1.0)
+ * Allow passing of `decimal_class` option to specify a class as which to parse
+ JSON float numbers.
+## 2017-03-23 (2.0.4)
+ * Raise exception for incomplete unicode surrogates/character escape
+ sequences. This problem was reported by Daniel Gollahon (dgollahon).
+ * Fix arbitrary heap exposure problem. This problem was reported by Ahmad
+ Sherif (ahmadsherif).
+
+## 2017-01-12 (2.0.3)
+ * Set `required_ruby_version` to 1.9
+ * Some small fixes
+
+## 2016-07-26 (2.0.2)
+ * Specify `required_ruby_version` for json\_pure.
+ * Fix issue #295 failure when parsing frozen strings.
+
+## 2016-07-01 (2.0.1)
+ * Fix problem when requiring json\_pure and Parser constant was defined top
+ level.
+ * Add `RB_GC_GUARD` to avoid possible GC problem via Pete Johns.
+ * Store `current_nesting` on stack by Aaron Patterson.
+
+## 2015-09-11 (2.0.0)
+ * Now complies to newest JSON RFC 7159.
+ * Implements compatibility to ruby 2.4 integer unification.
+ * Removed support for `quirks_mode` option.
+ * Drops support for old rubies whose life has ended, that is rubies < 2.0.
+ Also see https://www.ruby-lang.org/en/news/2014/07/01/eol-for-1-8-7-and-1-9-2/
+ * There were still some mentions of dual GPL licensing in the source, but JSON
+ has just the Ruby license that itself includes an explicit dual-licensing
+ clause that allows covered software to be distributed under the terms of
+ the Simplified BSD License instead for all ruby versions >= 1.9.3. This is
+ however a GPL compatible license according to the Free Software Foundation.
+ I changed these mentions to be consistent with the Ruby license setting in
+ the gemspec files which were already correct now.
+
+## 2017-01-13 (1.8.6)
+ * Be compatible with ancient ruby 1.8 (maybe?)
+
+## 2015-09-11 (1.8.5)
+ * Be compatible with ruby 2.4.0
+ * There were still some mentions of dual GPL licensing in the source, but JSON
+ has just the Ruby license that itself includes an explicit dual-licensing
+ clause that allows covered software to be distributed under the terms of
+ the Simplified BSD License instead for all ruby versions >= 1.9.3. This is
+ however a GPL compatible license according to the Free Software Foundation.
+ I changed these mentions to be consistent with the Ruby license setting in
+ the gemspec files which were already correct now.
+
+## 2015-06-01 (1.8.3)
+ * Fix potential memory leak, thx to nobu.
+
+## 2015-01-08 (1.8.2)
+ * Some performance improvements by Vipul A M <vipulnsward@gmail.com>.
+ * Fix by Jason R. Clark <jclark@newrelic.com> to avoid mutation of
+ `JSON.dump_default_options`.
+ * More tests by Michael Mac-Vicar <mmacvicar@gmail.com> and fixing
+ `space_before` accessor in generator.
+ * Performance on Jruby improved by Ben Browning <bbrownin@redhat.com>.
+ * Some fixes to be compatible with the new Ruby 2.2 by Zachary Scott <e@zzak.io>
+ and SHIBATA Hiroshi <hsbt@ruby-lang.org>.
+
+## 2013-05-13 (1.8.1)
+ * Remove Rubinius exception since transcoding should be working now.
+
+## 2013-05-13 (1.8.0)
+ * Fix https://github.com/ruby/json/issues/162 reported by Marc-Andre
+ Lafortune <github_rocks@marc-andre.ca>. Thanks!
+ * Applied patches by Yui NARUSE <naruse@airemix.jp> to suppress warning with
+ -Wchar-subscripts and better validate UTF-8 strings.
+ * Applied patch by ginriki@github to remove unnecessary if.
+ * Add load/dump interface to `JSON::GenericObject` to make
+ serialize :some_attribute, `JSON::GenericObject`
+ work in Rails active models for convenient `SomeModel#some_attribute.foo.bar`
+ access to serialised JSON data.
+
+## 2013-02-04 (1.7.7)
+ * Security fix for JSON create_additions default value and
+ `JSON::GenericObject`. It should not be possible to create additions unless
+ explicitly requested by setting the create_additions argument to true or
+ using the JSON.load/dump interface. If `JSON::GenericObject` is supposed to
+ be automatically deserialised, this has to be explicitly enabled by
+ setting
+ JSON::GenericObject.json_creatable = true
+ as well.
+ * Remove useless assert in fbuffer implementation.
+ * Apply patch attached to https://github.com/ruby/json/issues#issue/155
+ provided by John Shahid <jvshahid@gmail.com>, Thx!
+ * Add license information to rubygems spec data, reported by Jordi Massaguer Pla <jmassaguerpla@suse.de>.
+ * Improve documentation, thx to Zachary Scott <zachary@zacharyscott.net>.
+
+## 2012-11-29 (1.7.6)
+ * Add `GeneratorState#merge` alias for JRuby, fix state accessor methods. Thx to
+ jvshahid@github.
+ * Increase hash likeness of state objects.
+
+## 2012-08-17 (1.7.5)
+ * Fix compilation of extension on older rubies.
+
+## 2012-07-26 (1.7.4)
+ * Fix compilation problem on AIX, see https://github.com/ruby/json/issues/142
+
+## 2012-05-12 (1.7.3)
+ * Work around Rubinius encoding issues using iconv for conversion instead.
+
+## 2012-05-11 (1.7.2)
+ * Fix some encoding issues, that cause problems for the pure and the
+ extension variant in jruby 1.9 mode.
+
+## 2012-04-28 (1.7.1)
+ * Some small fixes for building
+
+## 2012-04-28 (1.7.0)
+ * Add `JSON::GenericObject` for method access to objects transmitted via JSON.
+
+## 2012-04-27 (1.6.7)
+ * Fix possible crash when trying to parse nil value.
+
+## 2012-02-11 (1.6.6)
+ * Propagate src encoding to values made from it (fixes 1.9 mode converting
+ everything to ascii-8bit; harmless for 1.8 mode too) (Thomas E. Enebo
+ <tom.enebo@gmail.com>), should fix
+ https://github.com/ruby/json/issues#issue/119.
+ * Fix https://github.com/ruby/json/issues#issue/124 Thx to Jason Hutchens.
+ * Fix https://github.com/ruby/json/issues#issue/117
+
+## 2012-01-15 (1.6.5)
+ * Vit Ondruch <v.ondruch@tiscali.cz> reported a bug that shows up when using
+ optimisation under GCC 4.7. Thx to him, Bohuslav Kabrda
+ <bkabrda@redhat.com> and Yui NARUSE <naruse@airemix.jp> for debugging and
+ developing a patch fix.
+
+## 2011-12-24 (1.6.4)
+ * Patches that improve speed on JRuby contributed by Charles Oliver Nutter
+ <headius@headius.com>.
+ * Support `object_class`/`array_class` with duck typed hash/array.
+
+## 2011-12-01 (1.6.3)
+ * Let `JSON.load('')` return nil as well to make mysql text columns (default to
+ `''`) work better for serialization.
+
+## 2011-11-21 (1.6.2)
+ * Add support for OpenStruct and BigDecimal.
+ * Fix bug when parsing nil in `quirks_mode`.
+ * Make JSON.dump and JSON.load methods better cooperate with Rails' serialize
+ method. Just use:
+ serialize :value, JSON
+ * Fix bug with time serialization concerning nanoseconds. Thanks for the
+ patch go to Josh Partlow (jpartlow@github).
+ * Improve parsing speed for JSON numbers (integers and floats) in a similar way to
+ what Evan Phoenix <evan@phx.io> suggested in:
+ https://github.com/ruby/json/pull/103
+
+## 2011-09-18 (1.6.1)
+ * Using -target 1.5 to force Java bits to compile with 1.5.
+
+## 2011-09-12 (1.6.0)
+ * Extract utilities (prettifier and GUI-editor) in its own gem json-utils.
+ * Split json/add/core into different files for classes to be serialised.
+
+## 2011-08-31 (1.5.4)
+ * Fix memory leak when used from multiple JRuby. (Patch by
+ jfirebaugh@github).
+ * Apply patch by Eric Wong <nocode@yhbt.net> that fixes garbage collection problem
+ reported in https://github.com/ruby/json/issues/46.
+ * Add :quirks_mode option to parser and generator.
+ * Add support for Rational and Complex number additions via json/add/complex
+ and json/add/rational requires.
+
+## 2011-06-20 (1.5.3)
+ * Alias State#configure method as State#merge to increase duck type synonymy with Hash.
+ * Add `as_json` methods in json/add/core, so rails can create its json objects the new way.
+
+## 2011-05-11 (1.5.2)
+ * Apply documentation patch by Cory Monty <cory.monty@gmail.com>.
+ * Add gemspecs for json and json\_pure.
+ * Fix bug in jruby pretty printing.
+ * Fix bug in `object_class` and `array_class` when inheriting from Hash or
+ Array.
+
+## 2011-01-24 (1.5.1)
+ * Made rake-compiler build a fat binary gem. This should fix issue
+ https://github.com/ruby/json/issues#issue/54.
+
+## 2011-01-22 (1.5.0)
+ * Included Java source codes for the Jruby extension made by Daniel Luz
+ <dev@mernen.com>.
+ * Output full exception message of `deep_const_get` to aid debugging.
+ * Fixed an issue with ruby 1.9 `Module#const_defined?` method, that was
+ reported by Riley Goodside.
+
+## 2010-08-09 (1.4.6)
+ * Fixed oversight reported in http://github.com/ruby/json/issues/closed#issue/23,
+ always create a new object from the state prototype.
+ * Made pure and ext api more similar again.
+
+## 2010-08-07 (1.4.5)
+ * Manage data structure nesting depth in state object during generation. This
+ should reduce problems with `to_json` method definіtions that only have one
+ argument.
+ * Some fixes in the state objects and additional tests.
+## 2010-08-06 (1.4.4)
+ * Fixes build problem for rubinius under OS X, http://github.com/ruby/json/issues/closed#issue/25
+ * Fixes crashes described in http://github.com/ruby/json/issues/closed#issue/21 and
+ http://github.com/ruby/json/issues/closed#issue/23
+## 2010-05-05 (1.4.3)
+ * Fixed some test assertions, from Ruby r27587 and r27590, patch by nobu.
+ * Fixed issue http://github.com/ruby/json/issues/#issue/20 reported by
+ electronicwhisper@github. Thx!
+
+## 2010-04-26 (1.4.2)
+ * Applied patch from naruse Yui NARUSE <naruse@airemix.com> to make building with
+ Microsoft Visual C possible again.
+ * Applied patch from devrandom <c1.github@niftybox.net> in order to allow building of
+ json_pure if extensiontask is not present.
+ * Thanks to Dustin Schneider <dustin@stocktwits.com>, who reported a memory
+ leak, which is fixed in this release.
+ * Applied 993f261ccb8f911d2ae57e9db48ec7acd0187283 patch from josh@github.
+
+## 2010-04-25 (1.4.1)
+ * Fix for a bug reported by Dan DeLeo <dan@kallistec.com>, caused by T_FIXNUM
+ being different on 32bit/64bit architectures.
+
+## 2010-04-23 (1.4.0)
+ * Major speed improvements and building with simplified
+ directory/file-structure.
+ * Extension should at least be compatible with MRI, YARV and Rubinius.
+
+## 2010-04-07 (1.2.4)
+ * Trigger const_missing callback to make Rails' dynamic class loading work.
+
+## 2010-03-11 (1.2.3)
+ * Added a `State#[]` method which returns an attribute's value in order to
+ increase duck type compatibility to Hash.
+
+## 2010-02-27 (1.2.2)
+ * Made some changes to make the building of the parser/generator compatible
+ to Rubinius.
+
+## 2009-11-25 (1.2.1)
+ * Added `:symbolize_names` option to Parser, which returns symbols instead of
+ strings in object names/keys.
+
+## 2009-10-01 (1.2.0)
+ * `fast_generate` now raises an exception for nan and infinite floats.
+ * On Ruby 1.8 json supports parsing of UTF-8, UTF-16BE, UTF-16LE, UTF-32BE,
+ and UTF-32LE JSON documents now. Under Ruby 1.9 the M17n conversion
+ functions are used to convert from all supported encodings. ASCII-8BIT
+ encoded strings are handled like all strings under Ruby 1.8 were.
+ * Better documentation
+
+## 2009-08-23 (1.1.9)
+ * Added forgotten main doc file `extra_rdoc_files`.
+
+## 2009-08-23 (1.1.8)
+ * Applied a patch by OZAWA Sakuro <sakuro@2238club.org> to make json/pure
+ work in environments that don't provide iconv.
+ * Applied patch by okkez_ in order to fix Ruby Bug #1768:
+ http://redmine.ruby-lang.org/issues/show/1768.
+ * Finally got around to avoid the rather paranoid escaping of ?/ characters
+ in the generator's output. The parsers aren't affected by this change.
+ Thanks to Rich Apodaca <rapodaca@metamolecular.com> for the suggestion.
+
+## 2009-06-29 (1.1.7)
+ * Security Fix for JSON::Pure::Parser. A specially designed string could
+ cause catastrophic backtracking in one of the parser's regular expressions
+ in earlier 1.1.x versions. JSON::Ext::Parser isn't affected by this issue.
+ Thanks to Bartosz Blimke <bartosz@new-bamboo.co.uk> for reporting this
+ problem.
+ * This release also uses a less strict ruby version requirement for the
+ creation of the mswin32 native gem.
+
+## 2009-05-10 (1.1.6)
+ * No changes. І tested native linux gems in the last release and they don't
+ play well with different ruby versions other than the one the gem was built
+ with. This release is just to bump the version number in order to skip the
+ native gem on rubyforge.
+
+## 2009-05-10 (1.1.5)
+ * Started to build gems with rake-compiler gem.
+ * Applied patch object/array class patch from Brian Candler
+ <B.Candler@pobox.com> and fixes.
+
+## 2009-04-01 (1.1.4)
+ * Fixed a bug in the creation of serialized generic rails objects reported by
+ Friedrich Graeter <graeter@hydrixos.org>.
+ * Deleted tests/runner.rb, we're using testrb instead.
+ * Editor supports Infinity in numbers now.
+ * Made some changes in order to get the library to compile/run under Ruby
+ 1.9.
+ * Improved speed of the code path for the fast_generate method in the pure
+ variant.
+
+## 2008-07-10 (1.1.3)
+ * Wesley Beary <monki@geemus.com> reported a bug in json/add/core's DateTime
+ handling: If the nominator and denominator of the offset were divisible by
+ each other Ruby's Rational#to_s returns them as an integer not a fraction
+ with '/'. This caused a ZeroDivisionError during parsing.
+ * Use Date#start and DateTime#start instead of sg method, while
+ remaining backwards compatible.
+ * Supports ragel >= 6.0 now.
+ * Corrected some tests.
+ * Some minor changes.
+
+## 2007-11-27 (1.1.2)
+ * Remember default dir (last used directory) in editor.
+ * JSON::Editor.edit method added, the editor can now receive json texts from
+ the clipboard via C-v.
+ * Load json texts from an URL pasted via middle button press.
+ * Added :create_additions option to Parser. This makes it possible to disable
+ the creation of additions by force, in order to treat json texts as data
+ while having additions loaded.
+ * Jacob Maine <jmaine@blurb.com> reported, that JSON(:foo) outputs a JSON
+ object if the rails addition is enabled, which is wrong. It now outputs a
+ JSON string "foo" instead, like suggested by Jacob Maine.
+ * Discovered a bug in the Ruby Bugs Tracker on rubyforge, that was reported
+ by John Evans lgastako@gmail.com. He could produce a crash in the JSON
+ generator by returning something other than a String instance from a
+ to_json method. I now guard against this by doing a rather crude type
+ check, which raises an exception instead of crashing.
+
+## 2007-07-06 (1.1.1)
+ * Yui NARUSE <naruse@airemix.com> sent some patches to fix tests for Ruby
+ 1.9. I applied them and adapted some of them a bit to run both on 1.8 and
+ 1.9.
+ * Introduced a `JSON.parse!` method without depth checking for people who
+ like danger.
+ * Made generate and `pretty_generate` methods configurable by an options hash.
+ * Added :allow_nan option to parser and generator in order to handle NaN,
+ Infinity, and -Infinity correctly - if requested. Floats, which aren't numbers,
+ aren't valid JSON according to RFC4627, so by default an exception will be
+ raised if any of these symbols are encountered. Thanks to Andrea Censi
+ <andrea.censi@dis.uniroma1.it> for his hint about this.
+ * Fixed some more tests for Ruby 1.9.
+ * Implemented dump/load interface of Marshal as suggested in ruby-core:11405
+ by murphy <murphy@rubychan.de>.
+ * Implemented the `max_nesting` feature for generate methods, too.
+ * Added some implementations for ruby core's custom objects for
+ serialisation/deserialisation purposes.
+
+## 2007-05-21 (1.1.0)
+ * Implemented max_nesting feature for parser to avoid stack overflows for
+ data from untrusted sources. If you trust the source, you can disable it
+ with the option max_nesting => false.
+ * Piers Cawley <pdcawley@bofh.org.uk> reported a bug, that not every
+ character can be escaped by `\` as required by RFC4627. There's a
+ contradiction between David Crockford's JSON checker test vectors (in
+ tests/fixtures) and RFC4627, though. I decided to stick to the RFC, because
+ the JSON checker seems to be a bit older than the RFC.
+ * Extended license to Ruby License, which includes the GPL.
+ * Added keyboard shortcuts, and 'Open location' menu item to edit_json.rb.
+
+## 2007-05-09 (1.0.4)
+ * Applied a patch from Yui NARUSE <naruse@airemix.com> to make JSON compile
+ under Ruby 1.9. Thank you very much for mailing it to me!
+ * Made binary variants of JSON fail early, instead of falling back to the
+ pure version. This should avoid overshadowing of eventual problems while
+ loading of the binary.
+
+## 2007-03-24 (1.0.3)
+ * Improved performance of pure variant a bit.
+ * The ext variant of this release supports the mswin32 platform. Ugh!
+
+## 2007-03-24 (1.0.2)
+ * Ext Parser didn't parse 0e0 correctly into 0.0: Fixed!
+
+## 2007-03-24 (1.0.1)
+ * Forgot some object files in the build dir. I really like that - not!
+
+## 2007-03-24 (1.0.0)
+ * Added C implementations for the JSON generator and a ragel based JSON
+ parser in C.
+ * Much more tests, especially fixtures from json.org.
+ * Further improved conformance to RFC4627.
+
+## 2007-02-09 (0.4.3)
+ * Conform more to RFC4627 for JSON: This means JSON strings
+ now always must contain exactly one object `"{ ... }"` or array `"[ ... ]"` in
+ order to be parsed without raising an exception. The definition of what
+ constitutes a whitespace is narrower in JSON than in Ruby ([ \t\r\n]), and
+ there are differences in floats and integers (no octals or hexadecimals) as
+ well.
+ * Added aliases generate and `pretty_generate` of unparse and `pretty_unparse`.
+ * Fixed a test case.
+ * Catch an `Iconv::InvalidEncoding` exception, that seems to occur on some Sun
+ boxes with SunOS 5.8, if iconv doesn't support utf16 conversions. This was
+ reported by Andrew R Jackson <andrewj@bcm.tmc.edu>, thanks a bunch!
+
+## 2006-08-25 (0.4.2)
+ * Fixed a bug in handling solidi (/-characters), that was reported by
+ Kevin Gilpin <kevin.gilpin@alum.mit.edu>.
+
+## 2006-02-06 (0.4.1)
+ * Fixed a bug related to escaping with backslashes. Thanks for the report go
+ to Florian Munz <surf@theflow.de>.
+
+## 2005-09-23 (0.4.0)
+ * Initial Rubyforge Version
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/COPYING b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/COPYING
new file mode 100644
index 0000000..426810a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/COPYING
@@ -0,0 +1,56 @@
+Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
+You can redistribute it and/or modify it under either the terms of the
+2-clause BSDL (see the file BSDL), or the conditions below:
+
+ 1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
+
+ 2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b) use the modified software only within your corporation or
+ organization.
+
+ c) give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d) make other distribution arrangements with the author.
+
+ 3. You may distribute the software in object code or binary form,
+ provided that you do at least ONE of the following:
+
+ a) distribute the binaries and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
+
+ b) accompany the distribution with the machine-readable source of
+ the software.
+
+ c) give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d) make other distribution arrangements with the author.
+
+ 4. You may modify and include the part of the software into any other
+ software (possibly commercial). But some files in the distribution
+ are not written by the author, so that they are not under these terms.
+
+ For the list of those files and their copying conditions, see the
+ file LEGAL.
+
+ 5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/LEGAL b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/LEGAL
new file mode 100644
index 0000000..3d3e093
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/LEGAL
@@ -0,0 +1,20 @@
+# -*- rdoc -*-
+
+= LEGAL NOTICE INFORMATION
+--------------------------
+
+All the files in this distribution are covered under either the Ruby's
+license (see the file COPYING) or public-domain except some files
+mentioned below.
+
+ext/json/ext/vendor/fpconv.h::
+ This file is adapted from https://github.com/night-shift/fpconv
+ It is licensed under Boost Software License 1.0.
+
+ext/json/ext/vendor/jeaiii-ltoa.h::
+ This file is adapted from https://github.com/jeaiii/itoa
+ It is licensed under the MIT License
+
+ext/json/ext/vendor/fast_float_parser.h::
+ This file is adapted from the Fast Float C++ library by The fast_float authors https://github.com/fastfloat/fast_float
+ It is licensed under the MIT License
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/README.md b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/README.md
new file mode 100644
index 0000000..5e6ffe4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/README.md
@@ -0,0 +1,308 @@
+# JSON implementation for Ruby
+
+[![CI](https://github.com/ruby/json/actions/workflows/ci.yml/badge.svg)](https://github.com/ruby/json/actions/workflows/ci.yml)
+
+## Description
+
+This is an implementation of the JSON specification according to RFC 7159
+http://www.ietf.org/rfc/rfc7159.txt .
+
+The JSON generator generate UTF-8 character sequences by default.
+If an :ascii\_only option with a true value is given, they escape all
+non-ASCII and control characters with \uXXXX escape sequences, and support
+UTF-16 surrogate pairs in order to be able to generate the whole range of
+unicode code points.
+
+All strings, that are to be encoded as JSON strings, should be UTF-8 byte
+sequences on the Ruby side. To encode raw binary strings, that aren't UTF-8
+encoded, please use the to\_json\_raw\_object method of String (which produces
+an object, that contains a byte array) and decode the result on the receiving
+endpoint.
+
+## Installation
+
+Install the gem and add to the application's Gemfile by executing:
+
+ $ bundle add json
+
+If bundler is not being used to manage dependencies, install the gem by executing:
+
+ $ gem install json
+
+## Basic Usage
+
+To use JSON you can
+
+```ruby
+require 'json'
+```
+
+Now you can parse a JSON document into a ruby data structure by calling
+
+```ruby
+JSON.parse(document)
+```
+
+If you want to generate a JSON document from a ruby data structure call
+```ruby
+JSON.generate(data)
+```
+
+You can also use the `pretty_generate` method (which formats the output more
+verbosely and nicely) or `fast_generate` (which doesn't do any of the security
+checks generate performs, e. g. nesting deepness checks).
+
+## Casting non native types
+
+JSON documents can only support Hashes, Arrays, Strings, Integers and Floats.
+
+By default if you attempt to serialize something else, `JSON.generate` will
+search for a `#to_json` method on that object:
+
+```ruby
+Position = Struct.new(:latitude, :longitude) do
+ def to_json(state = nil, *)
+ JSON::State.from_state(state).generate({
+ latitude: latitude,
+ longitude: longitude,
+ })
+ end
+end
+
+JSON.generate([
+ Position.new(12323.234, 435345.233),
+ Position.new(23434.676, 159435.324),
+]) # => [{"latitude":12323.234,"longitude":435345.233},{"latitude":23434.676,"longitude":159435.324}]
+```
+
+If a `#to_json` method isn't defined on the object, `JSON.generate` will fallback to call `#to_s`:
+
+```ruby
+JSON.generate(Object.new) # => "#<Object:0x000000011e768b98>"
+```
+
+Both of these behavior can be disabled using the `strict: true` option:
+
+```ruby
+JSON.generate(Object.new, strict: true) # => Object not allowed in JSON (JSON::GeneratorError)
+JSON.generate(Position.new(1, 2), strict: true) # => Position not allowed in JSON (JSON::GeneratorError)
+```
+
+## JSON::Coder
+
+Since `#to_json` methods are global, it can sometimes be problematic if you need a given type to be
+serialized in different ways in different locations.
+
+Instead it is recommended to use the newer `JSON::Coder` API:
+
+```ruby
+module MyApp
+ API_JSON_CODER = JSON::Coder.new do |object, is_object_key|
+ case object
+ when Time
+ object.iso8601(3)
+ else
+ object
+ end
+ end
+end
+
+puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
+```
+
+The provided block is called for all objects that don't have a native JSON equivalent, and
+must return a Ruby object that has a native JSON equivalent.
+
+It is also called for objects that do have a JSON equivalent, but are used as Hash keys, for instance `{ 1 => 2}`,
+as well as for strings that aren't valid UTF-8:
+
+```ruby
+coder = JSON::Coder.new do |object, is_object_key|
+ case object
+ when String
+ if !object.valid_encoding? || object.encoding != Encoding::UTF_8
+ Base64.encode64(object)
+ else
+ object
+ end
+ else
+ object
+ end
+end
+```
+
+## Combining JSON fragments
+
+To combine JSON fragments into a bigger JSON document, you can use `JSON::Fragment`:
+
+```ruby
+posts_json = cache.fetch_multi(post_ids) do |post_id|
+ JSON.generate(Post.find(post_id))
+end
+posts_json.map! { |post_json| JSON::Fragment.new(post_json) }
+JSON.generate({ posts: posts_json, count: posts_json.count })
+```
+
+## Round-tripping arbitrary types
+
+> [!CAUTION]
+> You should never use `JSON.unsafe_load` nor `JSON.parse(str, create_additions: true)` to parse untrusted user input,
+> as it can lead to remote code execution vulnerabilities.
+
+To create a JSON document from a ruby data structure, you can call
+`JSON.generate` like that:
+
+```ruby
+json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+# => "[1,2,{\"a\":3.141},false,true,null,\"4..10\"]"
+```
+
+To get back a ruby data structure from a JSON document, you have to call
+JSON.parse on it:
+
+```ruby
+JSON.parse json
+# => [1, 2, {"a"=>3.141}, false, true, nil, "4..10"]
+```
+
+Note, that the range from the original data structure is a simple
+string now. The reason for this is, that JSON doesn't support ranges
+or arbitrary classes. In this case the json library falls back to call
+`Object#to_json`, which is the same as `#to_s.to_json`.
+
+It's possible to add JSON support serialization to arbitrary classes by
+simply implementing a more specialized version of the `#to_json method`, that
+should return a JSON object (a hash converted to JSON with `#to_json`) like
+this (don't forget the `*a` for all the arguments):
+
+```ruby
+class Range
+ def to_json(*a)
+ {
+ 'json_class' => self.class.name, # = 'Range'
+ 'data' => [ first, last, exclude_end? ]
+ }.to_json(*a)
+ end
+end
+```
+
+The hash key `json_class` is the class, that will be asked to deserialise the
+JSON representation later. In this case it's `Range`, but any namespace of
+the form `A::B` or `::A::B` will do. All other keys are arbitrary and can be
+used to store the necessary data to configure the object to be deserialised.
+
+If the key `json_class` is found in a JSON object, the JSON parser checks
+if the given class responds to the `json_create` class method. If so, it is
+called with the JSON object converted to a Ruby hash. So a range can
+be deserialised by implementing `Range.json_create` like this:
+
+```ruby
+class Range
+ def self.json_create(o)
+ new(*o['data'])
+ end
+end
+```
+
+Now it possible to serialise/deserialise ranges as well:
+
+```ruby
+json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
+JSON.parse json
+# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+json = JSON.generate [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+# => "[1,2,{\"a\":3.141},false,true,null,{\"json_class\":\"Range\",\"data\":[4,10,false]}]"
+JSON.unsafe_load json
+# => [1, 2, {"a"=>3.141}, false, true, nil, 4..10]
+```
+
+`JSON.generate` always creates the shortest possible string representation of a
+ruby data structure in one line. This is good for data storage or network
+protocols, but not so good for humans to read. Fortunately there's also
+`JSON.pretty_generate` (or `JSON.pretty_generate`) that creates a more readable
+output:
+
+```ruby
+ puts JSON.pretty_generate([1, 2, {"a"=>3.141}, false, true, nil, 4..10])
+ [
+ 1,
+ 2,
+ {
+ "a": 3.141
+ },
+ false,
+ true,
+ null,
+ {
+ "json_class": "Range",
+ "data": [
+ 4,
+ 10,
+ false
+ ]
+ }
+ ]
+```
+
+There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
+`pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
+the `pp` library's `pp` methods.
+
+## Security
+
+When parsing or serializing untrusted input, parser and generator options should never be user controlled.
+
+```ruby
+# Dangerous, DO NOT DO THIS.
+JSON.generate(params[:data], params[:options])
+```
+
+Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
+
+## Development
+
+### Prerequisites
+
+1. Clone the repository
+2. Install dependencies with `bundle install`
+
+### Testing
+
+The full test suite can be run with:
+
+```bash
+bundle exec rake test
+```
+
+### Release
+
+Update the `lib/json/version.rb` file.
+
+```
+rbenv shell 2.6.5
+rake build
+gem push pkg/json-2.3.0.gem
+
+rbenv shell jruby-9.2.9.0
+rake build
+gem push pkg/json-2.3.0-java.gem
+```
+
+## Author
+
+Florian Frank <mailto:flori@ping.de>
+
+## License
+
+Ruby License, see https://www.ruby-lang.org/en/about/license.txt.
+
+## Download
+
+The latest version of this library can be downloaded at
+
+* https://rubygems.org/gems/json
+
+Online Documentation should be located at
+
+* https://www.rubydoc.info/gems/json
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/fbuffer/fbuffer.h b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/fbuffer/fbuffer.h
new file mode 100644
index 0000000..cc44ea0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/fbuffer/fbuffer.h
@@ -0,0 +1,259 @@
+#ifndef _FBUFFER_H_
+#define _FBUFFER_H_
+
+#include "../json.h"
+#include "../vendor/jeaiii-ltoa.h"
+
+enum fbuffer_type {
+ FBUFFER_HEAP_ALLOCATED = 0,
+ FBUFFER_STACK_ALLOCATED = 1,
+};
+
+typedef struct FBufferStruct {
+ enum fbuffer_type type;
+ size_t initial_length;
+ size_t len;
+ size_t capa;
+#if JSON_DEBUG
+ size_t requested;
+#endif
+ char *ptr;
+ VALUE io;
+} FBuffer;
+
+#define FBUFFER_STACK_SIZE 512
+#define FBUFFER_IO_BUFFER_SIZE (16384 - 1)
+#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
+
+#define FBUFFER_PTR(fb) ((fb)->ptr)
+#define FBUFFER_LEN(fb) ((fb)->len)
+#define FBUFFER_CAPA(fb) ((fb)->capa)
+#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
+
+static void fbuffer_free(FBuffer *fb);
+static void fbuffer_clear(FBuffer *fb);
+static void fbuffer_append(FBuffer *fb, const char *newstr, size_t len);
+static void fbuffer_append_long(FBuffer *fb, long number);
+static inline void fbuffer_append_char(FBuffer *fb, char newchr);
+static VALUE fbuffer_finalize(FBuffer *fb);
+
+static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
+{
+ if (RTEST(io)) {
+ JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
+ fb->io = io;
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
+ } else {
+ fb->type = FBUFFER_STACK_ALLOCATED;
+ fb->ptr = stack_buffer;
+ fb->capa = stack_buffer_size;
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
+ }
+#if JSON_DEBUG
+ fb->requested = 0;
+#endif
+}
+
+static inline void fbuffer_consumed(FBuffer *fb, size_t consumed)
+{
+#if JSON_DEBUG
+ if (consumed > fb->requested) {
+ rb_bug("fbuffer: Out of bound write");
+ }
+ fb->requested = 0;
+#endif
+ fb->len += consumed;
+}
+
+static void fbuffer_free(FBuffer *fb)
+{
+ if (fb->ptr && fb->type == FBUFFER_HEAP_ALLOCATED) {
+ JSON_SIZED_FREE_N(fb->ptr, fb->capa);
+ }
+}
+
+static void fbuffer_clear(FBuffer *fb)
+{
+ fb->len = 0;
+}
+
+static void fbuffer_flush(FBuffer *fb)
+{
+ rb_io_write(fb->io, rb_utf8_str_new(fb->ptr, fb->len));
+ fbuffer_clear(fb);
+}
+
+static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
+{
+ if (new_capa > fb->capa) {
+ if (fb->type == FBUFFER_STACK_ALLOCATED) {
+ const char *old_buffer = fb->ptr;
+ fb->ptr = ALLOC_N(char, new_capa);
+ fb->type = FBUFFER_HEAP_ALLOCATED;
+ MEMCPY(fb->ptr, old_buffer, char, fb->len);
+ } else {
+ JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa);
+ }
+ fb->capa = new_capa;
+ }
+}
+
+static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
+{
+ if (RB_UNLIKELY(fb->io)) {
+ if (fb->capa != 0) {
+ fbuffer_flush(fb);
+ if (RB_LIKELY(requested < fb->capa)) {
+ return;
+ }
+ }
+ }
+
+ size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
+ size_t needed_capa = requested + fb->len;
+
+ while (new_capa < needed_capa) {
+ new_capa *= 2;
+ }
+
+ fbuffer_realloc(fb, new_capa);
+}
+
+static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
+{
+#if JSON_DEBUG
+ fb->requested = requested;
+#endif
+
+ if (RB_UNLIKELY(requested > fb->capa - fb->len)) {
+ fbuffer_do_inc_capa(fb, requested);
+ }
+}
+
+static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
+{
+ size_t result = a * b;
+ if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
+ rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
+ }
+ return result;
+}
+
+static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
+{
+ MEMCPY(fb->ptr + fb->len, newstr, char, len);
+ fbuffer_consumed(fb, len);
+}
+
+static inline void fbuffer_append(FBuffer *fb, const char *newstr, size_t len)
+{
+ if (len > 0) {
+ fbuffer_inc_capa(fb, len);
+ fbuffer_append_reserved(fb, newstr, len);
+ }
+}
+
+/* Appends a character into a buffer. The buffer needs to have sufficient capacity, via fbuffer_inc_capa(...). */
+static inline void fbuffer_append_reserved_char(FBuffer *fb, char chr)
+{
+#if JSON_DEBUG
+ if (fb->requested < 1) {
+ rb_bug("fbuffer: unreserved write");
+ }
+ fb->requested--;
+#endif
+
+ fb->ptr[fb->len] = chr;
+ fb->len++;
+}
+
+static void fbuffer_append_str(FBuffer *fb, VALUE str)
+{
+ const char *ptr;
+ size_t len;
+ RSTRING_GETMEM(str, ptr, len);
+
+ fbuffer_append(fb, ptr, len);
+ RB_GC_GUARD(str);
+}
+
+static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
+{
+ const char *ptr;
+ size_t len;
+ RSTRING_GETMEM(str, ptr, len);
+
+ fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
+ while (repeat) {
+#if JSON_DEBUG
+ fb->requested = len;
+#endif
+ fbuffer_append_reserved(fb, ptr, len);
+ repeat--;
+ }
+ RB_GC_GUARD(str);
+}
+
+static inline void fbuffer_append_char(FBuffer *fb, char newchr)
+{
+ fbuffer_inc_capa(fb, 1);
+ *(fb->ptr + fb->len) = newchr;
+ fbuffer_consumed(fb, 1);
+}
+
+static inline char *fbuffer_cursor(FBuffer *fb)
+{
+ return fb->ptr + fb->len;
+}
+
+static inline void fbuffer_advance_to(FBuffer *fb, char *end)
+{
+ fbuffer_consumed(fb, (end - fb->ptr) - fb->len);
+}
+
+/*
+ * Appends the decimal string representation of \a number into the buffer.
+ */
+static void fbuffer_append_long(FBuffer *fb, long number)
+{
+ /*
+ * The jeaiii_ultoa() function produces digits left-to-right,
+ * allowing us to write directly into the buffer, but we don't know
+ * the number of resulting characters.
+ *
+ * We do know, however, that the `number` argument is always in the
+ * range 0xc000000000000000 to 0x3fffffffffffffff, or, in decimal,
+ * -4611686018427387904 to 4611686018427387903. The max number of chars
+ * generated is therefore 20 (including a potential sign character).
+ */
+
+ static const int MAX_CHARS_FOR_LONG = 20;
+
+ fbuffer_inc_capa(fb, MAX_CHARS_FOR_LONG);
+
+ if (number < 0) {
+ fbuffer_append_reserved_char(fb, '-');
+
+ /*
+ * Since number is always > LONG_MIN, `-number` will not overflow
+ * and is always the positive abs() value.
+ */
+ number = -number;
+ }
+
+ char *end = jeaiii_ultoa(fbuffer_cursor(fb), number);
+ fbuffer_advance_to(fb, end);
+}
+
+static VALUE fbuffer_finalize(FBuffer *fb)
+{
+ if (fb->io) {
+ fbuffer_flush(fb);
+ rb_io_flush(fb->io);
+ return fb->io;
+ } else {
+ return rb_utf8_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
+ }
+}
+
+#endif // _FBUFFER_H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/Makefile b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/Makefile
new file mode 100644
index 0000000..926cf71
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/Makefile
@@ -0,0 +1,273 @@
+
+SHELL = /bin/sh
+
+# V=0 quiet, V=1 verbose. other values don't work.
+V = 0
+V0 = $(V:0=)
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@ :)
+ECHO = $(ECHO1:0=@ echo)
+NULLCMD = :
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0
+hdrdir = $(topdir)
+arch_hdrdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24
+PATH_SEPARATOR = :
+VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
+prefix = $(DESTDIR)/Users/ben/.rubies/ruby-3.4.1
+rubysitearchprefix = $(rubylibprefix)/$(sitearch)
+rubyarchprefix = $(rubylibprefix)/$(arch)
+rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
+exec_prefix = $(prefix)
+vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
+sitearchhdrdir = $(sitehdrdir)/$(sitearch)
+rubyarchhdrdir = $(rubyhdrdir)/$(arch)
+vendorhdrdir = $(rubyhdrdir)/vendor_ruby
+sitehdrdir = $(rubyhdrdir)/site_ruby
+rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
+vendorarchdir = $(vendorlibdir)/$(sitearch)
+vendorlibdir = $(vendordir)/$(ruby_version)
+vendordir = $(rubylibprefix)/vendor_ruby
+sitearchdir = $(sitelibdir)/$(sitearch)
+sitelibdir = $(sitedir)/$(ruby_version)
+sitedir = $(rubylibprefix)/site_ruby
+rubyarchdir = $(rubylibdir)/$(arch)
+rubylibdir = $(rubylibprefix)/$(ruby_version)
+sitearchincludedir = $(includedir)/$(sitearch)
+archincludedir = $(includedir)/$(arch)
+sitearchlibdir = $(libdir)/$(sitearch)
+archlibdir = $(libdir)/$(arch)
+ridir = $(datarootdir)/$(RI_BASE_NAME)
+modular_gc_dir = $(DESTDIR)
+mandir = $(datarootdir)/man
+localedir = $(datarootdir)/locale
+libdir = $(exec_prefix)/lib
+psdir = $(docdir)
+pdfdir = $(docdir)
+dvidir = $(docdir)
+htmldir = $(docdir)
+infodir = $(datarootdir)/info
+docdir = $(datarootdir)/doc/$(PACKAGE)
+oldincludedir = $(DESTDIR)/usr/include
+includedir = $(SDKROOT)$(prefix)/include
+runstatedir = $(localstatedir)/run
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+sysconfdir = $(prefix)/etc
+datadir = $(datarootdir)
+datarootdir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+sbindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/bin
+archdir = $(rubyarchdir)
+
+
+CC_WRAPPER =
+CC = clang
+CXX = clang++ -std=gnu++11
+LIBRUBY = $(LIBRUBY_A)
+LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
+LIBRUBYARG_SHARED =
+LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation $(MAINLIBS)
+empty =
+OUTFLAG = -o $(empty)
+COUTFLAG = -o $(empty)
+CSRCFLAG = $(empty)
+
+RUBY_EXTCONF_H =
+cflags = $(hardenflags) -fdeclspec $(optflags) $(debugflags) $(warnflags)
+cxxflags =
+optflags = -O3 -fno-fast-math
+debugflags = -ggdb3
+warnflags = -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef
+cppflags =
+CCDLFLAGS = -fno-common
+CFLAGS = $(CCDLFLAGS) $(cflags) -pipe $(ARCH_FLAG)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS = -DHAVE_CONST_RUBY_TYPED_EMBEDDABLE -DJSON_GENERATOR -DHAVE_ARM_NEON_H -DJSON_ENABLE_SIMD -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/libyaml/include -I/opt/homebrew/opt/gdbm/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
+CXXFLAGS = $(CCDLFLAGS) -fdeclspec $(ARCH_FLAG)
+ldflags = -L. -fstack-protector-strong -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib
+dldflags = -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib -Wl,-undefined,dynamic_lookup -bundle_loader '$(BUILTRUBY)'
+ARCH_FLAG = -arch arm64
+DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
+LDSHARED = $(CC) -dynamic -bundle
+LDSHAREDXX = $(CXX) -dynamic -bundle
+POSTLINK = dsymutil $@ 2>/dev/null; { test -z '$(RUBY_CODESIGN)' || codesign -s '$(RUBY_CODESIGN)' $@; }
+AR = ar
+LD = ld
+EXEEXT =
+
+RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
+RUBY_SO_NAME = ruby.3.4
+RUBYW_INSTALL_NAME =
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+RUBYW_BASE_NAME = rubyw
+RUBY_BASE_NAME = ruby
+
+arch = arm64-darwin24
+sitearch = $(arch)
+ruby_version = 3.4.0
+ruby = $(bindir)/$(RUBY_BASE_NAME)
+RUBY = $(ruby)
+BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
+ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
+
+RM = rm -f
+RM_RF = rm -fr
+RMDIRS = rmdir -p
+MAKEDIRS = mkdir -p
+INSTALL = /usr/bin/install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+TOUCH = exit >
+
+#### End of system configuration section. ####
+
+preload =
+libpath = . $(libdir) /opt/homebrew/opt/readline/lib /opt/homebrew/opt/libyaml/lib /opt/homebrew/opt/gdbm/lib
+LIBPATH = -L. -L$(libdir) -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/gdbm/lib
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+DISTCLEANDIRS =
+
+extout =
+extout_prefix =
+target_prefix = /json/ext
+LOCAL_LIBS =
+LIBS = -lpthread
+ORIG_SRCS = generator.c
+SRCS = $(ORIG_SRCS)
+OBJS = generator.o
+HDRS =
+LOCAL_HDRS =
+TARGET = generator
+TARGET_NAME = generator
+TARGET_ENTRY = Init_$(TARGET_NAME)
+DLLIB = $(TARGET).bundle
+EXTSTATIC =
+STATIC_LIB =
+
+TIMESTAMP_DIR = .
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+HDRDIR = $(sitehdrdir)$(target_prefix)
+ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
+TARGET_SO_DIR =
+TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
+CLEANLIBS = $(TARGET_SO) $(TARGET_SO:=.dSYM)
+CLEANOBJS = $(OBJS) *.bak
+TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-static clean-rb
+
+clean-static::
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-static clean-rb-default clean-rb
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
+
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+clean-static::
+ -$(Q)$(RM) $(STATIC_LIB)
+install-rb: pre-install-rb do-install-rb install-rb-default
+install-rb-default: pre-install-rb-default do-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+do-install-rb:
+do-install-rb-default:
+pre-install-rb-default:
+ @$(NULLCMD)
+$(TARGET_SO_DIR_TIMESTAMP):
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
+ $(Q) $(TOUCH) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
+
+.cc.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cc.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.mm.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.mm.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cxx.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cxx.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cpp.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cpp.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.c.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.c.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.m.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.m.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+$(TARGET_SO): $(OBJS) Makefile
+ $(ECHO) linking shared-object json/ext/$(DLLIB)
+ -$(Q)$(RM) $(@)
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+ $(Q) $(POSTLINK)
+
+
+
+$(OBJS): $(HDRS) $(ruby_headers)
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/extconf.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/extconf.rb
new file mode 100644
index 0000000..33af03e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/extconf.rb
@@ -0,0 +1,19 @@
+require 'mkmf'
+
+if RUBY_ENGINE == 'truffleruby'
+ # The pure-Ruby generator is faster on TruffleRuby, so skip compiling the generator extension
+ File.write('Makefile', dummy_makefile("").join)
+else
+ append_cflags("-std=c99")
+ have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
+ have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
+
+ $defs << "-DJSON_GENERATOR"
+ $defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
+
+ if enable_config('generator-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
+ load __dir__ + "/../simd/conf.rb"
+ end
+
+ create_makefile 'json/ext/generator'
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/generator.c b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/generator.c
new file mode 100644
index 0000000..6f47332
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/generator/generator.c
@@ -0,0 +1,2066 @@
+#include "../json.h"
+#include "../fbuffer/fbuffer.h"
+#include "../vendor/fpconv.c"
+
+#include <math.h>
+#include <ctype.h>
+
+#include "../simd/simd.h"
+
+/* ruby api and some helpers */
+
+enum duplicate_key_action {
+ JSON_DEPRECATED = 0,
+ JSON_IGNORE,
+ JSON_RAISE,
+};
+
+typedef struct JSON_Generator_StateStruct {
+ VALUE indent;
+ VALUE space;
+ VALUE space_before;
+ VALUE object_nl;
+ VALUE array_nl;
+ VALUE as_json;
+
+ long max_nesting;
+ long depth;
+ long buffer_initial_length;
+
+ enum duplicate_key_action on_duplicate_key;
+
+ bool as_json_single_arg;
+ bool allow_nan;
+ bool ascii_only;
+ bool script_safe;
+ bool strict;
+ VALUE sort_keys;
+} JSON_Generator_State;
+
+static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
+
+static ID i_to_s, i_to_json, i_new, i_encode;
+static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
+ sym_ascii_only, sym_depth, sym_buffer_initial_length, sym_script_safe, sym_escape_slash, sym_strict, sym_as_json, sym_sort_keys;
+
+
+#define GET_STATE_TO(self, state) \
+ TypedData_Get_Struct(self, JSON_Generator_State, &JSON_Generator_State_type, state)
+
+#define GET_STATE(self) \
+ JSON_Generator_State *state; \
+ GET_STATE_TO(self, state)
+
+struct generate_json_data;
+
+typedef void (*generator_func)(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+
+struct generate_json_data {
+ FBuffer *buffer;
+ VALUE vstate;
+ JSON_Generator_State *state;
+ VALUE obj;
+ generator_func func;
+ long depth;
+};
+
+static SIMD_Implementation simd_impl;
+
+static VALUE cState_from_state_s(VALUE self, VALUE opts);
+static VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func, VALUE io);
+static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj);
+
+static int usascii_encindex, utf8_encindex, binary_encindex;
+
+NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str)
+{
+ rb_enc_associate_index(str, utf8_encindex);
+ VALUE exc = rb_exc_new_str(eGeneratorError, str);
+ rb_ivar_set(exc, rb_intern("@invalid_object"), invalid_object);
+ rb_exc_raise(exc);
+}
+
+#ifdef RBIMPL_ATTR_FORMAT
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+#endif
+NORETURN(static void) raise_generator_error(VALUE invalid_object, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ VALUE str = rb_vsprintf(fmt, args);
+ va_end(args);
+ raise_generator_error_str(invalid_object, str);
+}
+
+// 0 - single byte char that don't need to be escaped.
+// (x | 8) - char that needs to be escaped.
+static const unsigned char CHAR_LENGTH_MASK = 7;
+static const unsigned char ESCAPE_MASK = 8;
+
+typedef struct _search_state {
+ const char *ptr;
+ const char *end;
+ const char *cursor;
+ FBuffer *buffer;
+
+#ifdef HAVE_SIMD
+ const char *chunk_base;
+ const char *chunk_end;
+ bool has_matches;
+
+#if defined(HAVE_SIMD_NEON)
+ uint64_t matches_mask;
+#elif defined(HAVE_SIMD_SSE2)
+ int matches_mask;
+#else
+#error "Unknown SIMD Implementation."
+#endif /* HAVE_SIMD_NEON */
+#endif /* HAVE_SIMD */
+} search_state;
+
+ALWAYS_INLINE(static) void search_flush(search_state *search)
+{
+ // Do not remove this conditional without profiling, specifically escape-heavy text.
+ // escape_UTF8_char_basic will advance search->ptr and search->cursor (effectively a search_flush).
+ // For back-to-back characters that need to be escaped, specifically for the SIMD code paths, this method
+ // will be called just before calling escape_UTF8_char_basic. There will be no characters to append for the
+ // consecutive characters that need to be escaped. While the fbuffer_append is a no-op if
+ // nothing needs to be flushed, we can save a few memory references with this conditional.
+ if (search->ptr > search->cursor) {
+ fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
+ search->cursor = search->ptr;
+ }
+}
+
+static const unsigned char escape_table_basic[256] = {
+ // ASCII Control Characters
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ // ASCII Characters
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static inline unsigned char search_escape_basic(search_state *search)
+{
+ while (search->ptr < search->end) {
+ if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) {
+ search_flush(search);
+ return 1;
+ } else {
+ search->ptr++;
+ }
+ }
+ search_flush(search);
+ return 0;
+}
+
+ALWAYS_INLINE(static) void escape_UTF8_char_basic(search_state *search)
+{
+ const unsigned char ch = (unsigned char)*search->ptr;
+ switch (ch) {
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
+ default: {
+ const char *hexdig = "0123456789abcdef";
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
+ scratch[5] = hexdig[ch & 0xf];
+ fbuffer_append(search->buffer, scratch, 6);
+ break;
+ }
+ }
+ search->ptr++;
+ search->cursor = search->ptr;
+}
+
+/* Converts in_string to a JSON string (without the wrapping '"'
+ * characters) in FBuffer out_buffer.
+ *
+ * Character are JSON-escaped according to:
+ *
+ * - Always: ASCII control characters (0x00-0x1F), dquote, and
+ * backslash.
+ *
+ * - If out_ascii_only: non-ASCII characters (>0x7F)
+ *
+ * - If script_safe: forwardslash (/), line separator (U+2028), and
+ * paragraph separator (U+2029)
+ *
+ * Everything else (should be UTF-8) is just passed through and
+ * appended to the result.
+ */
+
+
+#if defined(HAVE_SIMD_NEON)
+static inline unsigned char search_escape_basic_neon(search_state *search);
+#elif defined(HAVE_SIMD_SSE2)
+static inline unsigned char search_escape_basic_sse2(search_state *search);
+#endif
+
+static inline unsigned char search_escape_basic(search_state *search);
+
+static inline void convert_UTF8_to_JSON(search_state *search)
+{
+#ifdef HAVE_SIMD
+#if defined(HAVE_SIMD_NEON)
+ while (search_escape_basic_neon(search)) {
+ escape_UTF8_char_basic(search);
+ }
+#elif defined(HAVE_SIMD_SSE2)
+ if (simd_impl == SIMD_SSE2) {
+ while (search_escape_basic_sse2(search)) {
+ escape_UTF8_char_basic(search);
+ }
+ return;
+ }
+ while (search_escape_basic(search)) {
+ escape_UTF8_char_basic(search);
+ }
+#endif
+#else
+ while (search_escape_basic(search)) {
+ escape_UTF8_char_basic(search);
+ }
+#endif /* HAVE_SIMD */
+}
+
+static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
+{
+ const unsigned char ch = (unsigned char)*search->ptr;
+ switch (ch_len) {
+ case 1: {
+ switch (ch) {
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
+ default: {
+ const char *hexdig = "0123456789abcdef";
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
+ scratch[5] = hexdig[ch & 0xf];
+ fbuffer_append(search->buffer, scratch, 6);
+ break;
+ }
+ }
+ break;
+ }
+ case 3: {
+ if (search->ptr[2] & 1) {
+ fbuffer_append(search->buffer, "\\u2029", 6);
+ } else {
+ fbuffer_append(search->buffer, "\\u2028", 6);
+ }
+ break;
+ }
+ }
+ search->cursor = (search->ptr += ch_len);
+}
+
+#ifdef HAVE_SIMD
+
+ALWAYS_INLINE(static) char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len)
+{
+ RBIMPL_ASSERT_OR_ASSUME(len < vec_len);
+
+ // Flush the buffer so everything up until the last 'len' characters are unflushed.
+ search_flush(search);
+
+ FBuffer *buf = search->buffer;
+ fbuffer_inc_capa(buf, vec_len);
+
+ char *s = (buf->ptr + buf->len);
+
+ // Pad the buffer with dummy characters that won't need escaping.
+ // This seem wasteful at first sight, but memset of vector length is very fast.
+ // This is a space as it can be directly represented as an immediate on AArch64.
+ memset(s, ' ', vec_len);
+
+ // Optimistically copy the remaining 'len' characters to the output FBuffer. If there are no characters
+ // to escape, then everything ends up in the correct spot. Otherwise it was convenient temporary storage.
+ if (vec_len == 16) {
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD);
+ json_fast_memcpy16(s, search->ptr, len);
+ } else {
+ MEMCPY(s, search->ptr, char, len);
+ }
+
+ return s;
+}
+
+#ifdef HAVE_SIMD_NEON
+
+ALWAYS_INLINE(static) unsigned char neon_next_match(search_state *search)
+{
+ uint64_t mask = search->matches_mask;
+ uint32_t index = trailing_zeros64(mask) >> 2;
+
+ // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character.
+ // If we want to use a similar approach for full escaping we'll need to ensure:
+ // search->chunk_base + index >= search->ptr
+ // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match
+ // is one byte after the previous match then:
+ // search->chunk_base + index == search->ptr
+ search->ptr = search->chunk_base + index;
+ mask &= mask - 1;
+ search->matches_mask = mask;
+ search_flush(search);
+ return 1;
+}
+
+static inline unsigned char search_escape_basic_neon(search_state *search)
+{
+ if (RB_UNLIKELY(search->has_matches)) {
+ // There are more matches if search->matches_mask > 0.
+ if (search->matches_mask > 0) {
+ return neon_next_match(search);
+ } else {
+ // neon_next_match will only advance search->ptr up to the last matching character.
+ // Skip over any characters in the last chunk that occur after the last match.
+ search->has_matches = false;
+ search->ptr = search->chunk_end;
+ }
+ }
+
+ /*
+ * The code below implements an SIMD-based algorithm to determine if N bytes at a time
+ * need to be escaped.
+ *
+ * Assume the ptr = "Te\sting!" (the double quotes are included in the string)
+ *
+ * The explanation will be limited to the first 8 bytes of the string for simplicity. However
+ * the vector insructions may work on larger vectors.
+ *
+ * First, we load three constants 'lower_bound', 'backslash' and 'dblquote" in vector registers.
+ *
+ * lower_bound: [20 20 20 20 20 20 20 20]
+ * backslash: [5C 5C 5C 5C 5C 5C 5C 5C]
+ * dblquote: [22 22 22 22 22 22 22 22]
+ *
+ * Next we load the first chunk of the ptr:
+ * [22 54 65 5C 73 74 69 6E] (" T e \ s t i n)
+ *
+ * First we check if any byte in chunk is less than 32 (0x20). This returns the following vector
+ * as no bytes are less than 32 (0x20):
+ * [0 0 0 0 0 0 0 0]
+ *
+ * Next, we check if any byte in chunk is equal to a backslash:
+ * [0 0 0 FF 0 0 0 0]
+ *
+ * Finally we check if any byte in chunk is equal to a double quote:
+ * [FF 0 0 0 0 0 0 0]
+ *
+ * Now we have three vectors where each byte indicates if the corresponding byte in chunk
+ * needs to be escaped. We combine these vectors with a series of logical OR instructions.
+ * This is the needs_escape vector and it is equal to:
+ * [FF 0 0 FF 0 0 0 0]
+ *
+ * Next we compute the bitwise AND between each byte and 0x1 and compute the horizontal sum of
+ * the values in the vector. This computes how many bytes need to be escaped within this chunk.
+ *
+ * Finally we compute a mask that indicates which bytes need to be escaped. If the mask is 0 then,
+ * no bytes need to be escaped and we can continue to the next chunk. If the mask is not 0 then we
+ * have at least one byte that needs to be escaped.
+ */
+
+ if (string_scan_simd_neon(&search->ptr, search->end, &search->matches_mask)) {
+ search->has_matches = true;
+ search->chunk_base = search->ptr;
+ search->chunk_end = search->ptr + sizeof(uint8x16_t);
+ return neon_next_match(search);
+ }
+
+ // There are fewer than 16 bytes left.
+ unsigned long remaining = (search->end - search->ptr);
+ if (remaining >= SIMD_MINIMUM_THRESHOLD) {
+ char *s = copy_remaining_bytes(search, sizeof(uint8x16_t), remaining);
+
+ uint64_t mask = compute_chunk_mask_neon(s);
+
+ if (!mask) {
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
+ // search->cursor to search->ptr.
+ fbuffer_consumed(search->buffer, remaining);
+ search->ptr = search->end;
+ search->cursor = search->end;
+ return 0;
+ }
+
+ search->matches_mask = mask;
+ search->has_matches = true;
+ search->chunk_end = search->end;
+ search->chunk_base = search->ptr;
+ return neon_next_match(search);
+ }
+
+ if (search->ptr < search->end) {
+ return search_escape_basic(search);
+ }
+
+ search_flush(search);
+ return 0;
+}
+#endif /* HAVE_SIMD_NEON */
+
+#ifdef HAVE_SIMD_SSE2
+
+ALWAYS_INLINE(static) unsigned char sse2_next_match(search_state *search)
+{
+ int mask = search->matches_mask;
+ int index = trailing_zeros(mask);
+
+ // It is assumed escape_UTF8_char_basic will only ever increase search->ptr by at most one character.
+ // If we want to use a similar approach for full escaping we'll need to ensure:
+ // search->chunk_base + index >= search->ptr
+ // However, since we know escape_UTF8_char_basic only increases search->ptr by one, if the next match
+ // is one byte after the previous match then:
+ // search->chunk_base + index == search->ptr
+ search->ptr = search->chunk_base + index;
+ mask &= mask - 1;
+ search->matches_mask = mask;
+ search_flush(search);
+ return 1;
+}
+
+#if defined(__clang__) || defined(__GNUC__)
+#define TARGET_SSE2 __attribute__((target("sse2")))
+#else
+#define TARGET_SSE2
+#endif
+
+ALWAYS_INLINE(static) TARGET_SSE2 unsigned char search_escape_basic_sse2(search_state *search)
+{
+ if (RB_UNLIKELY(search->has_matches)) {
+ // There are more matches if search->matches_mask > 0.
+ if (search->matches_mask > 0) {
+ return sse2_next_match(search);
+ } else {
+ // sse2_next_match will only advance search->ptr up to the last matching character.
+ // Skip over any characters in the last chunk that occur after the last match.
+ search->has_matches = false;
+ if (RB_UNLIKELY(search->chunk_base + sizeof(__m128i) >= search->end)) {
+ search->ptr = search->end;
+ } else {
+ search->ptr = search->chunk_base + sizeof(__m128i);
+ }
+ }
+ }
+
+ if (string_scan_simd_sse2(&search->ptr, search->end, &search->matches_mask)) {
+ search->has_matches = true;
+ search->chunk_base = search->ptr;
+ search->chunk_end = search->ptr + sizeof(__m128i);
+ return sse2_next_match(search);
+ }
+
+ // There are fewer than 16 bytes left.
+ unsigned long remaining = (search->end - search->ptr);
+ if (remaining >= SIMD_MINIMUM_THRESHOLD) {
+ char *s = copy_remaining_bytes(search, sizeof(__m128i), remaining);
+
+ int needs_escape_mask = compute_chunk_mask_sse2(s);
+
+ if (needs_escape_mask == 0) {
+ // Nothing to escape, ensure search_flush doesn't do anything by setting
+ // search->cursor to search->ptr.
+ fbuffer_consumed(search->buffer, remaining);
+ search->ptr = search->end;
+ search->cursor = search->end;
+ return 0;
+ }
+
+ search->has_matches = true;
+ search->matches_mask = needs_escape_mask;
+ search->chunk_base = search->ptr;
+ return sse2_next_match(search);
+ }
+
+ if (search->ptr < search->end) {
+ return search_escape_basic(search);
+ }
+
+ search_flush(search);
+ return 0;
+}
+
+#endif /* HAVE_SIMD_SSE2 */
+
+#endif /* HAVE_SIMD */
+
+static const unsigned char script_safe_escape_table[256] = {
+ // ASCII Control Characters
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ // ASCII Characters
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // '"' and '/'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // Continuation byte
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ // First byte of a 2-byte code point
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ // First byte of a 3-byte code point
+ 3, 3,11, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE2 is the start of \u2028 and \u2029
+ //First byte of a 4+ byte code point
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
+};
+
+static inline unsigned char search_script_safe_escape(search_state *search)
+{
+ while (search->ptr < search->end) {
+ unsigned char ch = (unsigned char)*search->ptr;
+ unsigned char ch_len = script_safe_escape_table[ch];
+
+ if (RB_UNLIKELY(ch_len)) {
+ if (ch_len & ESCAPE_MASK) {
+ if (RB_UNLIKELY(ch_len == 11)) {
+ const unsigned char *uptr = (const unsigned char *)search->ptr;
+ if (!(uptr[1] == 0x80 && (uptr[2] >> 1) == 0x54)) {
+ search->ptr += 3;
+ continue;
+ }
+ }
+ search_flush(search);
+ return ch_len & CHAR_LENGTH_MASK;
+ } else {
+ search->ptr += ch_len;
+ }
+ } else {
+ search->ptr++;
+ }
+ }
+ search_flush(search);
+ return 0;
+}
+
+static void convert_UTF8_to_script_safe_JSON(search_state *search)
+{
+ unsigned char ch_len;
+ while ((ch_len = search_script_safe_escape(search))) {
+ escape_UTF8_char(search, ch_len);
+ }
+}
+
+static const unsigned char ascii_only_escape_table[256] = {
+ // ASCII Control Characters
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ // ASCII Characters
+ 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, // '\\'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // Continuation byte
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ // First byte of a 2-byte code point
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ // First byte of a 3-byte code point
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ //First byte of a 4+ byte code point
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 9, 9,
+};
+
+static inline unsigned char search_ascii_only_escape(search_state *search, const unsigned char escape_table[256])
+{
+ while (search->ptr < search->end) {
+ unsigned char ch = (unsigned char)*search->ptr;
+ unsigned char ch_len = escape_table[ch];
+
+ if (RB_UNLIKELY(ch_len)) {
+ search_flush(search);
+ return ch_len & CHAR_LENGTH_MASK;
+ } else {
+ search->ptr++;
+ }
+ }
+ search_flush(search);
+ return 0;
+}
+
+static inline void full_escape_UTF8_char(search_state *search, unsigned char ch_len)
+{
+ const unsigned char ch = (unsigned char)*search->ptr;
+ switch (ch_len) {
+ case 1: {
+ switch (ch) {
+ case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
+ case '\\': fbuffer_append(search->buffer, "\\\\", 2); break;
+ case '/': fbuffer_append(search->buffer, "\\/", 2); break;
+ case '\b': fbuffer_append(search->buffer, "\\b", 2); break;
+ case '\f': fbuffer_append(search->buffer, "\\f", 2); break;
+ case '\n': fbuffer_append(search->buffer, "\\n", 2); break;
+ case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
+ case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
+ default: {
+ const char *hexdig = "0123456789abcdef";
+ char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
+ scratch[4] = hexdig[(ch >> 4) & 0xf];
+ scratch[5] = hexdig[ch & 0xf];
+ fbuffer_append(search->buffer, scratch, 6);
+ break;
+ }
+ }
+ break;
+ }
+ default: {
+ const char *hexdig = "0123456789abcdef";
+ char scratch[12] = { '\\', 'u', 0, 0, 0, 0, '\\', 'u' };
+
+ uint32_t wchar = 0;
+
+ switch (ch_len) {
+ case 2:
+ wchar = ch & 0x1F;
+ break;
+ case 3:
+ wchar = ch & 0x0F;
+ break;
+ case 4:
+ wchar = ch & 0x07;
+ break;
+ }
+
+ for (short i = 1; i < ch_len; i++) {
+ wchar = (wchar << 6) | (search->ptr[i] & 0x3F);
+ }
+
+ if (wchar <= 0xFFFF) {
+ scratch[2] = hexdig[wchar >> 12];
+ scratch[3] = hexdig[(wchar >> 8) & 0xf];
+ scratch[4] = hexdig[(wchar >> 4) & 0xf];
+ scratch[5] = hexdig[wchar & 0xf];
+ fbuffer_append(search->buffer, scratch, 6);
+ } else {
+ uint16_t hi, lo;
+ wchar -= 0x10000;
+ hi = 0xD800 + (uint16_t)(wchar >> 10);
+ lo = 0xDC00 + (uint16_t)(wchar & 0x3FF);
+
+ scratch[2] = hexdig[hi >> 12];
+ scratch[3] = hexdig[(hi >> 8) & 0xf];
+ scratch[4] = hexdig[(hi >> 4) & 0xf];
+ scratch[5] = hexdig[hi & 0xf];
+
+ scratch[8] = hexdig[lo >> 12];
+ scratch[9] = hexdig[(lo >> 8) & 0xf];
+ scratch[10] = hexdig[(lo >> 4) & 0xf];
+ scratch[11] = hexdig[lo & 0xf];
+
+ fbuffer_append(search->buffer, scratch, 12);
+ }
+
+ break;
+ }
+ }
+ search->cursor = (search->ptr += ch_len);
+}
+
+static void convert_UTF8_to_ASCII_only_JSON(search_state *search, const unsigned char escape_table[256])
+{
+ unsigned char ch_len;
+ while ((ch_len = search_ascii_only_escape(search, escape_table))) {
+ full_escape_UTF8_char(search, ch_len);
+ }
+}
+
+static void State_mark(void *ptr)
+{
+ JSON_Generator_State *state = ptr;
+ rb_gc_mark_movable(state->indent);
+ rb_gc_mark_movable(state->space);
+ rb_gc_mark_movable(state->space_before);
+ rb_gc_mark_movable(state->object_nl);
+ rb_gc_mark_movable(state->array_nl);
+ rb_gc_mark_movable(state->as_json);
+ rb_gc_mark_movable(state->sort_keys);
+}
+
+static void State_compact(void *ptr)
+{
+ JSON_Generator_State *state = ptr;
+ state->indent = rb_gc_location(state->indent);
+ state->space = rb_gc_location(state->space);
+ state->space_before = rb_gc_location(state->space_before);
+ state->object_nl = rb_gc_location(state->object_nl);
+ state->array_nl = rb_gc_location(state->array_nl);
+ state->as_json = rb_gc_location(state->as_json);
+ state->sort_keys = rb_gc_location(state->sort_keys);
+}
+
+static size_t State_memsize(const void *ptr)
+{
+#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
+ return 0;
+#else
+ return sizeof(JSON_Generator_State);
+#endif
+}
+
+static const rb_data_type_t JSON_Generator_State_type = {
+ .wrap_struct_name = "JSON/Generator/State",
+ .function = {
+ .dmark = State_mark,
+ .dfree = RUBY_DEFAULT_FREE,
+ .dsize = State_memsize,
+ .dcompact = State_compact,
+ },
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
+};
+
+static void state_init(JSON_Generator_State *state)
+{
+ state->max_nesting = 100;
+ state->buffer_initial_length = FBUFFER_INITIAL_LENGTH_DEFAULT;
+}
+
+static VALUE cState_s_allocate(VALUE klass)
+{
+ JSON_Generator_State *state;
+ VALUE obj = TypedData_Make_Struct(klass, JSON_Generator_State, &JSON_Generator_State_type, state);
+ state_init(state);
+ return obj;
+}
+
+static void vstate_spill(struct generate_json_data *data)
+{
+ VALUE vstate = cState_s_allocate(cState);
+ GET_STATE(vstate);
+ MEMCPY(state, data->state, JSON_Generator_State, 1);
+ data->state = state;
+ data->vstate = vstate;
+ RB_OBJ_WRITTEN(vstate, Qundef, state->indent);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->space);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->space_before);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->object_nl);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->array_nl);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->as_json);
+ RB_OBJ_WRITTEN(vstate, Qundef, state->sort_keys);
+}
+
+static inline VALUE json_call_to_json(struct generate_json_data *data, VALUE obj)
+{
+ if (RB_UNLIKELY(!data->vstate)) {
+ vstate_spill(data);
+ }
+ GET_STATE(data->vstate);
+ state->depth = data->depth;
+ VALUE tmp = rb_funcall(obj, i_to_json, 1, data->vstate);
+ // no need to restore state->depth, vstate is just a temporary State
+ return tmp;
+}
+
+static VALUE
+json_call_as_json(JSON_Generator_State *state, VALUE object, VALUE is_key)
+{
+ VALUE proc_args[2] = {object, is_key};
+ return rb_proc_call_with_block(state->as_json, 2, proc_args, Qnil);
+}
+
+static VALUE
+convert_string_subclass(VALUE key)
+{
+ VALUE key_to_s = rb_funcall(key, i_to_s, 0);
+
+ if (RB_UNLIKELY(!RB_TYPE_P(key_to_s, T_STRING))) {
+ VALUE cname = rb_obj_class(key);
+ rb_raise(rb_eTypeError,
+ "can't convert %"PRIsVALUE" to %s (%"PRIsVALUE"#%s gives %"PRIsVALUE")",
+ cname, "String", cname, "to_s", rb_obj_class(key_to_s));
+ }
+
+ return key_to_s;
+}
+
+static bool enc_utf8_compatible_p(int enc_idx)
+{
+ if (enc_idx == usascii_encindex) return true;
+ if (enc_idx == utf8_encindex) return true;
+ return false;
+}
+
+static VALUE encode_json_string_try(VALUE str)
+{
+ return rb_funcall(str, i_encode, 1, Encoding_UTF_8);
+}
+
+static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
+{
+ raise_generator_error_str(str, rb_funcall(exception, rb_intern("message"), 0));
+ return Qundef;
+}
+
+static inline int json_str_coderange(VALUE str) {
+ int coderange = RB_ENC_CODERANGE(str);
+ if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
+ coderange = rb_enc_str_coderange(str);
+ }
+ return coderange;
+}
+
+static inline bool valid_json_string_p(VALUE str)
+{
+ int coderange = json_str_coderange(str);
+
+ if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
+ return true;
+ }
+
+ if (RB_LIKELY(coderange == ENC_CODERANGE_VALID)) {
+ return enc_utf8_compatible_p(RB_ENCODING_GET_INLINED(str));
+ }
+
+ return false;
+}
+
+NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
+{
+ if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
+ VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
+ if (coerced_str != str) {
+ if (RB_TYPE_P(coerced_str, T_STRING)) {
+ if (!valid_json_string_p(coerced_str)) {
+ raise_generator_error(str, "source sequence is illegal/malformed utf-8");
+ }
+ } else {
+ // as_json could return another type than T_STRING
+ if (is_key) {
+ raise_generator_error(coerced_str, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(coerced_str));
+ }
+ }
+
+ return coerced_str;
+ }
+ }
+
+ if (RB_ENCODING_GET_INLINED(str) == binary_encindex) {
+ VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex);
+ switch (rb_enc_str_coderange(utf8_string)) {
+ case ENC_CODERANGE_7BIT:
+ return utf8_string;
+ case ENC_CODERANGE_VALID:
+ // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work.
+ // TODO: Raise in 3.0.0
+ rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0");
+ return utf8_string;
+ break;
+ }
+ }
+
+ return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
+}
+
+ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
+{
+ if (RB_LIKELY(valid_json_string_p(str))) {
+ return str;
+ }
+ else {
+ return convert_invalid_encoding(data, str, as_json_called, is_key);
+ }
+}
+
+static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ fbuffer_append_char(buffer, '"');
+
+ long len;
+ search_state search;
+ search.buffer = buffer;
+ RSTRING_GETMEM(obj, search.ptr, len);
+ search.cursor = search.ptr;
+ search.end = search.ptr + len;
+
+#ifdef HAVE_SIMD
+ search.matches_mask = 0;
+ search.has_matches = false;
+ search.chunk_base = NULL;
+ search.chunk_end = NULL;
+#endif /* HAVE_SIMD */
+
+ switch (json_str_coderange(obj)) {
+ case ENC_CODERANGE_7BIT:
+ case ENC_CODERANGE_VALID:
+ if (RB_UNLIKELY(data->state->ascii_only)) {
+ convert_UTF8_to_ASCII_only_JSON(&search, data->state->script_safe ? script_safe_escape_table : ascii_only_escape_table);
+ } else if (RB_UNLIKELY(data->state->script_safe)) {
+ convert_UTF8_to_script_safe_JSON(&search);
+ } else {
+ convert_UTF8_to_JSON(&search);
+ }
+ break;
+ default:
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
+ break;
+ }
+ fbuffer_append_char(buffer, '"');
+}
+
+static void generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ obj = ensure_valid_encoding(data, obj, false, false);
+ raw_generate_json_string(buffer, data, obj);
+}
+
+struct hash_foreach_arg {
+ VALUE hash;
+ struct generate_json_data *data;
+ int first_key_type;
+ bool first;
+ bool mixed_keys_encountered;
+};
+
+NOINLINE(static) void
+json_inspect_hash_with_mixed_keys(struct hash_foreach_arg *arg)
+{
+ if (arg->mixed_keys_encountered) {
+ return;
+ }
+ arg->mixed_keys_encountered = true;
+
+ JSON_Generator_State *state = arg->data->state;
+ if (state->on_duplicate_key != JSON_IGNORE) {
+ VALUE do_raise = state->on_duplicate_key == JSON_RAISE ? Qtrue : Qfalse;
+ rb_funcall(mJSON, rb_intern("on_mixed_keys_hash"), 2, arg->hash, do_raise);
+ }
+}
+
+static int
+json_object_i(VALUE key, VALUE val, VALUE _arg)
+{
+ struct hash_foreach_arg *arg = (struct hash_foreach_arg *)_arg;
+ struct generate_json_data *data = arg->data;
+
+ FBuffer *buffer = data->buffer;
+ JSON_Generator_State *state = data->state;
+
+ long depth = data->depth;
+ int key_type = rb_type(key);
+
+ if (arg->first) {
+ arg->first = false;
+ arg->first_key_type = key_type;
+ }
+ else {
+ fbuffer_append_char(buffer, ',');
+ }
+
+ if (RB_UNLIKELY(data->state->object_nl)) {
+ fbuffer_append_str(buffer, data->state->object_nl);
+ }
+ if (RB_UNLIKELY(data->state->indent)) {
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
+ }
+
+ VALUE key_to_s;
+ bool as_json_called = false;
+
+ start:
+ switch (key_type) {
+ case T_STRING:
+ if (RB_UNLIKELY(arg->first_key_type != T_STRING)) {
+ json_inspect_hash_with_mixed_keys(arg);
+ }
+
+ if (RB_LIKELY(RBASIC_CLASS(key) == rb_cString)) {
+ key_to_s = key;
+ } else {
+ key_to_s = convert_string_subclass(key);
+ }
+ break;
+ case T_SYMBOL:
+ if (RB_UNLIKELY(arg->first_key_type != T_SYMBOL)) {
+ json_inspect_hash_with_mixed_keys(arg);
+ }
+
+ key_to_s = rb_sym2str(key);
+ break;
+ default:
+ if (data->state->strict) {
+ if (RTEST(data->state->as_json) && !as_json_called) {
+ key = json_call_as_json(data->state, key, Qtrue);
+ key_type = rb_type(key);
+ as_json_called = true;
+ goto start;
+ } else {
+ raise_generator_error(key, "%"PRIsVALUE" not allowed as object key in JSON", CLASS_OF(key));
+ }
+ }
+ key_to_s = rb_convert_type(key, T_STRING, "String", "to_s");
+ break;
+ }
+
+ key_to_s = ensure_valid_encoding(data, key_to_s, as_json_called, true);
+
+ if (RB_LIKELY(RBASIC_CLASS(key_to_s) == rb_cString)) {
+ raw_generate_json_string(buffer, data, key_to_s);
+ } else {
+ generate_json(buffer, data, key_to_s);
+ }
+ if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, data->state->space_before);
+ fbuffer_append_char(buffer, ':');
+ if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, data->state->space);
+ generate_json(buffer, data, val);
+
+ return ST_CONTINUE;
+}
+
+static inline long increase_depth(struct generate_json_data *data)
+{
+ JSON_Generator_State *state = data->state;
+ long depth = ++data->depth;
+ if (RB_UNLIKELY(depth > state->max_nesting && state->max_nesting)) {
+ rb_raise(eNestingError, "nesting of %ld is too deep. Did you try to serialize objects with circular references?", --data->depth);
+ }
+ return depth;
+}
+
+static void generate_json_object(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ if (RB_UNLIKELY(data->state->sort_keys)) {
+ obj = rb_proc_call_with_block(data->state->sort_keys, 1, &obj, Qnil);
+ Check_Type(obj, T_HASH);
+ }
+
+ long depth = increase_depth(data);
+
+ if (RHASH_SIZE(obj) == 0) {
+ fbuffer_append(buffer, "{}", 2);
+ --data->depth;
+ return;
+ }
+
+ fbuffer_append_char(buffer, '{');
+
+ struct hash_foreach_arg arg = {
+ .hash = obj,
+ .data = data,
+ .first = true,
+ };
+ rb_hash_foreach(obj, json_object_i, (VALUE)&arg);
+
+ depth = --data->depth;
+ if (RB_UNLIKELY(data->state->object_nl)) {
+ fbuffer_append_str(buffer, data->state->object_nl);
+ if (RB_UNLIKELY(data->state->indent)) {
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
+ }
+ }
+ fbuffer_append_char(buffer, '}');
+}
+
+static void generate_json_array(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ long depth = increase_depth(data);
+
+ if (RARRAY_LEN(obj) == 0) {
+ fbuffer_append(buffer, "[]", 2);
+ --data->depth;
+ return;
+ }
+
+ fbuffer_append_char(buffer, '[');
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
+ for (int i = 0; i < RARRAY_LEN(obj); i++) {
+ if (i > 0) {
+ fbuffer_append_char(buffer, ',');
+ if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);
+ }
+ if (RB_UNLIKELY(data->state->indent)) {
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
+ }
+ generate_json(buffer, data, RARRAY_AREF(obj, i));
+ }
+ data->depth = --depth;
+ if (RB_UNLIKELY(data->state->array_nl)) {
+ fbuffer_append_str(buffer, data->state->array_nl);
+ if (RB_UNLIKELY(data->state->indent)) {
+ fbuffer_append_str_repeat(buffer, data->state->indent, depth);
+ }
+ }
+ fbuffer_append_char(buffer, ']');
+}
+
+static void generate_json_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ VALUE tmp;
+ if (rb_respond_to(obj, i_to_json)) {
+ tmp = json_call_to_json(data, obj);
+ Check_Type(tmp, T_STRING);
+ fbuffer_append_str(buffer, tmp);
+ } else {
+ tmp = rb_funcall(obj, i_to_s, 0);
+ Check_Type(tmp, T_STRING);
+ generate_json_string(buffer, data, tmp);
+ }
+}
+
+static inline void generate_json_symbol(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ if (data->state->strict) {
+ generate_json_string(buffer, data, rb_sym2str(obj));
+ } else {
+ generate_json_fallback(buffer, data, obj);
+ }
+}
+
+static void generate_json_null(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ fbuffer_append(buffer, "null", 4);
+}
+
+static void generate_json_false(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ fbuffer_append(buffer, "false", 5);
+}
+
+static void generate_json_true(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ fbuffer_append(buffer, "true", 4);
+}
+
+static void generate_json_fixnum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ fbuffer_append_long(buffer, FIX2LONG(obj));
+}
+
+static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
+ fbuffer_append_str(buffer, StringValue(tmp));
+}
+
+static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ double value = RFLOAT_VALUE(obj);
+ char allow_nan = data->state->allow_nan;
+ if (isinf(value) || isnan(value)) {
+ /* for NaN and Infinity values we either raise an error or rely on Float#to_s. */
+ if (!allow_nan) {
+ if (data->state->strict && data->state->as_json) {
+ VALUE casted_obj = json_call_as_json(data->state, obj, Qfalse);
+ if (casted_obj != obj) {
+ increase_depth(data);
+ generate_json(buffer, data, casted_obj);
+ data->depth--;
+ return;
+ }
+ }
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", rb_funcall(obj, i_to_s, 0));
+ }
+
+ VALUE tmp = rb_funcall(obj, i_to_s, 0);
+ fbuffer_append_str(buffer, tmp);
+ return;
+ }
+
+ /* This implementation writes directly into the buffer. We reserve
+ * the 32 characters that fpconv_dtoa states as its maximum.
+ */
+ fbuffer_inc_capa(buffer, 32);
+ char* d = buffer->ptr + buffer->len;
+ int len = fpconv_dtoa(value, d);
+ /* fpconv_dtoa converts a float to its shortest string representation,
+ * but it adds a ".0" if this is a plain integer.
+ */
+ fbuffer_consumed(buffer, len);
+}
+
+static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ VALUE fragment = RSTRUCT_GET(obj, 0);
+ Check_Type(fragment, T_STRING);
+ fbuffer_append_str(buffer, fragment);
+}
+
+static inline void generate_json_general(FBuffer *buffer, struct generate_json_data *data, VALUE obj, bool fallback)
+{
+ bool as_json_called = false;
+start:
+ if (obj == Qnil) {
+ generate_json_null(buffer, data, obj);
+ } else if (obj == Qfalse) {
+ generate_json_false(buffer, data, obj);
+ } else if (obj == Qtrue) {
+ generate_json_true(buffer, data, obj);
+ } else if (RB_SPECIAL_CONST_P(obj)) {
+ if (RB_FIXNUM_P(obj)) {
+ generate_json_fixnum(buffer, data, obj);
+ } else if (RB_FLONUM_P(obj)) {
+ generate_json_float(buffer, data, obj);
+ } else if (RB_STATIC_SYM_P(obj)) {
+ generate_json_symbol(buffer, data, obj);
+ } else {
+ goto general;
+ }
+ } else {
+ VALUE klass = RBASIC_CLASS(obj);
+ switch (RB_BUILTIN_TYPE(obj)) {
+ case T_BIGNUM:
+ generate_json_bignum(buffer, data, obj);
+ break;
+ case T_HASH:
+ if (fallback && klass != rb_cHash) goto general;
+ generate_json_object(buffer, data, obj);
+ break;
+ case T_ARRAY:
+ if (fallback && klass != rb_cArray) goto general;
+ generate_json_array(buffer, data, obj);
+ break;
+ case T_STRING:
+ if (fallback && klass != rb_cString) goto general;
+
+ if (RB_LIKELY(valid_json_string_p(obj))) {
+ raw_generate_json_string(buffer, data, obj);
+ } else if (as_json_called) {
+ raise_generator_error(obj, "source sequence is illegal/malformed utf-8");
+ } else {
+ obj = ensure_valid_encoding(data, obj, false, false);
+ as_json_called = true;
+ goto start;
+ }
+ break;
+ case T_SYMBOL:
+ generate_json_symbol(buffer, data, obj);
+ break;
+ case T_FLOAT:
+ if (fallback && klass != rb_cFloat) goto general;
+ generate_json_float(buffer, data, obj);
+ break;
+ case T_STRUCT:
+ if (klass != cFragment) goto general;
+ generate_json_fragment(buffer, data, obj);
+ break;
+ default:
+ general:
+ if (data->state->strict) {
+ if (RTEST(data->state->as_json) && !as_json_called) {
+ obj = json_call_as_json(data->state, obj, Qfalse);
+ as_json_called = true;
+ goto start;
+ } else {
+ raise_generator_error(obj, "%"PRIsVALUE" not allowed in JSON", CLASS_OF(obj));
+ }
+ } else {
+ generate_json_fallback(buffer, data, obj);
+ }
+ }
+ }
+}
+
+static void generate_json(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ generate_json_general(buffer, data, obj, true);
+}
+
+static void generate_json_no_fallback(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
+{
+ generate_json_general(buffer, data, obj, false);
+}
+
+static VALUE generate_json_try(VALUE d)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+
+ data->func(data->buffer, data, data->obj);
+
+ return fbuffer_finalize(data->buffer);
+}
+
+static VALUE generate_json_ensure(VALUE d)
+{
+ struct generate_json_data *data = (struct generate_json_data *)d;
+ fbuffer_free(data->buffer);
+
+ return Qundef;
+}
+
+static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_func func, VALUE io)
+{
+ GET_STATE(self);
+
+ char stack_buffer[FBUFFER_STACK_SIZE];
+ FBuffer buffer = { 0 };
+ fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
+
+ struct generate_json_data data = {
+ .buffer = &buffer,
+ .vstate = Qfalse, // don't use self as it may be frozen and its depth is mutated when calling to_json
+ .state = state,
+ .depth = state->depth,
+ .obj = obj,
+ .func = func
+ };
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
+}
+
+/* call-seq:
+ * generate(obj) -> String
+ * generate(obj, anIO) -> anIO
+ *
+ * Generates a valid JSON document from object +obj+ and returns the
+ * result. If no valid JSON document can be created this method raises a
+ * GeneratorError exception.
+ */
+static VALUE cState_generate(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 2);
+ VALUE obj = argv[0];
+ VALUE io = argc > 1 ? argv[1] : Qnil;
+ return cState_partial_generate(self, obj, generate_json, io);
+}
+
+/* :nodoc: */
+static VALUE cState_generate_no_fallback(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 1, 2);
+ VALUE obj = argv[0];
+ VALUE io = argc > 1 ? argv[1] : Qnil;
+ return cState_partial_generate(self, obj, generate_json_no_fallback, io);
+}
+
+static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
+{
+ rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
+ return self;
+}
+
+/*
+ * call-seq: initialize_copy(orig)
+ *
+ * Initializes this object from orig if it can be duplicated/cloned and returns
+ * it.
+*/
+static VALUE cState_init_copy(VALUE obj, VALUE orig)
+{
+ JSON_Generator_State *objState, *origState;
+
+ if (obj == orig) return obj;
+ GET_STATE_TO(obj, objState);
+ GET_STATE_TO(orig, origState);
+ if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
+
+ MEMCPY(objState, origState, JSON_Generator_State, 1);
+
+ RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
+ RB_OBJ_WRITTEN(obj, Qundef, objState->sort_keys);
+
+ return obj;
+}
+
+/*
+ * call-seq: from_state(opts)
+ *
+ * Creates a State object from _opts_, which ought to be Hash to create a
+ * new State instance configured by _opts_, something else to create an
+ * unconfigured instance. If _opts_ is a State object, it is just returned.
+ */
+static VALUE cState_from_state_s(VALUE self, VALUE opts)
+{
+ if (rb_obj_is_kind_of(opts, self)) {
+ return opts;
+ } else if (rb_obj_is_kind_of(opts, rb_cHash)) {
+ return rb_funcall(self, i_new, 1, opts);
+ } else {
+ return rb_class_new_instance(0, NULL, cState);
+ }
+}
+
+/*
+ * call-seq: indent()
+ *
+ * Returns the string that is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent(VALUE self)
+{
+ GET_STATE(self);
+ return state->indent ? state->indent : rb_str_freeze(rb_utf8_str_new("", 0));
+}
+
+static VALUE string_config(VALUE config)
+{
+ if (RTEST(config)) {
+ Check_Type(config, T_STRING);
+ if (RSTRING_LEN(config)) {
+ return rb_str_new_frozen(config);
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq: indent=(indent)
+ *
+ * Sets the string that is used to indent levels in the JSON text.
+ */
+static VALUE cState_indent_set(VALUE self, VALUE indent)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->indent, string_config(indent));
+ return Qnil;
+}
+
+/*
+ * call-seq: space()
+ *
+ * Returns the string that is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space(VALUE self)
+{
+ GET_STATE(self);
+ return state->space ? state->space : rb_str_freeze(rb_utf8_str_new("", 0));
+}
+
+/*
+ * call-seq: space=(space)
+ *
+ * Sets _space_ to the string that is used to insert a space between the tokens in a JSON
+ * string.
+ */
+static VALUE cState_space_set(VALUE self, VALUE space)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->space, string_config(space));
+ return Qnil;
+}
+
+/*
+ * call-seq: space_before()
+ *
+ * Returns the string that is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before(VALUE self)
+{
+ GET_STATE(self);
+ return state->space_before ? state->space_before : rb_str_freeze(rb_utf8_str_new("", 0));
+}
+
+/*
+ * call-seq: space_before=(space_before)
+ *
+ * Sets the string that is used to insert a space before the ':' in JSON objects.
+ */
+static VALUE cState_space_before_set(VALUE self, VALUE space_before)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->space_before, string_config(space_before));
+ return Qnil;
+}
+
+/*
+ * call-seq: object_nl()
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->object_nl ? state->object_nl : rb_str_freeze(rb_utf8_str_new("", 0));
+}
+
+/*
+ * call-seq: object_nl=(object_nl)
+ *
+ * This string is put at the end of a line that holds a JSON object (or
+ * Hash).
+ */
+static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->object_nl, string_config(object_nl));
+ return Qnil;
+}
+
+/*
+ * call-seq: array_nl()
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl(VALUE self)
+{
+ GET_STATE(self);
+ return state->array_nl ? state->array_nl : rb_str_freeze(rb_utf8_str_new("", 0));
+}
+
+/*
+ * call-seq: array_nl=(array_nl)
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->array_nl, string_config(array_nl));
+ return Qnil;
+}
+
+/*
+ * call-seq: as_json()
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_as_json(VALUE self)
+{
+ GET_STATE(self);
+ return state->as_json;
+}
+
+/*
+ * call-seq: as_json=(as_json)
+ *
+ * This string is put at the end of a line that holds a JSON array.
+ */
+static VALUE cState_as_json_set(VALUE self, VALUE as_json)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
+ return Qnil;
+}
+
+/*
+* call-seq: check_circular?
+*
+* Returns true, if circular data structures should be checked,
+* otherwise returns false.
+*/
+static VALUE cState_check_circular_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->max_nesting ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: max_nesting
+ *
+ * This integer returns the maximum level of data structure nesting in
+ * the generated JSON, max_nesting = 0 if no maximum is checked.
+ */
+static VALUE cState_max_nesting(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->max_nesting);
+}
+
+static long long_config(VALUE num)
+{
+ return RTEST(num) ? NUM2LONG(num) : 0;
+}
+
+// depth must never be negative; reject early with a clear error.
+static long depth_config(VALUE num)
+{
+ if (!RTEST(num)) return 0;
+ long d = NUM2LONG(num);
+ if (RB_UNLIKELY(d < 0)) {
+ rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
+ }
+ if (RB_UNLIKELY(d > INT_MAX)) {
+ rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
+ }
+ return d;
+}
+
+/*
+ * call-seq: max_nesting=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_max_nesting_set(VALUE self, VALUE depth)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->max_nesting = long_config(depth);
+ return Qnil;
+}
+
+/*
+ * call-seq: script_safe
+ *
+ * If this boolean is true, the forward slashes will be escaped in
+ * the json output.
+ */
+static VALUE cState_script_safe(VALUE self)
+{
+ GET_STATE(self);
+ return state->script_safe ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: script_safe=(enable)
+ *
+ * This sets whether or not the forward slashes will be escaped in
+ * the json output.
+ */
+static VALUE cState_script_safe_set(VALUE self, VALUE enable)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->script_safe = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: strict
+ *
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict(VALUE self)
+{
+ GET_STATE(self);
+ return state->strict ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: strict=(enable)
+ *
+ * This sets whether or not to serialize types unsupported by the
+ * JSON format as strings.
+ * If this boolean is false, types unsupported by the JSON format will
+ * be serialized as strings.
+ * If this boolean is true, types unsupported by the JSON format will
+ * raise a JSON::GeneratorError.
+ */
+static VALUE cState_strict_set(VALUE self, VALUE enable)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->strict = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: allow_nan?
+ *
+ * Returns true, if NaN, Infinity, and -Infinity should be generated, otherwise
+ * returns false.
+ */
+static VALUE cState_allow_nan_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->allow_nan ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: allow_nan=(enable)
+ *
+ * This sets whether or not to serialize NaN, Infinity, and -Infinity
+ */
+static VALUE cState_allow_nan_set(VALUE self, VALUE enable)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->allow_nan = RTEST(enable);
+ return Qnil;
+}
+
+/*
+ * call-seq: ascii_only?
+ *
+ * Returns true, if only ASCII characters should be generated. Otherwise
+ * returns false.
+ */
+static VALUE cState_ascii_only_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->ascii_only ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: ascii_only=(enable)
+ *
+ * This sets whether only ASCII characters should be generated.
+ */
+static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->ascii_only = RTEST(enable);
+ return Qnil;
+}
+
+static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
+{
+ if (!rb_obj_is_proc(proc)) {
+ rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
+ }
+ return default_sort_keys_proc = proc;
+}
+
+static VALUE normalize_sort_keys(VALUE value)
+{
+ if (rb_obj_is_proc(value)) {
+ return value;
+ } else if (value == Qtrue) {
+ return default_sort_keys_proc;
+ } else if (RTEST(value)) {
+ rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
+ } else {
+ return Qfalse;
+ }
+}
+
+/*
+ * call-seq: sort_keys
+ *
+ * Get the value of sort_keys.
+ */
+static VALUE cState_sort_keys_p(VALUE self)
+{
+ GET_STATE(self);
+ return state->sort_keys;
+}
+
+/*
+ * call-seq: sort_keys=(value)
+ *
+ * value is a boolean or a proc. If the value is the boolean true, object keys
+ * will be sorted lexicographically in ascending order.
+ *
+ * If the value is a proc, it receives the entire Hash and must return a Hash
+ * with its pairs in the desired order, allowing for arbitrary sorting.
+ */
+static VALUE cState_sort_keys_set(VALUE self, VALUE value)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ RB_OBJ_WRITE(self, &state->sort_keys, normalize_sort_keys(value));
+ return Qnil;
+}
+
+static VALUE cState_allow_duplicate_key_p(VALUE self)
+{
+ GET_STATE(self);
+ switch (state->on_duplicate_key) {
+ case JSON_IGNORE:
+ return Qtrue;
+ case JSON_DEPRECATED:
+ return Qnil;
+ default:
+ return Qfalse;
+ }
+}
+
+/*
+ * call-seq: depth
+ *
+ * This integer returns the current depth of data structure nesting.
+ */
+static VALUE cState_depth(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->depth);
+}
+
+/*
+ * call-seq: depth=(depth)
+ *
+ * This sets the maximum level of data structure nesting in the generated JSON
+ * to the integer depth, max_nesting = 0 if no maximum should be checked.
+ */
+static VALUE cState_depth_set(VALUE self, VALUE depth)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ state->depth = depth_config(depth);
+ return Qnil;
+}
+
+/*
+ * call-seq: buffer_initial_length
+ *
+ * This integer returns the current initial length of the buffer.
+ */
+static VALUE cState_buffer_initial_length(VALUE self)
+{
+ GET_STATE(self);
+ return LONG2FIX(state->buffer_initial_length);
+}
+
+static void buffer_initial_length_set(JSON_Generator_State *state, VALUE buffer_initial_length)
+{
+ Check_Type(buffer_initial_length, T_FIXNUM);
+ long initial_length = FIX2LONG(buffer_initial_length);
+ if (initial_length > 0) {
+ state->buffer_initial_length = initial_length;
+ }
+}
+
+/*
+ * call-seq: buffer_initial_length=(length)
+ *
+ * This sets the initial length of the buffer to +length+, if +length+ > 0,
+ * otherwise its value isn't changed.
+ */
+static VALUE cState_buffer_initial_length_set(VALUE self, VALUE buffer_initial_length)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ buffer_initial_length_set(state, buffer_initial_length);
+ return Qnil;
+}
+
+struct configure_state_data {
+ JSON_Generator_State *state;
+ VALUE vstate; // Ruby object that owns the state, or Qfalse if stack-allocated
+};
+
+static inline void state_write_value(struct configure_state_data *data, VALUE *field, VALUE value)
+{
+ if (RTEST(data->vstate)) {
+ RB_OBJ_WRITE(data->vstate, field, value);
+ } else {
+ *field = value;
+ }
+}
+
+static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
+{
+ struct configure_state_data *data = (struct configure_state_data *)_arg;
+ JSON_Generator_State *state = data->state;
+
+ if (key == sym_indent) { state_write_value(data, &state->indent, string_config(val)); }
+ else if (key == sym_space) { state_write_value(data, &state->space, string_config(val)); }
+ else if (key == sym_space_before) { state_write_value(data, &state->space_before, string_config(val)); }
+ else if (key == sym_object_nl) { state_write_value(data, &state->object_nl, string_config(val)); }
+ else if (key == sym_array_nl) { state_write_value(data, &state->array_nl, string_config(val)); }
+ else if (key == sym_max_nesting) { state->max_nesting = long_config(val); }
+ else if (key == sym_allow_nan) { state->allow_nan = RTEST(val); }
+ else if (key == sym_ascii_only) { state->ascii_only = RTEST(val); }
+ else if (key == sym_depth) { state->depth = depth_config(val); }
+ else if (key == sym_buffer_initial_length) { buffer_initial_length_set(state, val); }
+ else if (key == sym_script_safe) { state->script_safe = RTEST(val); }
+ else if (key == sym_escape_slash) { state->script_safe = RTEST(val); }
+ else if (key == sym_strict) { state->strict = RTEST(val); }
+ else if (key == sym_allow_duplicate_key) { state->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
+ else if (key == sym_as_json) {
+ VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
+ state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
+ state_write_value(data, &state->as_json, proc);
+ }
+ else if (key == sym_sort_keys) {
+ state_write_value(data, &state->sort_keys, normalize_sort_keys(val));
+ }
+ return ST_CONTINUE;
+}
+
+static void configure_state(JSON_Generator_State *state, VALUE vstate, VALUE config)
+{
+ if (!RTEST(config)) return;
+
+ Check_Type(config, T_HASH);
+
+ if (!RHASH_SIZE(config)) return;
+
+ struct configure_state_data data = {
+ .state = state,
+ .vstate = vstate
+ };
+
+ // We assume in most cases few keys are set so it's faster to go over
+ // the provided keys than to check all possible keys.
+ rb_hash_foreach(config, configure_state_i, (VALUE)&data);
+}
+
+static VALUE cState_configure(VALUE self, VALUE opts)
+{
+ rb_check_frozen(self);
+ GET_STATE(self);
+ configure_state(state, self, opts);
+ return self;
+}
+
+static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, generator_func func)
+{
+ JSON_Generator_State state = {0};
+ state_init(&state);
+ configure_state(&state, Qfalse, opts);
+
+ char stack_buffer[FBUFFER_STACK_SIZE];
+ FBuffer buffer = { 0 };
+ fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
+
+ struct generate_json_data data = {
+ .buffer = &buffer,
+ .vstate = Qfalse,
+ .state = &state,
+ .depth = state.depth,
+ .obj = obj,
+ .func = func,
+ };
+ return rb_ensure(generate_json_try, (VALUE)&data, generate_json_ensure, (VALUE)&data);
+}
+
+static VALUE cState_m_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io)
+{
+ return cState_m_do_generate(klass, obj, opts, io, generate_json);
+}
+
+static VALUE cState_m_generate_no_fallback(VALUE klass, VALUE obj, VALUE opts, VALUE io)
+{
+ return cState_m_do_generate(klass, obj, opts, io, generate_json_no_fallback);
+}
+
+void Init_generator(void)
+{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+#undef rb_intern
+ rb_require("json/common");
+
+ mJSON = rb_define_module("JSON");
+
+ rb_global_variable(&cFragment);
+ cFragment = rb_const_get(mJSON, rb_intern("Fragment"));
+
+ VALUE mExt = rb_define_module_under(mJSON, "Ext");
+ VALUE mGenerator = rb_define_module_under(mExt, "Generator");
+
+ rb_global_variable(&default_sort_keys_proc);
+
+ rb_global_variable(&eGeneratorError);
+ eGeneratorError = rb_path2class("JSON::GeneratorError");
+
+ rb_global_variable(&eNestingError);
+ eNestingError = rb_path2class("JSON::NestingError");
+
+ cState = rb_define_class_under(mGenerator, "State", rb_cObject);
+ rb_define_alloc_func(cState, cState_s_allocate);
+ rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
+ rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
+
+ rb_define_method(cState, "initialize", cState_initialize, -1);
+ rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
+ rb_define_private_method(cState, "_configure", cState_configure, 1);
+
+ rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
+ rb_define_method(cState, "indent", cState_indent, 0);
+ rb_define_method(cState, "indent=", cState_indent_set, 1);
+ rb_define_method(cState, "space", cState_space, 0);
+ rb_define_method(cState, "space=", cState_space_set, 1);
+ rb_define_method(cState, "space_before", cState_space_before, 0);
+ rb_define_method(cState, "space_before=", cState_space_before_set, 1);
+ rb_define_method(cState, "object_nl", cState_object_nl, 0);
+ rb_define_method(cState, "object_nl=", cState_object_nl_set, 1);
+ rb_define_method(cState, "array_nl", cState_array_nl, 0);
+ rb_define_method(cState, "array_nl=", cState_array_nl_set, 1);
+ rb_define_method(cState, "as_json", cState_as_json, 0);
+ rb_define_method(cState, "as_json=", cState_as_json_set, 1);
+ rb_define_method(cState, "max_nesting", cState_max_nesting, 0);
+ rb_define_method(cState, "max_nesting=", cState_max_nesting_set, 1);
+ rb_define_method(cState, "script_safe", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe?", cState_script_safe, 0);
+ rb_define_method(cState, "script_safe=", cState_script_safe_set, 1);
+ rb_define_alias(cState, "escape_slash", "script_safe");
+ rb_define_alias(cState, "escape_slash?", "script_safe?");
+ rb_define_alias(cState, "escape_slash=", "script_safe=");
+ rb_define_method(cState, "strict", cState_strict, 0);
+ rb_define_method(cState, "strict?", cState_strict, 0);
+ rb_define_method(cState, "strict=", cState_strict_set, 1);
+ rb_define_method(cState, "check_circular?", cState_check_circular_p, 0);
+ rb_define_method(cState, "allow_nan?", cState_allow_nan_p, 0);
+ rb_define_method(cState, "allow_nan=", cState_allow_nan_set, 1);
+ rb_define_method(cState, "ascii_only?", cState_ascii_only_p, 0);
+ rb_define_method(cState, "ascii_only=", cState_ascii_only_set, 1);
+ rb_define_method(cState, "depth", cState_depth, 0);
+ rb_define_method(cState, "depth=", cState_depth_set, 1);
+ rb_define_method(cState, "buffer_initial_length", cState_buffer_initial_length, 0);
+ rb_define_method(cState, "buffer_initial_length=", cState_buffer_initial_length_set, 1);
+ rb_define_method(cState, "generate", cState_generate, -1);
+ rb_define_method(cState, "_generate_no_fallback", cState_generate_no_fallback, -1);
+ rb_define_method(cState, "sort_keys", cState_sort_keys_p, 0);
+ rb_define_method(cState, "sort_keys=", cState_sort_keys_set, 1);
+
+ rb_define_private_method(cState, "allow_duplicate_key?", cState_allow_duplicate_key_p, 0);
+
+ rb_define_singleton_method(cState, "generate", cState_m_generate, 3);
+ rb_define_singleton_method(cState, "_generate_no_fallback", cState_m_generate_no_fallback, 3);
+
+ rb_global_variable(&Encoding_UTF_8);
+ Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
+
+ i_to_s = rb_intern("to_s");
+ i_to_json = rb_intern("to_json");
+ i_new = rb_intern("new");
+ i_encode = rb_intern("encode");
+
+ sym_indent = ID2SYM(rb_intern("indent"));
+ sym_space = ID2SYM(rb_intern("space"));
+ sym_space_before = ID2SYM(rb_intern("space_before"));
+ sym_object_nl = ID2SYM(rb_intern("object_nl"));
+ sym_array_nl = ID2SYM(rb_intern("array_nl"));
+ sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
+ sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
+ sym_ascii_only = ID2SYM(rb_intern("ascii_only"));
+ sym_depth = ID2SYM(rb_intern("depth"));
+ sym_buffer_initial_length = ID2SYM(rb_intern("buffer_initial_length"));
+ sym_script_safe = ID2SYM(rb_intern("script_safe"));
+ sym_escape_slash = ID2SYM(rb_intern("escape_slash"));
+ sym_strict = ID2SYM(rb_intern("strict"));
+ sym_as_json = ID2SYM(rb_intern("as_json"));
+ sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
+ sym_sort_keys = ID2SYM(rb_intern("sort_keys"));
+
+ usascii_encindex = rb_usascii_encindex();
+ utf8_encindex = rb_utf8_encindex();
+ binary_encindex = rb_ascii8bit_encindex();
+
+ rb_require("json/ext/generator/state");
+
+ simd_impl = find_simd_implementation();
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/json.h b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/json.h
new file mode 100644
index 0000000..afddbe2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/json.h
@@ -0,0 +1,183 @@
+#ifndef _JSON_H_
+#define _JSON_H_
+
+#include "ruby.h"
+#include "ruby/encoding.h"
+#include <stdint.h>
+
+#ifndef RBIMPL_ASSERT_OR_ASSUME
+# define RBIMPL_ASSERT_OR_ASSUME(x)
+#endif
+
+#if defined(RUBY_DEBUG) && RUBY_DEBUG
+# define JSON_ASSERT RUBY_ASSERT
+# ifndef JSON_DEBUG
+# define JSON_DEBUG 1
+# endif
+#else
+# ifdef JSON_DEBUG
+# include <assert.h>
+# define JSON_ASSERT(x) assert(x)
+# else
+# define JSON_ASSERT(x)
+# endif
+#endif
+
+#ifdef JSON_DEBUG
+# define JSON_UNREACHABLE_RETURN(val) rb_bug("Unreachable")
+#else
+# define JSON_UNREACHABLE_RETURN UNREACHABLE_RETURN
+#endif
+
+/* shims */
+
+#ifndef RUBY_TYPED_THREAD_SAFE_FREE
+#define RUBY_TYPED_THREAD_SAFE_FREE RUBY_TYPED_FREE_IMMEDIATELY
+#endif
+
+#ifndef UNDEF_P
+#define UNDEF_P(val) (val == Qundef)
+#endif
+
+#if SIZEOF_UINT64_T == SIZEOF_LONG_LONG
+# define INT64T2NUM(x) LL2NUM(x)
+# define UINT64T2NUM(x) ULL2NUM(x)
+#elif SIZEOF_UINT64_T == SIZEOF_LONG
+# define INT64T2NUM(x) LONG2NUM(x)
+# define UINT64T2NUM(x) ULONG2NUM(x)
+#else
+# error No uint64_t conversion
+#endif
+
+/* This is the fallback definition from Ruby 3.4 */
+#ifndef RBIMPL_STDBOOL_H
+#if defined(__cplusplus)
+# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
+# include <cstdbool>
+# endif
+#elif defined(HAVE_STDBOOL_H)
+# include <stdbool.h>
+#elif !defined(HAVE__BOOL)
+typedef unsigned char _Bool;
+# define bool _Bool
+# define true ((_Bool)+1)
+# define false ((_Bool)+0)
+# define __bool_true_false_are_defined
+#endif
+#endif
+
+#ifndef HAVE_RUBY_XFREE_SIZED
+static inline void ruby_xfree_sized(void *ptr, size_t oldsize)
+{
+ ruby_xfree(ptr);
+}
+
+static inline void *ruby_xrealloc2_sized(void *ptr, size_t new_elems, size_t elem_size, size_t old_elems)
+{
+ return ruby_xrealloc2(ptr, new_elems, elem_size);
+}
+#endif
+
+# define JSON_SIZED_REALLOC_N(v, T, m, n) \
+ ((v) = (T *)ruby_xrealloc2_sized((void *)(v), (m), sizeof(T), (n)))
+
+# define JSON_SIZED_FREE(v) ruby_xfree_sized((void *)(v), sizeof(*(v)))
+# define JSON_SIZED_FREE_N(v, n) ruby_xfree_sized((void *)(v), sizeof(*(v)) * (n))
+
+#ifndef HAVE_RB_EXT_RACTOR_SAFE
+# undef RUBY_TYPED_FROZEN_SHAREABLE
+# define RUBY_TYPED_FROZEN_SHAREABLE 0
+#endif
+
+#ifdef RUBY_TYPED_EMBEDDABLE
+# define HAVE_RUBY_TYPED_EMBEDDABLE 1
+#else
+# ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE
+# define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE
+# define HAVE_RUBY_TYPED_EMBEDDABLE 1
+# else
+# define RUBY_TYPED_EMBEDDABLE 0
+# endif
+#endif
+
+#ifndef NORETURN
+#if defined(__has_attribute) && __has_attribute(noreturn)
+#define NORETURN(x) __attribute__((noreturn)) x
+#else
+#define NORETURN(x) x
+#endif
+#endif
+
+#ifndef NOINLINE
+#if defined(__has_attribute) && __has_attribute(noinline)
+#define NOINLINE(x) __attribute__((noinline)) x
+#else
+#define NOINLINE(x) x
+#endif
+#endif
+
+#ifndef ALWAYS_INLINE
+#if defined(__has_attribute) && __has_attribute(always_inline)
+#define ALWAYS_INLINE(x) inline __attribute__((always_inline)) x
+#else
+#define ALWAYS_INLINE(x) inline x
+#endif
+#endif
+
+#ifndef RB_UNLIKELY
+#define RB_UNLIKELY(expr) expr
+#endif
+
+#ifndef RB_LIKELY
+#define RB_LIKELY(expr) expr
+#endif
+
+#ifndef MAYBE_UNUSED
+# define MAYBE_UNUSED(x) x
+#endif
+
+#ifdef RUBY_DEBUG
+#ifndef JSON_DEBUG
+#define JSON_DEBUG RUBY_DEBUG
+#endif
+#endif
+
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && INTPTR_MAX == INT64_MAX
+#define JSON_CPU_LITTLE_ENDIAN_64BITS 1
+#else
+#define JSON_CPU_LITTLE_ENDIAN_64BITS 0
+#endif
+
+#ifdef JSON_TRUFFLERUBY_RB_CATCH_BUG
+
+#undef RB_BLOCK_CALL_FUNC_ARGLIST
+#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, func_args) VALUE func_args
+
+NORETURN(static inline) void json_rb_throw_obj(VALUE tag, VALUE obj)
+{
+ VALUE exc = rb_exc_new_str(rb_eException, rb_utf8_str_new_cstr("throw_workaround"));
+ rb_ivar_set(exc, rb_intern("@throw_tag"), tag);
+ rb_ivar_set(exc, rb_intern("@throw_obj"), obj);
+ rb_exc_raise(exc);
+}
+#define rb_throw_obj json_rb_throw_obj
+
+static inline VALUE json_rb_catch_obj(VALUE tag, VALUE (*func)(VALUE args), VALUE func_args)
+{
+ int status;
+ VALUE result = rb_protect(func, func_args, &status);
+ if (status) {
+ VALUE exc = rb_errinfo();
+ if (tag == rb_ivar_get(exc, rb_intern("@throw_tag"))) {
+ rb_set_errinfo(Qnil);
+ return rb_ivar_get(exc, rb_intern("@throw_obj"));
+ }
+ rb_jump_tag(status);
+ }
+ return result;
+}
+#define rb_catch_obj json_rb_catch_obj
+
+#endif // JSON_TRUFFLERUBY_RB_CATCH_BUG
+
+#endif // _JSON_H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/Makefile b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/Makefile
new file mode 100644
index 0000000..ab8cb53
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/Makefile
@@ -0,0 +1,273 @@
+
+SHELL = /bin/sh
+
+# V=0 quiet, V=1 verbose. other values don't work.
+V = 0
+V0 = $(V:0=)
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@ :)
+ECHO = $(ECHO1:0=@ echo)
+NULLCMD = :
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0
+hdrdir = $(topdir)
+arch_hdrdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24
+PATH_SEPARATOR = :
+VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
+prefix = $(DESTDIR)/Users/ben/.rubies/ruby-3.4.1
+rubysitearchprefix = $(rubylibprefix)/$(sitearch)
+rubyarchprefix = $(rubylibprefix)/$(arch)
+rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
+exec_prefix = $(prefix)
+vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
+sitearchhdrdir = $(sitehdrdir)/$(sitearch)
+rubyarchhdrdir = $(rubyhdrdir)/$(arch)
+vendorhdrdir = $(rubyhdrdir)/vendor_ruby
+sitehdrdir = $(rubyhdrdir)/site_ruby
+rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
+vendorarchdir = $(vendorlibdir)/$(sitearch)
+vendorlibdir = $(vendordir)/$(ruby_version)
+vendordir = $(rubylibprefix)/vendor_ruby
+sitearchdir = $(sitelibdir)/$(sitearch)
+sitelibdir = $(sitedir)/$(ruby_version)
+sitedir = $(rubylibprefix)/site_ruby
+rubyarchdir = $(rubylibdir)/$(arch)
+rubylibdir = $(rubylibprefix)/$(ruby_version)
+sitearchincludedir = $(includedir)/$(sitearch)
+archincludedir = $(includedir)/$(arch)
+sitearchlibdir = $(libdir)/$(sitearch)
+archlibdir = $(libdir)/$(arch)
+ridir = $(datarootdir)/$(RI_BASE_NAME)
+modular_gc_dir = $(DESTDIR)
+mandir = $(datarootdir)/man
+localedir = $(datarootdir)/locale
+libdir = $(exec_prefix)/lib
+psdir = $(docdir)
+pdfdir = $(docdir)
+dvidir = $(docdir)
+htmldir = $(docdir)
+infodir = $(datarootdir)/info
+docdir = $(datarootdir)/doc/$(PACKAGE)
+oldincludedir = $(DESTDIR)/usr/include
+includedir = $(SDKROOT)$(prefix)/include
+runstatedir = $(localstatedir)/run
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+sysconfdir = $(prefix)/etc
+datadir = $(datarootdir)
+datarootdir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+sbindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/bin
+archdir = $(rubyarchdir)
+
+
+CC_WRAPPER =
+CC = clang
+CXX = clang++ -std=gnu++11
+LIBRUBY = $(LIBRUBY_A)
+LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
+LIBRUBYARG_SHARED =
+LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation $(MAINLIBS)
+empty =
+OUTFLAG = -o $(empty)
+COUTFLAG = -o $(empty)
+CSRCFLAG = $(empty)
+
+RUBY_EXTCONF_H =
+cflags = $(hardenflags) -fdeclspec $(optflags) $(debugflags) $(warnflags)
+cxxflags =
+optflags = -O3 -fno-fast-math
+debugflags = -ggdb3
+warnflags = -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef
+cppflags =
+CCDLFLAGS = -fno-common
+CFLAGS = $(CCDLFLAGS) $(cflags) -pipe $(ARCH_FLAG)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS = -DHAVE_RB_ENC_INTERNED_STR -DHAVE_RB_STR_TO_INTERNED_STR -DHAVE_RB_HASH_NEW_CAPA -DHAVE_RB_HASH_BULK_INSERT -DHAVE_CONST_RUBY_TYPED_EMBEDDABLE -DHAVE_ARM_NEON_H -DJSON_ENABLE_SIMD -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/libyaml/include -I/opt/homebrew/opt/gdbm/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
+CXXFLAGS = $(CCDLFLAGS) -fdeclspec $(ARCH_FLAG)
+ldflags = -L. -fstack-protector-strong -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib
+dldflags = -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib -Wl,-undefined,dynamic_lookup -bundle_loader '$(BUILTRUBY)'
+ARCH_FLAG = -arch arm64
+DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
+LDSHARED = $(CC) -dynamic -bundle
+LDSHAREDXX = $(CXX) -dynamic -bundle
+POSTLINK = dsymutil $@ 2>/dev/null; { test -z '$(RUBY_CODESIGN)' || codesign -s '$(RUBY_CODESIGN)' $@; }
+AR = ar
+LD = ld
+EXEEXT =
+
+RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
+RUBY_SO_NAME = ruby.3.4
+RUBYW_INSTALL_NAME =
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+RUBYW_BASE_NAME = rubyw
+RUBY_BASE_NAME = ruby
+
+arch = arm64-darwin24
+sitearch = $(arch)
+ruby_version = 3.4.0
+ruby = $(bindir)/$(RUBY_BASE_NAME)
+RUBY = $(ruby)
+BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
+ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
+
+RM = rm -f
+RM_RF = rm -fr
+RMDIRS = rmdir -p
+MAKEDIRS = mkdir -p
+INSTALL = /usr/bin/install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+TOUCH = exit >
+
+#### End of system configuration section. ####
+
+preload =
+libpath = . $(libdir) /opt/homebrew/opt/readline/lib /opt/homebrew/opt/libyaml/lib /opt/homebrew/opt/gdbm/lib
+LIBPATH = -L. -L$(libdir) -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/gdbm/lib
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+DISTCLEANDIRS =
+
+extout =
+extout_prefix =
+target_prefix = /json/ext
+LOCAL_LIBS =
+LIBS = -lpthread
+ORIG_SRCS = parser.c
+SRCS = $(ORIG_SRCS)
+OBJS = parser.o
+HDRS =
+LOCAL_HDRS =
+TARGET = parser
+TARGET_NAME = parser
+TARGET_ENTRY = Init_$(TARGET_NAME)
+DLLIB = $(TARGET).bundle
+EXTSTATIC =
+STATIC_LIB =
+
+TIMESTAMP_DIR = .
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+HDRDIR = $(sitehdrdir)$(target_prefix)
+ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
+TARGET_SO_DIR =
+TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
+CLEANLIBS = $(TARGET_SO) $(TARGET_SO:=.dSYM)
+CLEANOBJS = $(OBJS) *.bak
+TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.json.-.ext.time
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-static clean-rb
+
+clean-static::
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-static clean-rb-default clean-rb
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
+
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+clean-static::
+ -$(Q)$(RM) $(STATIC_LIB)
+install-rb: pre-install-rb do-install-rb install-rb-default
+install-rb-default: pre-install-rb-default do-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+do-install-rb:
+do-install-rb-default:
+pre-install-rb-default:
+ @$(NULLCMD)
+$(TARGET_SO_DIR_TIMESTAMP):
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
+ $(Q) $(TOUCH) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
+
+.cc.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cc.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.mm.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.mm.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cxx.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cxx.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cpp.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cpp.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.c.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.c.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.m.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.m.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+$(TARGET_SO): $(OBJS) Makefile
+ $(ECHO) linking shared-object json/ext/$(DLLIB)
+ -$(Q)$(RM) $(@)
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+ $(Q) $(POSTLINK)
+
+
+
+$(OBJS): $(HDRS) $(ruby_headers)
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/extconf.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/extconf.rb
new file mode 100644
index 0000000..a0358cc
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/extconf.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+require 'mkmf'
+
+$defs << "-DJSON_DEBUG" if ENV.fetch("JSON_DEBUG", "0") != "0"
+
+if RUBY_ENGINE == 'truffleruby' && RUBY_ENGINE_VERSION < '40.0'
+ # Ref: https://github.com/truffleruby/truffleruby/issues/4329
+ # Ref: https://github.com/truffleruby/truffleruby/pull/4333
+ $defs << "-DJSON_TRUFFLERUBY_RB_CATCH_BUG"
+end
+
+have_func("rb_enc_interned_str", "ruby/encoding.h") # RUBY_VERSION >= 3.0
+have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
+have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
+have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
+have_func("ruby_xfree_sized", "ruby.h") # RUBY_VERSION >= 4.1
+
+if have_header("x86intrin.h")
+ have_func("_lzcnt_u64", "x86intrin.h")
+end
+
+if have_header("intrin.h")
+ have_func("__lzcnt64", "intrin.h")
+ have_func("_BitScanReverse64", "intrin.h")
+end
+
+if RUBY_ENGINE == "ruby"
+ have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
+end
+
+append_cflags("-std=c99")
+
+if enable_config('parser-use-simd', default=!ENV["JSON_DISABLE_SIMD"])
+ load __dir__ + "/../simd/conf.rb"
+end
+
+create_makefile 'json/ext/parser'
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/parser.c b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/parser.c
new file mode 100644
index 0000000..d1c90ff
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/parser/parser.c
@@ -0,0 +1,2917 @@
+#include "../json.h"
+#include "../vendor/fast_float_parser.h"
+#include "../simd/simd.h"
+
+static VALUE mJSON, eNestingError, eParserError, Encoding_UTF_8;
+static VALUE CNaN, CInfinity, CMinusInfinity, JSON_empty_string;
+
+static ID i_new, i_try_convert, i_encode, i_at_line, i_at_column;
+#ifndef HAVE_RB_STR_TO_INTERNED_STR
+static ID i_uminus;
+#endif
+
+static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_comments,
+ sym_allow_control_characters, sym_allow_invalid_escape, sym_symbolize_names,
+ sym_freeze, sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
+
+static int binary_encindex;
+static int utf8_encindex;
+
+#ifndef HAVE_RB_HASH_BULK_INSERT
+// For TruffleRuby
+static void
+rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash)
+{
+ long index = 0;
+ while (index < count) {
+ VALUE name = pairs[index++];
+ VALUE value = pairs[index++];
+ rb_hash_aset(hash, name, value);
+ }
+ RB_GC_GUARD(hash);
+}
+#endif
+
+#ifndef HAVE_RB_HASH_NEW_CAPA
+#define rb_hash_new_capa(n) rb_hash_new()
+#endif
+
+#ifndef HAVE_RB_STR_TO_INTERNED_STR
+static VALUE rb_str_to_interned_str(VALUE str)
+{
+ return rb_funcall(rb_str_freeze(str), i_uminus, 0);
+}
+#endif
+
+/* name cache */
+
+#include <string.h>
+#include <ctype.h>
+
+// Object names are likely to be repeated, and are frozen.
+// As such we can re-use them if we keep a cache of the ones we've seen so far,
+// and save much more expensive lookups into the global fstring table.
+// This cache implementation is deliberately simple, as we're optimizing for compactness,
+// to be able to fit safely on the stack.
+// As such, binary search into a sorted array gives a good tradeoff between compactness and
+// performance.
+#define JSON_RVALUE_CACHE_CAPA 63
+typedef struct rvalue_cache_struct {
+ int length;
+ VALUE entries[JSON_RVALUE_CACHE_CAPA];
+} rvalue_cache;
+
+static void rvalue_cache_mark(rvalue_cache *cache)
+{
+ for (int index = 0; index < cache->length; index++) {
+ rb_gc_mark_movable(cache->entries[index]);
+ }
+}
+
+static void rvalue_cache_compact(rvalue_cache *cache)
+{
+ for (int index = 0; index < cache->length; index++) {
+ cache->entries[index] = rb_gc_location(cache->entries[index]);
+ }
+}
+
+static rb_encoding *enc_utf8;
+
+#define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55
+
+static inline VALUE build_interned_string(const char *str, const long length)
+{
+# ifdef HAVE_RB_ENC_INTERNED_STR
+ return rb_enc_interned_str(str, length, enc_utf8);
+# else
+ VALUE rstring = rb_utf8_str_new(str, length);
+ return rb_funcall(rb_str_freeze(rstring), i_uminus, 0);
+# endif
+}
+
+static inline VALUE build_symbol(const char *str, const long length)
+{
+ return rb_str_intern(build_interned_string(str, length));
+}
+
+static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring)
+{
+ MEMMOVE(&cache->entries[index + 1], &cache->entries[index], VALUE, cache->length - index);
+ cache->length++;
+ cache->entries[index] = rstring;
+}
+
+#define rstring_cache_memcmp memcmp
+
+#if JSON_CPU_LITTLE_ENDIAN_64BITS
+#if __has_builtin(__builtin_bswap64)
+#undef rstring_cache_memcmp
+ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr, const long length)
+{
+ // The libc memcmp has numerous complex optimizations, but in this particular case,
+ // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to
+ // inline a simpler memcmp outperforms calling the libc version.
+ long i = 0;
+
+ for (; i + 8 <= length; i += 8) {
+ uint64_t a, b;
+ memcpy(&a, str + i, 8);
+ memcpy(&b, rptr + i, 8);
+ if (a != b) {
+ a = __builtin_bswap64(a);
+ b = __builtin_bswap64(b);
+ return (a < b) ? -1 : 1;
+ }
+ }
+
+ for (; i < length; i++) {
+ if (str[i] != rptr[i]) {
+ return (str[i] < rptr[i]) ? -1 : 1;
+ }
+ }
+
+ return 0;
+}
+#endif
+#endif
+
+ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
+{
+ const char *rstring_ptr;
+ long rstring_length;
+
+ RSTRING_GETMEM(rstring, rstring_ptr, rstring_length);
+
+ if (length == rstring_length) {
+ return rstring_cache_memcmp(str, rstring_ptr, length);
+ } else {
+ return (int)(length - rstring_length);
+ }
+}
+
+ALWAYS_INLINE(static) VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
+{
+ int low = 0;
+ int high = cache->length - 1;
+
+ while (low <= high) {
+ int mid = (high + low) >> 1;
+ VALUE entry = cache->entries[mid];
+ int cmp = rstring_cache_cmp(str, length, entry);
+
+ if (cmp == 0) {
+ return entry;
+ } else if (cmp > 0) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+
+ VALUE rstring = build_interned_string(str, length);
+
+ if (cache->length < JSON_RVALUE_CACHE_CAPA) {
+ rvalue_cache_insert_at(cache, low, rstring);
+ }
+ return rstring;
+}
+
+static VALUE rsymbol_cache_fetch(rvalue_cache *cache, const char *str, const long length)
+{
+ int low = 0;
+ int high = cache->length - 1;
+
+ while (low <= high) {
+ int mid = (high + low) >> 1;
+ VALUE entry = cache->entries[mid];
+ int cmp = rstring_cache_cmp(str, length, rb_sym2str(entry));
+
+ if (cmp == 0) {
+ return entry;
+ } else if (cmp > 0) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+
+ VALUE rsymbol = build_symbol(str, length);
+
+ if (cache->length < JSON_RVALUE_CACHE_CAPA) {
+ rvalue_cache_insert_at(cache, low, rsymbol);
+ }
+ return rsymbol;
+}
+
+/* rvalue stack */
+
+#define RVALUE_STACK_INITIAL_CAPA 128
+
+enum rvalue_stack_type {
+ RVALUE_STACK_HEAP_ALLOCATED = 0,
+ RVALUE_STACK_STACK_ALLOCATED = 1,
+};
+
+typedef struct rvalue_stack_struct {
+ enum rvalue_stack_type type;
+ long capa;
+ long head;
+ VALUE *ptr;
+} rvalue_stack;
+
+static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref);
+
+static rvalue_stack *rvalue_stack_grow(rvalue_stack *stack, VALUE *handle, rvalue_stack **stack_ref)
+{
+ long required = stack->capa ? stack->capa * 2 : RVALUE_STACK_INITIAL_CAPA;
+
+ if (stack->type == RVALUE_STACK_STACK_ALLOCATED) {
+ stack = rvalue_stack_spill(stack, handle, stack_ref);
+ } else {
+ JSON_SIZED_REALLOC_N(stack->ptr, VALUE, required, stack->capa);
+ stack->capa = required;
+ }
+ return stack;
+}
+
+static VALUE rvalue_stack_push(rvalue_stack *stack, VALUE value, VALUE *handle, rvalue_stack **stack_ref)
+{
+ JSON_ASSERT(stack->type != RVALUE_STACK_STACK_ALLOCATED || handle);
+
+ if (RB_UNLIKELY(stack->head >= stack->capa)) {
+ stack = rvalue_stack_grow(stack, handle, stack_ref);
+ }
+
+ stack->ptr[stack->head] = value;
+ stack->head++;
+
+ return value;
+}
+
+static inline VALUE *rvalue_stack_peek(rvalue_stack *stack, long count)
+{
+ return stack->ptr + (stack->head - count);
+}
+
+static inline void rvalue_stack_pop(rvalue_stack *stack, long count)
+{
+ stack->head -= count;
+}
+
+static void rvalue_stack_mark(void *ptr)
+{
+ rvalue_stack *stack = (rvalue_stack *)ptr;
+ long index;
+ if (stack && stack->ptr) {
+ for (index = 0; index < stack->head; index++) {
+ rb_gc_mark_movable(stack->ptr[index]);
+ }
+ }
+}
+
+static void rvalue_stack_free_buffer(rvalue_stack *stack)
+{
+ JSON_SIZED_FREE_N(stack->ptr, stack->capa);
+ stack->ptr = NULL;
+}
+
+static void rvalue_stack_free(void *ptr)
+{
+ rvalue_stack *stack = (rvalue_stack *)ptr;
+ if (stack) {
+ rvalue_stack_free_buffer(stack);
+#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
+ JSON_SIZED_FREE(stack);
+#endif
+ }
+}
+
+static size_t rvalue_stack_memsize(const void *ptr)
+{
+ const rvalue_stack *stack = (const rvalue_stack *)ptr;
+ size_t memsize = sizeof(VALUE) * stack->capa;
+#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
+ memsize += sizeof(rvalue_stack);
+#endif
+ return memsize;
+}
+
+static void rvalue_stack_compact(void *ptr)
+{
+ rvalue_stack *stack = (rvalue_stack *)ptr;
+ long index;
+ if (stack && stack->ptr) {
+ for (index = 0; index < stack->head; index++) {
+ stack->ptr[index] = rb_gc_location(stack->ptr[index]);
+ }
+ }
+}
+
+static const rb_data_type_t JSON_Parser_rvalue_stack_type = {
+ .wrap_struct_name = "JSON::Ext::Parser/rvalue_stack",
+ .function = {
+ .dmark = rvalue_stack_mark,
+ .dfree = rvalue_stack_free,
+ .dsize = rvalue_stack_memsize,
+ .dcompact = rvalue_stack_compact,
+ },
+ // We deliberately don't declare rvalue_stack as RUBY_TYPED_WB_PROTECTED
+ // because it churns a lot of values so trigering write barriers every time is very costly.
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_EMBEDDABLE,
+};
+
+static rvalue_stack *rvalue_stack_spill(rvalue_stack *old_stack, VALUE *handle, rvalue_stack **stack_ref)
+{
+ rvalue_stack *stack;
+ *handle = TypedData_Make_Struct(0, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack);
+ *stack_ref = stack;
+ MEMCPY(stack, old_stack, rvalue_stack, 1);
+
+ stack->capa = old_stack->capa << 1;
+ stack->ptr = ALLOC_N(VALUE, stack->capa);
+ stack->type = RVALUE_STACK_HEAP_ALLOCATED;
+ MEMCPY(stack->ptr, old_stack->ptr, VALUE, old_stack->head);
+ return stack;
+}
+
+static void rvalue_stack_eagerly_release(VALUE handle)
+{
+ if (handle) {
+ rvalue_stack *stack;
+ TypedData_Get_Struct(handle, rvalue_stack, &JSON_Parser_rvalue_stack_type, stack);
+#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
+ rvalue_stack_free_buffer(stack);
+#else
+ rvalue_stack_free(stack);
+ RTYPEDDATA_DATA(handle) = NULL;
+#endif
+ }
+}
+
+/* frame stack */
+
+// Iterative (non-recursive) parsing keeps an explicit stack of the containers
+// currently being built, instead of relying on the C call stack. Each frame
+// only needs enough bookkeeping to close its container: which kind it is, the
+// rvalue_stack position where its children start (so we know how many to pop),
+// and the cursor at its opening brace (used to rewind for duplicate key
+// errors). Frames hold no VALUEs, so this stack needs no GC marking; it reuses
+// the same stack-allocated-with-heap-spill strategy as the rvalue_stack so that
+// it's freed even if parsing raises.
+//
+// The lifecycle helpers below (grow/push/peek/pop/spill/free/eagerly_release
+// and the rb_data_type_t) deliberately mirror their rvalue_stack counterparts
+// -- the element type and the absence of a mark function are the only real
+// differences. Keep the two in sync: a fix to the spill/release or
+// HAVE_RUBY_TYPED_EMBEDDABLE handling in one almost certainly belongs in the
+// other.
+#define JSON_FRAME_STACK_INITIAL_CAPA 32
+
+enum json_frame_type {
+ JSON_FRAME_ROOT, // == JSON_PHASE_DONE
+ JSON_FRAME_ARRAY, // == JSON_PHASE_ARRAY_COMMA
+ JSON_FRAME_OBJECT, // = JSON_PHASE_OBJECT_COMMA
+};
+
+// Where a frame is within its container's grammar. This is the entirety of the
+// parser's "what to do next" state: json_parse_any dispatches on the top
+// frame's phase and holds no resume state in C locals, so a parse can stop at
+// any value boundary and be resumed purely from the (persistable) frame stack.
+//
+// The first three phases are deliberately equal to the corresponding json_frame_type
+// to simplify the transition of phase in json_value_completed.
+enum json_frame_phase {
+ JSON_PHASE_DONE = JSON_FRAME_ROOT, // root only: the document value has been parsed
+ JSON_PHASE_ARRAY_COMMA = JSON_FRAME_ARRAY, // after a value: expecting ',' or the closing ']'
+ JSON_PHASE_OBJECT_COMMA = JSON_FRAME_OBJECT, // after a value: expecting ',' or the closing '}'
+ JSON_PHASE_VALUE, // expecting a value (document root, array element, or object value after ':')
+ JSON_PHASE_OBJECT_KEY, // expecting a '"' key (after '{' or ',')
+ JSON_PHASE_OBJECT_COLON, // object only: after a key, expecting ':'
+};
+
+typedef struct json_frame_struct {
+ enum json_frame_type type;
+ enum json_frame_phase phase;
+ long value_stack_head; // rvalue_stack->head when this container opened
+ size_t start_offset; // object frames only (the '{'); NULL otherwise
+} json_frame;
+
+typedef struct json_frame_stack_struct {
+ enum rvalue_stack_type type; // shared with rvalue_stack: is ptr stack- or heap-allocated
+ long capa;
+ long head;
+ json_frame *ptr;
+} json_frame_stack;
+
+enum deprecatable_action {
+ JSON_DEPRECATED = 0,
+ JSON_IGNORE,
+ JSON_RAISE,
+};
+
+typedef struct JSON_ParserStruct {
+ VALUE on_load_proc;
+ VALUE decimal_class;
+ ID decimal_method_id;
+ enum deprecatable_action on_duplicate_key;
+ enum deprecatable_action on_comment;
+ int max_nesting;
+ bool allow_nan;
+ bool allow_trailing_comma;
+ bool allow_control_characters;
+ bool allow_invalid_escape;
+ bool symbolize_names;
+ bool freeze;
+} JSON_ParserConfig;
+
+typedef struct JSON_ParserStateStruct {
+ VALUE *value_stack_handle;
+ VALUE *frame_stack_handle;
+ const char *start;
+ const char *cursor;
+ const char *end;
+ rvalue_stack *value_stack;
+ json_frame_stack *frames;
+ rvalue_cache name_cache;
+ int in_array;
+ int current_nesting;
+ unsigned int emitted_deprecations;
+ VALUE parser;
+} JSON_ParserState;
+
+static json_frame_stack *json_frame_stack_spill(json_frame_stack *old_stack, VALUE *handle, json_frame_stack **stack_ref);
+
+static json_frame_stack *json_frame_stack_grow(json_frame_stack *stack, VALUE *handle, json_frame_stack **stack_ref)
+{
+ long required = stack->capa ? stack->capa * 2 : JSON_FRAME_STACK_INITIAL_CAPA;
+
+ if (stack->type == RVALUE_STACK_STACK_ALLOCATED) {
+ stack = json_frame_stack_spill(stack, handle, stack_ref);
+ } else {
+ JSON_SIZED_REALLOC_N(stack->ptr, json_frame, required, stack->capa);
+ stack->capa = required;
+ }
+ return stack;
+}
+
+static json_frame *json_frame_stack_push(JSON_ParserState *state, json_frame frame)
+{
+ json_frame_stack *stack = state->frames;
+
+ JSON_ASSERT(stack->type != RVALUE_STACK_STACK_ALLOCATED || state->frame_stack_handle);
+
+ if (RB_UNLIKELY(stack->head >= stack->capa)) {
+ stack = json_frame_stack_grow(stack, state->frame_stack_handle, &state->frames);
+ }
+
+ json_frame *frame_ptr = &stack->ptr[stack->head++];
+ *frame_ptr = frame;
+ return frame_ptr;
+}
+
+static inline json_frame *json_frame_stack_peek(json_frame_stack *stack)
+{
+ return &stack->ptr[stack->head - 1];
+}
+
+static inline void json_frame_stack_pop(json_frame_stack *stack)
+{
+ stack->head--;
+}
+
+static void json_frame_stack_free_buffer(json_frame_stack *stack)
+{
+ JSON_SIZED_FREE_N(stack->ptr, stack->capa);
+ stack->ptr = NULL;
+}
+
+static void json_frame_stack_free(void *ptr)
+{
+ json_frame_stack *stack = (json_frame_stack *)ptr;
+ if (stack) {
+ json_frame_stack_free_buffer(stack);
+#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
+ JSON_SIZED_FREE(stack);
+#endif
+ }
+}
+
+static size_t json_frame_stack_memsize(const void *ptr)
+{
+ const json_frame_stack *stack = (const json_frame_stack *)ptr;
+
+ size_t memsize = sizeof(json_frame) * stack->capa;
+#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
+ memsize += sizeof(json_frame_stack);
+#endif
+ return memsize;
+}
+
+static const rb_data_type_t JSON_Parser_frame_stack_type = {
+ .wrap_struct_name = "JSON::Ext::Parser/frame_stack",
+ .function = {
+ .dmark = NULL,
+ .dfree = json_frame_stack_free,
+ .dsize = json_frame_stack_memsize,
+ },
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
+};
+
+static json_frame_stack *json_frame_stack_spill(json_frame_stack *old_stack, VALUE *handle, json_frame_stack **stack_ref)
+{
+ json_frame_stack *stack;
+ *handle = TypedData_Make_Struct(0, json_frame_stack, &JSON_Parser_frame_stack_type, stack);
+ *stack_ref = stack;
+ MEMCPY(stack, old_stack, json_frame_stack, 1);
+
+ stack->capa = old_stack->capa << 1;
+ stack->ptr = ALLOC_N(json_frame, stack->capa);
+ stack->type = RVALUE_STACK_HEAP_ALLOCATED;
+ MEMCPY(stack->ptr, old_stack->ptr, json_frame, old_stack->head);
+ return stack;
+}
+
+static void json_frame_stack_eagerly_release(VALUE handle)
+{
+ if (handle) {
+ json_frame_stack *stack;
+ TypedData_Get_Struct(handle, json_frame_stack, &JSON_Parser_frame_stack_type, stack);
+#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
+ json_frame_stack_free_buffer(stack);
+#else
+ json_frame_stack_free(stack);
+ RTYPEDDATA_DATA(handle) = NULL;
+#endif
+ }
+}
+
+static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
+{
+ int len = 1;
+ if (ch <= 0x7F) {
+ buf[0] = (char) ch;
+ } else if (ch <= 0x07FF) {
+ buf[0] = (char) ((ch >> 6) | 0xC0);
+ buf[1] = (char) ((ch & 0x3F) | 0x80);
+ len++;
+ } else if (ch <= 0xFFFF) {
+ buf[0] = (char) ((ch >> 12) | 0xE0);
+ buf[1] = (char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[2] = (char) ((ch & 0x3F) | 0x80);
+ len += 2;
+ } else if (ch <= 0x1fffff) {
+ buf[0] =(char) ((ch >> 18) | 0xF0);
+ buf[1] =(char) (((ch >> 12) & 0x3F) | 0x80);
+ buf[2] =(char) (((ch >> 6) & 0x3F) | 0x80);
+ buf[3] =(char) ((ch & 0x3F) | 0x80);
+ len += 3;
+ } else {
+ buf[0] = '?';
+ }
+ return len;
+}
+
+static inline size_t rest(JSON_ParserState *state) {
+ return state->end - state->cursor;
+}
+
+static inline bool eos(JSON_ParserState *state) {
+ return state->cursor >= state->end;
+}
+
+static inline char peek(JSON_ParserState *state)
+{
+ if (RB_UNLIKELY(eos(state))) {
+ return 0;
+ }
+ return *state->cursor;
+}
+
+static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out)
+{
+ JSON_ASSERT(state->cursor <= state->end);
+
+ // Redundant but helpful for hardening
+ if (RB_UNLIKELY(state->cursor > state->end)) {
+ state->cursor = state->end;
+ }
+
+ const char *cursor = state->cursor;
+ long column = 0;
+ long line = 1;
+
+ while (cursor >= state->start) {
+ if (*cursor-- == '\n') {
+ line++;
+ break;
+ }
+ column++;
+ }
+
+ while (cursor >= state->start) {
+ if (*cursor-- == '\n') {
+ line++;
+ }
+ }
+ *line_out = line;
+ *column_out = column;
+}
+
+static const unsigned int MAX_DEPRECATIONS = 5;
+
+static void emit_parse_warning(const char *message, JSON_ParserState *state)
+{
+ long line, column;
+ cursor_position(state, &line, &column);
+
+ VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
+ rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
+}
+
+#define PARSE_ERROR_FRAGMENT_LEN 32
+
+static VALUE build_parse_error_message(const char *format, JSON_ParserState *state)
+{
+ unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
+
+ const char *ptr = "EOF";
+ if (state->cursor && state->cursor < state->end) {
+ ptr = state->cursor;
+ size_t len = 0;
+ while (len < PARSE_ERROR_FRAGMENT_LEN) {
+ char ch = ptr[len];
+ if (!ch || ch == '\n' || ch == ' ' || ch == '\t' || ch == '\r') {
+ break;
+ }
+ len++;
+ }
+
+ if (len) {
+ buffer[0] = '\'';
+ MEMCPY(buffer + 1, ptr, char, len);
+
+ while (buffer[len] >= 0x80 && buffer[len] < 0xC0) { // Is continuation byte
+ len--;
+ }
+
+ if (buffer[len] >= 0xC0) { // multibyte character start
+ len--;
+ }
+
+ buffer[len + 1] = '\'';
+ buffer[len + 2] = '\0';
+ ptr = (const char *)buffer;
+ }
+ }
+
+ return rb_enc_sprintf(enc_utf8, format, ptr);
+}
+
+static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, long column, bool eos)
+{
+ VALUE exc = rb_exc_new_str(eParserError, message);
+ rb_ivar_set(exc, i_at_line, LONG2NUM(line));
+ rb_ivar_set(exc, i_at_column, LONG2NUM(column));
+ return exc;
+}
+
+NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state, bool eos)
+{
+ if (state->parser) {
+ if (eos) {
+ // the error will be swallowed by ResumableParser#parse, so no
+ // point building a message or backtrace.
+ rb_throw_obj(state->parser, state->parser);
+ } else {
+ // line and columns can't be accurate in resumable
+ rb_exc_raise(parse_error_new(state, build_parse_error_message(format, state), 0, 0, eos));
+ }
+ } else {
+ VALUE message = build_parse_error_message(format, state);
+ long line, column;
+ cursor_position(state, &line, &column);
+ rb_str_catf(message, " at line %ld column %ld", line, column);
+ rb_exc_raise(parse_error_new(state, message, line, column, eos));
+ }
+}
+
+NORETURN(static) void raise_eos_error(const char *format, JSON_ParserState *state)
+{
+ raise_parse_error(format, state, true);
+}
+
+NORETURN(static) void raise_syntax_error(const char *format, JSON_ParserState *state)
+{
+ raise_parse_error(format, state, false);
+}
+
+NORETURN(static) void raise_parse_error_at(const char *format, JSON_ParserState *state, const char *at, bool eos)
+{
+ state->cursor = at;
+ raise_parse_error(format, state, eos);
+}
+
+NORETURN(static) void raise_eos_error_at(const char *format, JSON_ParserState *state, const char *at)
+{
+ raise_parse_error_at(format, state, at, true);
+}
+
+NORETURN(static) void raise_syntax_error_at(const char *format, JSON_ParserState *state, const char *at)
+{
+ raise_parse_error_at(format, state, at, false);
+}
+
+/* unicode */
+
+static const signed char digit_values[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+};
+
+static uint32_t unescape_unicode(JSON_ParserState *state, const char *sp, const char *spe)
+{
+ if (RB_UNLIKELY(sp > spe - 4)) {
+ raise_eos_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
+ }
+
+ const unsigned char *p = (const unsigned char *)sp;
+
+ const signed char b0 = digit_values[p[0]];
+ const signed char b1 = digit_values[p[1]];
+ const signed char b2 = digit_values[p[2]];
+ const signed char b3 = digit_values[p[3]];
+
+ if (RB_UNLIKELY((signed char)(b0 | b1 | b2 | b3) < 0)) {
+ raise_syntax_error_at("incomplete unicode character escape sequence at %s", state, sp - 2);
+ }
+
+ return ((uint32_t)b0 << 12) | ((uint32_t)b1 << 8) | ((uint32_t)b2 << 4) | (uint32_t)b3;
+}
+
+#define GET_PARSER_CONFIG \
+ JSON_ParserConfig *config; \
+ TypedData_Get_Struct(self, JSON_ParserConfig, &JSON_ParserConfig_type, config)
+
+static const rb_data_type_t JSON_ParserConfig_type;
+
+const char *COMMENT_DEPRECATION_MESSAGE = "Encountered comment in JSON. This will raise an error in json 3.0 unless enabled via `allow_comments: true`";
+NOINLINE(static) void
+json_eat_comments(JSON_ParserState *state, JSON_ParserConfig *config, const char *resume_pos)
+{
+ if (config->on_comment == JSON_RAISE) {
+ raise_syntax_error("unexpected token %s", state);
+ }
+
+ const char *start = state->cursor;
+ // An incomplete comment suspends a resumable parse by rewinding the cursor
+ // and throwing. Callers that already consumed a token not yet committed to
+ // the frame stack pass resume_pos so the rewind re-reads that token too.
+ // Non-resumable error positions keep pointing at the comment either way.
+ const char *rewind_pos = (state->parser && resume_pos) ? resume_pos : start;
+ state->cursor++;
+
+ switch (peek(state)) {
+ case '/': {
+ const char *newline = memchr(state->cursor, '\n', state->end - state->cursor);
+ if (!newline) {
+ // state->parser marks resumable mode, where the buffer end is only a
+ // chunk boundary: the terminating newline may still arrive, so leave
+ // the comment unterminated instead of consuming to end as a one-shot
+ // parse would.
+ if (state->parser) {
+ raise_eos_error_at("unterminated comment, expected end of line", state, rewind_pos);
+ }
+ state->cursor = state->end;
+ } else {
+ state->cursor = newline + 1;
+ }
+ break;
+ }
+ case '*': {
+ state->cursor++;
+
+ while (true) {
+ const char *next_match = memchr(state->cursor, '*', state->end - state->cursor);
+ if (!next_match) {
+ raise_eos_error_at("unterminated comment, expected closing '*/'", state, rewind_pos);
+ }
+
+ state->cursor = next_match + 1;
+ if (peek(state) == '/') {
+ state->cursor++;
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ raise_parse_error_at("unexpected token %s", state, eos(state) ? rewind_pos : start, eos(state));
+ break;
+ }
+
+ if (config->on_comment == JSON_DEPRECATED && state->emitted_deprecations < MAX_DEPRECATIONS) {
+ state->emitted_deprecations++;
+ emit_parse_warning(COMMENT_DEPRECATION_MESSAGE, state);
+ }
+}
+
+ALWAYS_INLINE(static) void
+json_eat_whitespace_resume_at(JSON_ParserState *state, JSON_ParserConfig *config, bool include_comments, const char *resume_pos)
+{
+ while (true) {
+ switch (peek(state)) {
+ case ' ':
+ state->cursor++;
+ break;
+ case '\n':
+ state->cursor++;
+
+ // Heuristic: if we see a newline, there is likely consecutive spaces after it.
+#if JSON_CPU_LITTLE_ENDIAN_64BITS
+ while (rest(state) > 8) {
+ uint64_t chunk;
+ memcpy(&chunk, state->cursor, sizeof(uint64_t));
+ if (chunk == 0x2020202020202020) {
+ state->cursor += 8;
+ continue;
+ }
+
+ uint32_t consecutive_spaces = trailing_zeros64(chunk ^ 0x2020202020202020) / CHAR_BIT;
+ state->cursor += consecutive_spaces;
+ break;
+ }
+#endif
+ break;
+ case '\t':
+ case '\r':
+ state->cursor++;
+ break;
+ case '/':
+ if (!include_comments) {
+ return;
+ }
+
+ json_eat_comments(state, config, resume_pos);
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+ALWAYS_INLINE(static) void
+json_eat_whitespace(JSON_ParserState *state, JSON_ParserConfig *config, bool include_comments)
+{
+ json_eat_whitespace_resume_at(state, config, include_comments, NULL);
+}
+
+static inline VALUE build_string(const char *start, const char *end, bool intern, bool symbolize)
+{
+ if (symbolize) {
+ intern = true;
+ }
+ VALUE result;
+# ifdef HAVE_RB_ENC_INTERNED_STR
+ if (intern) {
+ result = rb_enc_interned_str(start, (long)(end - start), enc_utf8);
+ } else {
+ result = rb_utf8_str_new(start, (long)(end - start));
+ }
+# else
+ result = rb_utf8_str_new(start, (long)(end - start));
+ if (intern) {
+ result = rb_funcall(rb_str_freeze(result), i_uminus, 0);
+ }
+# endif
+
+ if (symbolize) {
+ result = rb_str_intern(result);
+ }
+
+ return result;
+}
+
+static inline bool json_string_cacheable_p(const char *string, size_t length)
+{
+ // We mostly want to cache strings that are likely to be repeated.
+ // Simple heuristics:
+ // - Common names aren't likely to be very long. So we just don't cache names above an arbitrary threshold.
+ // - If the first character isn't a letter, we're much less likely to see this string again.
+ return length <= JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH && rb_isalpha(string[0]);
+}
+
+static inline VALUE json_string_fastpath(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name)
+{
+ bool intern = is_name || config->freeze;
+ bool symbolize = is_name && config->symbolize_names;
+ size_t bufferSize = stringEnd - string;
+
+ if (is_name && state->in_array && RB_LIKELY(json_string_cacheable_p(string, bufferSize))) {
+ VALUE cached_key;
+ if (RB_UNLIKELY(symbolize)) {
+ cached_key = rsymbol_cache_fetch(&state->name_cache, string, bufferSize);
+ } else {
+ cached_key = rstring_cache_fetch(&state->name_cache, string, bufferSize);
+ }
+
+ if (RB_LIKELY(cached_key)) {
+ return cached_key;
+ }
+ }
+
+ return build_string(string, stringEnd, intern, symbolize);
+}
+
+#define JSON_MAX_UNESCAPE_POSITIONS 16
+typedef struct _json_unescape_positions {
+ long size;
+ const char **positions;
+ unsigned long additional_backslashes;
+} JSON_UnescapePositions;
+
+static inline const char *json_next_backslash(const char *pe, const char *stringEnd, JSON_UnescapePositions *positions)
+{
+ while (positions->size) {
+ positions->size--;
+ const char *next_position = positions->positions[0];
+ positions->positions++;
+ if (next_position >= pe) {
+ return next_position;
+ }
+ }
+
+ if (positions->additional_backslashes) {
+ positions->additional_backslashes--;
+ return memchr(pe, '\\', stringEnd - pe);
+ }
+
+ return NULL;
+}
+
+NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_ParserConfig *config, const char *string, const char *stringEnd, bool is_name, JSON_UnescapePositions *positions)
+{
+ bool intern = is_name || config->freeze;
+ bool symbolize = is_name && config->symbolize_names;
+ size_t bufferSize = stringEnd - string;
+ const char *p = string, *pe = string, *bufferStart;
+ char *buffer;
+
+ VALUE result = rb_str_buf_new(bufferSize);
+ rb_enc_associate_index(result, utf8_encindex);
+ buffer = RSTRING_PTR(result);
+ bufferStart = buffer;
+
+#define APPEND_CHAR(chr) *buffer++ = chr; p = ++pe;
+
+ while (pe < stringEnd && (pe = json_next_backslash(pe, stringEnd, positions))) {
+ if (pe > p) {
+ MEMCPY(buffer, p, char, pe - p);
+ buffer += pe - p;
+ }
+ switch (*++pe) {
+ case '"':
+ case '/':
+ p = pe; // nothing to unescape just need to skip the backslash
+ break;
+ case '\\':
+ APPEND_CHAR('\\');
+ break;
+ case 'n':
+ APPEND_CHAR('\n');
+ break;
+ case 'r':
+ APPEND_CHAR('\r');
+ break;
+ case 't':
+ APPEND_CHAR('\t');
+ break;
+ case 'b':
+ APPEND_CHAR('\b');
+ break;
+ case 'f':
+ APPEND_CHAR('\f');
+ break;
+ case 'u': {
+ uint32_t ch = unescape_unicode(state, ++pe, stringEnd);
+ pe += 3;
+ /* To handle values above U+FFFF, we take a sequence of
+ * \uXXXX escapes in the U+D800..U+DBFF then
+ * U+DC00..U+DFFF ranges, take the low 10 bits from each
+ * to make a 20-bit number, then add 0x10000 to get the
+ * final codepoint.
+ *
+ * See Unicode 15: 3.8 "Surrogates", 5.3 "Handling
+ * Surrogate Pairs in UTF-16", and 23.6 "Surrogates
+ * Area".
+ */
+ if ((ch & 0xFC00) == 0xD800) {
+ pe++;
+ if (RB_LIKELY((pe <= stringEnd - 6) && memcmp(pe, "\\u", 2) == 0)) {
+ uint32_t sur = unescape_unicode(state, pe + 2, stringEnd);
+
+ if (RB_UNLIKELY((sur & 0xFC00) != 0xDC00)) {
+ raise_syntax_error_at("invalid surrogate pair at %s", state, p);
+ }
+
+ ch = (((ch & 0x3F) << 10) | ((((ch >> 6) & 0xF) + 1) << 16) | (sur & 0x3FF));
+ pe += 5;
+ } else {
+ raise_syntax_error_at("incomplete surrogate pair at %s", state, p);
+ break;
+ }
+ }
+
+ int unescape_len = convert_UTF32_to_UTF8(buffer, ch);
+ buffer += unescape_len;
+ p = ++pe;
+ break;
+ }
+ case 0:
+ return Qundef;
+ default:
+ if ((unsigned char)*pe < 0x20) {
+ if (!config->allow_control_characters) {
+ if (*pe == '\n') {
+ raise_syntax_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1);
+ }
+ raise_syntax_error_at("invalid ASCII control character in string: %s", state, pe - 1);
+ }
+ }
+
+ if (config->allow_invalid_escape) {
+ APPEND_CHAR(*pe);
+ } else {
+ raise_syntax_error_at("invalid escape character in string: %s", state, pe - 1);
+ }
+ break;
+ }
+ }
+#undef APPEND_CHAR
+
+ if (stringEnd > p) {
+ MEMCPY(buffer, p, char, stringEnd - p);
+ buffer += stringEnd - p;
+ }
+ rb_str_set_len(result, buffer - bufferStart);
+
+ if (symbolize) {
+ result = rb_str_intern(result);
+ } else if (intern) {
+ result = rb_str_to_interned_str(result);
+ }
+
+ return result;
+}
+
+#define MAX_FAST_INTEGER_SIZE 18
+#define MAX_NUMBER_STACK_BUFFER 128
+
+typedef VALUE (*json_number_decode_func_t)(const char *ptr);
+
+static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func)
+{
+ if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
+ char buffer[MAX_NUMBER_STACK_BUFFER];
+ MEMCPY(buffer, start, char, len);
+ buffer[len] = '\0';
+ return func(buffer);
+ } else {
+ VALUE buffer_v = rb_str_tmp_new(len);
+ char *buffer = RSTRING_PTR(buffer_v);
+ MEMCPY(buffer, start, char, len);
+ buffer[len] = '\0';
+ VALUE number = func(buffer);
+ RB_GC_GUARD(buffer_v);
+ return number;
+ }
+}
+
+static VALUE json_decode_inum(const char *buffer)
+{
+ return rb_cstr2inum(buffer, 10);
+}
+
+NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
+{
+ return json_decode_large_number(start, len, json_decode_inum);
+}
+
+static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
+{
+ if (RB_LIKELY(mantissa_digits < MAX_FAST_INTEGER_SIZE)) {
+ if (negative) {
+ return INT64T2NUM(-((int64_t)mantissa));
+ }
+ return UINT64T2NUM(mantissa);
+ }
+
+ return json_decode_large_integer(start, end - start);
+}
+
+static VALUE json_decode_dnum(const char *buffer)
+{
+ return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
+}
+
+NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
+{
+ return json_decode_large_number(start, len, json_decode_dnum);
+}
+
+/* Ruby JSON optimized float decoder using vendored Ryu algorithm
+ * Accepts pre-extracted mantissa and exponent from first-pass validation
+ */
+static inline VALUE json_decode_float(JSON_ParserConfig *config, uint64_t mantissa, int mantissa_digits, int64_t exponent, bool negative,
+ const char *start, const char *end)
+{
+ if (RB_UNLIKELY(config->decimal_class)) {
+ VALUE text = rb_str_new(start, end - start);
+ return rb_funcallv(config->decimal_class, config->decimal_method_id, 1, &text);
+ }
+
+ if (RB_UNLIKELY(exponent > INT32_MAX)) {
+ return negative ? CMinusInfinity : CInfinity;
+ }
+
+ if (RB_UNLIKELY(exponent < INT32_MIN)) {
+ return rb_float_new(negative ? -0.0 : 0.0);
+ }
+
+ if (RB_UNLIKELY(mantissa_digits > 18 || mantissa_digits + exponent < -307)) {
+ // If the value is so small that it definitely underflows to 0.0, return early
+ // to avoid triggering a "Float out of range" warning from rb_cstr_to_dbl.
+ // When mantissa_digits + exponent < -324, value < 10^(-324) < DBL_TRUE_MIN/2,
+ // so it rounds to 0 in IEEE 754 round-to-nearest.
+ if (RB_UNLIKELY(mantissa_digits + exponent < -324)) {
+ return rb_float_new(negative ? -0.0 : 0.0);
+ }
+ return json_decode_large_float(start, end - start);
+ }
+
+ return DBL2NUM(ffp_s2d(exponent, mantissa, negative));
+}
+
+static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig *config, long count)
+{
+ VALUE array = rb_ary_new_from_values(count, rvalue_stack_peek(state->value_stack, count));
+ rvalue_stack_pop(state->value_stack, count);
+
+ if (config->freeze) {
+ RB_OBJ_FREEZE(array);
+ }
+
+ return array;
+}
+
+static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
+{
+ VALUE set = rb_hash_new_capa(count / 2);
+ for (size_t index = 0; index < count; index += 2) {
+ size_t before = RHASH_SIZE(set);
+ VALUE key = pairs[index];
+ rb_hash_aset(set, key, Qtrue);
+ if (RHASH_SIZE(set) == before) {
+ if (RB_SYMBOL_P(key)) {
+ return rb_sym2str(key);
+ }
+ return key;
+ }
+ }
+ return Qfalse;
+}
+
+NOINLINE(static) void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
+{
+ VALUE message = rb_sprintf(
+ "detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
+ rb_inspect(duplicate_key)
+ );
+
+ emit_parse_warning(RSTRING_PTR(message), state);
+ RB_GC_GUARD(message);
+}
+
+NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
+{
+ VALUE message = rb_sprintf(
+ "duplicate key %"PRIsVALUE,
+ rb_inspect(duplicate_key)
+ );
+
+ rb_str_concat(message, build_parse_error_message("", state));
+ if (state->parser) { // line and columns can't be accurate in resumable
+ rb_exc_raise(parse_error_new(state, message, 0, 0, false));
+ } else {
+ long line, column;
+ cursor_position(state, &line, &column);
+ rb_str_catf(message, " at line %ld column %ld", line, column);
+ rb_exc_raise(parse_error_new(state, message, line, column, false));
+ }
+}
+
+NOINLINE(static) void json_on_duplicate_key(JSON_ParserState *state, JSON_ParserConfig *config, size_t count, const VALUE *pairs)
+{
+ switch (config->on_duplicate_key) {
+ case JSON_IGNORE:
+ return;
+
+ case JSON_DEPRECATED:
+ // Only emit the first few deprecations to avoid spamming.
+ if (state->emitted_deprecations < MAX_DEPRECATIONS) {
+ state->emitted_deprecations++;
+ emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
+ }
+ return;
+
+ case JSON_RAISE:
+ raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
+ return;
+ }
+ UNREACHABLE;
+}
+
+static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
+{
+ size_t entries_count = count / 2;
+ VALUE object = rb_hash_new_capa(entries_count);
+ const VALUE *pairs = rvalue_stack_peek(state->value_stack, count);
+ rb_hash_bulk_insert(count, pairs, object);
+
+ if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) {
+ json_on_duplicate_key(state, config, count, pairs);
+ }
+
+ rvalue_stack_pop(state->value_stack, count);
+
+ if (config->freeze) {
+ RB_OBJ_FREEZE(object);
+ }
+
+ return object;
+}
+
+static inline VALUE json_push_value(JSON_ParserState *state, JSON_ParserConfig *config, VALUE value)
+{
+ if (RB_UNLIKELY(config->on_load_proc)) {
+ value = rb_proc_call_with_block(config->on_load_proc, 1, &value, Qnil);
+ }
+ rvalue_stack_push(state->value_stack, value, state->value_stack_handle, &state->value_stack);
+ return value;
+}
+
+static const bool string_scan_table[256] = {
+ // ASCII Control Characters
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ // ASCII Characters
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // '"'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // '\\'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#ifdef HAVE_SIMD
+static SIMD_Implementation simd_impl = SIMD_NONE;
+#endif /* HAVE_SIMD */
+
+ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
+{
+#ifdef HAVE_SIMD
+#if defined(HAVE_SIMD_NEON)
+
+ uint64_t mask = 0;
+ if (string_scan_simd_neon(&state->cursor, state->end, &mask)) {
+ state->cursor += trailing_zeros64(mask) >> 2;
+ return true;
+ }
+
+#elif defined(HAVE_SIMD_SSE2)
+ if (simd_impl == SIMD_SSE2) {
+ int mask = 0;
+ if (string_scan_simd_sse2(&state->cursor, state->end, &mask)) {
+ state->cursor += trailing_zeros(mask);
+ return true;
+ }
+ }
+#endif /* HAVE_SIMD_NEON or HAVE_SIMD_SSE2 */
+#endif /* HAVE_SIMD */
+
+ while (!eos(state)) {
+ if (RB_UNLIKELY(string_scan_table[(unsigned char)*state->cursor])) {
+ return true;
+ }
+ state->cursor++;
+ }
+
+ // If the string ended with an unterminated escape sequence, we might
+ // have gone past the end.
+ if (RB_UNLIKELY(state->cursor > state->end)) {
+ state->cursor = state->end;
+ }
+
+ return false;
+}
+
+static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name, const char *start)
+{
+ const char *backslashes[JSON_MAX_UNESCAPE_POSITIONS];
+ JSON_UnescapePositions positions = {
+ .size = 0,
+ .positions = backslashes,
+ .additional_backslashes = 0,
+ };
+
+ do {
+ switch (*state->cursor) {
+ case '"': {
+ VALUE string = json_string_unescape(state, config, start, state->cursor, is_name, &positions);
+ state->cursor++;
+ return string;
+ }
+ case '\\': {
+ if (RB_LIKELY(positions.size < JSON_MAX_UNESCAPE_POSITIONS)) {
+ backslashes[positions.size] = state->cursor;
+ positions.size++;
+ } else {
+ positions.additional_backslashes++;
+ }
+ state->cursor++;
+ break;
+ }
+ default:
+ if (!config->allow_control_characters) {
+ raise_syntax_error("invalid ASCII control character in string: %s", state);
+ }
+ break;
+ }
+
+ state->cursor++;
+ } while (string_scan(state));
+
+ return Qundef;
+}
+
+ALWAYS_INLINE(static) VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig *config, bool is_name)
+{
+ state->cursor++;
+ const char *start = state->cursor;
+
+ if (RB_UNLIKELY(!string_scan(state))) {
+ return Qundef;
+ }
+
+ VALUE string;
+ if (RB_LIKELY(*state->cursor == '"')) {
+ string = json_string_fastpath(state, config, start, state->cursor, is_name);
+ state->cursor++;
+ }
+ else {
+ string = json_parse_escaped_string(state, config, is_name, start);
+ }
+
+ return string;
+}
+
+#if JSON_CPU_LITTLE_ENDIAN_64BITS
+// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/
+// Additional References:
+// https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
+// http://0x80.pl/notesen/2014-10-12-parsing-decimal-numbers-part-1-swar.html
+static inline uint64_t decode_8digits_unrolled(uint64_t val) {
+ const uint64_t mask = 0x000000FF000000FF;
+ const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return val;
+}
+
+static inline uint64_t decode_4digits_unrolled(uint32_t val) {
+ const uint32_t mask = 0x000000FF;
+ const uint32_t mul1 = 100;
+ val -= 0x30303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = ((val & mask) * mul1) + (((val >> 16) & mask));
+ return val;
+}
+#endif
+
+static inline int json_parse_digits(JSON_ParserState *state, uint64_t *accumulator)
+{
+ const char *start = state->cursor;
+
+#if JSON_CPU_LITTLE_ENDIAN_64BITS
+ while (rest(state) >= sizeof(uint64_t)) {
+ uint64_t next_8bytes;
+ memcpy(&next_8bytes, state->cursor, sizeof(uint64_t));
+
+ // From: https://github.com/simdjson/simdjson/blob/32b301893c13d058095a07d9868edaaa42ee07aa/include/simdjson/generic/numberparsing.h#L333
+ // Branchless version of: http://0x80.pl/articles/swar-digits-validate.html
+ uint64_t match = (next_8bytes & 0xF0F0F0F0F0F0F0F0) | (((next_8bytes + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4);
+
+ if (match == 0x3333333333333333) { // 8 consecutive digits
+ *accumulator = (*accumulator * 100000000) + decode_8digits_unrolled(next_8bytes);
+ state->cursor += 8;
+ continue;
+ }
+
+ uint32_t consecutive_digits = trailing_zeros64(match ^ 0x3333333333333333) / CHAR_BIT;
+
+ if (consecutive_digits >= 4) {
+ *accumulator = (*accumulator * 10000) + decode_4digits_unrolled((uint32_t)next_8bytes);
+ state->cursor += 4;
+ consecutive_digits -= 4;
+ }
+
+ while (consecutive_digits) {
+ *accumulator = *accumulator * 10 + (*state->cursor - '0');
+ consecutive_digits--;
+ state->cursor++;
+ }
+
+ return (int)(state->cursor - start);
+ }
+#endif
+
+ char next_char;
+ while (rb_isdigit(next_char = peek(state))) {
+ *accumulator = *accumulator * 10 + (next_char - '0');
+ state->cursor++;
+ }
+ return (int)(state->cursor - start);
+}
+
+static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig *config, bool negative, const char *start, bool resumable)
+{
+ bool integer = true;
+ const char first_digit = *state->cursor;
+
+ // Variables for Ryu optimization - extract digits during parsing
+ int64_t exponent = 0;
+ int decimal_point_pos = -1;
+ uint64_t mantissa = 0;
+
+ // Parse integer part and extract mantissa digits
+ int mantissa_digits = json_parse_digits(state, &mantissa);
+
+ if (RB_UNLIKELY((first_digit == '0' && mantissa_digits > 1) || (negative && mantissa_digits == 0))) {
+ return Qundef;
+ }
+
+ // Parse fractional part
+ if (peek(state) == '.') {
+ integer = false;
+ decimal_point_pos = mantissa_digits; // Remember position of decimal point
+ state->cursor++;
+
+ int fractional_digits = json_parse_digits(state, &mantissa);
+ mantissa_digits += fractional_digits;
+
+ if (RB_UNLIKELY(!fractional_digits)) {
+ return Qundef;
+ }
+ }
+
+ // Parse exponent
+ if (rb_tolower(peek(state)) == 'e') {
+ integer = false;
+ state->cursor++;
+
+ bool negative_exponent = false;
+ const char next_char = peek(state);
+ if (next_char == '-' || next_char == '+') {
+ negative_exponent = next_char == '-';
+ state->cursor++;
+ }
+
+ uint64_t abs_exponent = 0;
+ int exponent_digits = json_parse_digits(state, &abs_exponent);
+
+ if (RB_UNLIKELY(!exponent_digits)) {
+ return Qundef;
+ }
+
+ if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
+ exponent = negative_exponent ? INT64_MIN : INT64_MAX;
+ } else {
+ exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
+ }
+ }
+
+ // A number touching the end of the buffer may still grow in a later chunk,
+ // so the caller will rewind and wait. Decoding it now would build a value
+ // -- for a long run of digits, an expensive bignum -- only to discard it,
+ // and repeating that on every resumed chunk is quadratic in the number's
+ // length. The digit scan above already advanced the cursor, which is all
+ // the caller needs to detect the incomplete number.
+ if (RB_UNLIKELY(resumable && eos(state))) {
+ return Qundef;
+ }
+
+ if (integer) {
+ return json_decode_integer(mantissa, mantissa_digits, negative, start, state->cursor);
+ }
+
+ // Adjust exponent based on decimal point position
+ if (decimal_point_pos >= 0) {
+ exponent -= (mantissa_digits - decimal_point_pos);
+ }
+
+ return json_decode_float(config, mantissa, mantissa_digits, exponent, negative, start, state->cursor);
+}
+
+// How many values (array elements, or interleaved object keys+values) have been
+// pushed onto the rvalue stack since this container opened. Used to size the
+// bulk decode on close, and to tell the first key/colon from later ones.
+static inline long json_frame_entry_count(const json_frame *frame, const rvalue_stack *value_stack)
+{
+ return value_stack->head - frame->value_stack_head;
+}
+
+// A complete value now sits on top of the rvalue stack. Advance the frame that
+// was waiting for it: the root document is done, or the enclosing container
+// moves on to expecting a ',' or its closing bracket. The caller passes the
+// frame it already has in hand -- the one that was expecting the value -- which
+// after a container close is the freshly re-exposed parent.
+static inline enum json_frame_phase json_value_completed(json_frame *frame)
+{
+ JSON_ASSERT((int)JSON_PHASE_DONE == (int)JSON_FRAME_ROOT);
+ JSON_ASSERT((int)JSON_PHASE_ARRAY_COMMA == (int)JSON_FRAME_ARRAY);
+ JSON_ASSERT((int)JSON_PHASE_OBJECT_COMMA == (int)JSON_FRAME_OBJECT);
+
+ return frame->phase = (enum json_frame_phase) frame->type;
+}
+
+ALWAYS_INLINE(static) void json_match_keyword(JSON_ParserState *state, const char *keyword, size_t offset)
+{
+ // It is assumed that since `keyword` is always a literal, the compiler is able to constantize this
+ // `strlen` and several other computations in that routine.
+
+ size_t len = strlen(keyword);
+
+ // Note: memcmp with a small power of two and a literal string compile to an integer comparison /
+ // That's why we sometime compare starting from the first byte and sometimes from the second.
+ if (rest(state) >= len && (memcmp(state->cursor + offset, keyword + offset, len - offset) == 0)) {
+ state->cursor += len;
+ return;
+ }
+
+ bool eos = rest(state) < len && memcmp(state->cursor, keyword, rest(state)) == 0;
+ raise_parse_error("unexpected token %s", state, eos);
+}
+
+// Parse an arbitrary JSON value iteratively. This is a state machine driven
+// entirely by the top frame's phase so it can stop at any value boundary and
+// resume purely from the frame stack. A JSON_FRAME_ROOT frame sits at the
+// bottom of the stack, so the stack is never empty mid-parse and the document
+// itself is just another frame whose value, once parsed, leaves its phase DONE.
+// When invoked in resumable mode, it returns true after parsing a complete document.
+// If reaching EOS without having parsed a complete document, either returns false
+// of raise a JSON::ParserError tagged with `@eos=true`.
+ALWAYS_INLINE(static) bool json_parse_any(JSON_ParserState *state, JSON_ParserConfig *config, bool resumable)
+{
+ json_frame *frame = json_frame_stack_peek(state->frames);
+
+ switch (frame->phase) {
+ case JSON_PHASE_DONE: JSON_UNREACHABLE_RETURN(false);
+ case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA;
+ case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA;
+ case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE;
+ case JSON_PHASE_OBJECT_KEY: goto JSON_PHASE_OBJECT_KEY;
+ case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON;
+ }
+ JSON_UNREACHABLE_RETURN(false);
+
+ JSON_PHASE_VALUE: {
+ json_eat_whitespace(state, config, true);
+
+ // A trailing comma lands us here expecting an element but finding the
+ // closing bracket; hand off to ARRAY_COMMA to close. An empty array
+ // closes inline at '[', so this position is only reached after a ','.
+ if (config->allow_trailing_comma && frame->type == JSON_FRAME_ARRAY && peek(state) == ']') {
+ goto JSON_PHASE_ARRAY_COMMA;
+ }
+
+ VALUE value;
+ const char *value_start = state->cursor;
+
+ switch (peek(state)) {
+ case 'n':
+ json_match_keyword(state, "null", 0);
+ value = Qnil;
+ break;
+
+ case 't':
+ json_match_keyword(state, "true", 0);
+ value = Qtrue;
+ break;
+
+ case 'f':
+ json_match_keyword(state, "false", 1);
+ value = Qfalse;
+ break;
+
+ case 'N':
+ if (!config->allow_nan) {
+ raise_syntax_error("unexpected token %s", state);
+ }
+
+ json_match_keyword(state, "NaN", 1);
+ value = CNaN;
+ break;
+
+ case 'I':
+ if (!config->allow_nan) {
+ raise_syntax_error("unexpected token %s", state);
+ }
+
+ json_match_keyword(state, "Infinity", 0);
+ value = CInfinity;
+ break;
+
+ case '-': {
+ state->cursor++;
+
+ value = json_parse_number(state, config, true, value_start, resumable);
+
+ if (RB_UNLIKELY(UNDEF_P(value) && config->allow_nan && peek(state) == 'I')) {
+ state->cursor = value_start;
+ json_match_keyword(state, "-Infinity", 1);
+ value = CMinusInfinity;
+ break;
+ }
+
+ // Top level numbers are ambiguous when parsing streams, we can't
+ // know if we parsed all the digits if we hit EOS.
+ if (RB_UNLIKELY(resumable && eos(state))) {
+ state->cursor = value_start;
+ return false;
+ }
+
+ if (RB_UNLIKELY(UNDEF_P(value))) {
+ raise_syntax_error_at("invalid number: %s", state, value_start);
+ }
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
+ value = json_parse_number(state, config, false, value_start, resumable);
+
+ // Top level numbers are ambiguous when parsing streams, we can't
+ // know if we parsed all the digits if we hit EOS.
+ if (RB_UNLIKELY(resumable && eos(state))) {
+ state->cursor = value_start;
+ return false;
+ }
+
+ if (RB_UNLIKELY(UNDEF_P(value))) {
+ raise_syntax_error_at("invalid number: %s", state, value_start);
+ }
+ break;
+ }
+
+ case '"': {
+ // %r{\A"[^"\\\t\n\x00]*(?:\\[bfnrtu\\/"][^"\\]*)*"}
+ value = json_parse_string(state, config, false);
+
+ if (RB_UNLIKELY(UNDEF_P(value))) {
+ bool is_eos = eos(state);
+ if (resumable && is_eos) {
+ state->cursor = value_start;
+ return false;
+ }
+ raise_parse_error("unexpected end of input, expected closing \"", state, is_eos);
+ }
+ break;
+ }
+
+ case '[': {
+ state->cursor++;
+ // The '[' is consumed but its frame is only pushed below, so a
+ // comment suspending here must resume from the bracket.
+ json_eat_whitespace_resume_at(state, config, true, value_start);
+
+ const char next = peek(state);
+ if (next == ']') {
+ state->cursor++;
+ value = json_decode_array(state, config, 0);
+ break;
+ } else if (resumable && eos(state)) {
+ state->cursor = value_start;
+ return false;
+ }
+
+ state->current_nesting++;
+ if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
+ rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting);
+ }
+ state->in_array++;
+
+ // Phase stays VALUE: the next iteration reads the first element.
+ frame = json_frame_stack_push(state, (json_frame){
+ .type = JSON_FRAME_ARRAY,
+ .phase = JSON_PHASE_VALUE,
+ .value_stack_head = state->value_stack->head,
+ });
+ goto JSON_PHASE_VALUE;
+ }
+
+ case '{': {
+ state->cursor++;
+ // Same as '[': the frame is only pushed below.
+ json_eat_whitespace_resume_at(state, config, true, value_start);
+
+ if (peek(state) == '}') {
+ state->cursor++;
+ value = json_decode_object(state, config, 0);
+ break;
+ } else if (resumable && eos(state)) {
+ state->cursor = value_start;
+ return false;
+ }
+
+ state->current_nesting++;
+ if (RB_UNLIKELY(config->max_nesting && (config->max_nesting < state->current_nesting))) {
+ rb_raise(eNestingError, "nesting of %d is too deep", state->current_nesting);
+ }
+
+ // Phase KEY: the next iteration reads the first key.
+ frame = json_frame_stack_push(state, (json_frame){
+ .type = JSON_FRAME_OBJECT,
+ .phase = JSON_PHASE_OBJECT_KEY,
+ .value_stack_head = state->value_stack->head,
+ .start_offset = value_start - state->start,
+ });
+ goto JSON_PHASE_OBJECT_KEY;
+ }
+
+ case 0:
+ // peek() returns 0 both at end-of-stream and for a literal NUL byte in the
+ // buffer. Only a genuine EOS means "feed me more"; a NUL byte that is not at
+ // EOS is just an invalid character.
+ if (eos(state)) {
+ return false;
+ } else {
+ raise_syntax_error("unexpected NULL byte: %s", state);
+ }
+ default:
+ raise_syntax_error("unexpected character: %s", state);
+ }
+
+ json_push_value(state, config, value);
+ json_value_completed(frame);
+
+ switch (frame->phase) {
+ case JSON_PHASE_DONE: return true;
+ case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA;
+ case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA;
+ case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE;
+ case JSON_PHASE_OBJECT_KEY: JSON_UNREACHABLE_RETURN(false);
+ case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON;
+ }
+ JSON_UNREACHABLE_RETURN(false);
+ }
+
+ JSON_PHASE_OBJECT_KEY: {
+ JSON_ASSERT(frame->type == JSON_FRAME_OBJECT);
+
+ json_eat_whitespace(state, config, true);
+
+ // A trailing comma lands us here expecting a key but finding the closing
+ // brace; hand off to OBJECT_COMMA to close. An empty object closes inline
+ // at '{', so this position is only reached after a ','.
+ if (config->allow_trailing_comma && peek(state) == '}') {
+ goto JSON_PHASE_OBJECT_COMMA;
+ }
+
+ const char *start = state->cursor;
+
+ if (RB_LIKELY(peek(state) == '"')) {
+ VALUE string = json_parse_string(state, config, true);
+ if (UNDEF_P(string)) {
+ if (resumable) {
+ state->cursor = start;
+ return false;
+ } else {
+ raise_syntax_error("unexpected end of input, expected closing \"", state);
+ }
+ }
+ json_push_value(state, config, string);
+ frame->phase = JSON_PHASE_OBJECT_COLON;
+ goto JSON_PHASE_OBJECT_COLON;
+ } else if (resumable && eos(state)) {
+ return false;
+ } else {
+ // The message differs for the first key vs. a key after a
+ // ',': the first is the only one reached with nothing pushed
+ // for this object yet.
+ if (json_frame_entry_count(frame, state->value_stack) == 0) {
+ raise_syntax_error("expected object key, got %s", state);
+ } else {
+ raise_syntax_error("expected object key, got: %s", state);
+ }
+ }
+ JSON_UNREACHABLE_RETURN(false);
+ }
+
+ JSON_PHASE_OBJECT_COLON: {
+ JSON_ASSERT(frame->type == JSON_FRAME_OBJECT);
+
+ json_eat_whitespace(state, config, true);
+
+ if (RB_LIKELY(peek(state) == ':')) {
+ state->cursor++;
+ frame->phase = JSON_PHASE_VALUE;
+ goto JSON_PHASE_VALUE;
+ } else if (resumable && eos(state)) {
+ return false;
+ } else {
+ // First colon (only the first pair's key is pushed, nothing
+ // else) vs. a later one.
+ if (json_frame_entry_count(frame, state->value_stack) == 1) {
+ raise_syntax_error("expected ':' after object key", state);
+ } else {
+ raise_syntax_error("expected ':' after object key, got: %s", state);
+ }
+ }
+ JSON_UNREACHABLE_RETURN(false);
+ }
+
+ JSON_PHASE_ARRAY_COMMA: {
+ JSON_ASSERT(frame->type == JSON_FRAME_ARRAY);
+
+ json_eat_whitespace(state, config, true);
+
+ const char next_char = peek(state);
+
+ if (RB_LIKELY(next_char == ',')) {
+ state->cursor++;
+ // Commit the phase before eating the whitespace that follows: an
+ // incomplete comment there would suspend the parse, and a phase not
+ // yet advanced past the ',' would drop it on resume. A trailing comma
+ // is recognized in JSON_PHASE_VALUE once the ']' is in the buffer.
+ frame->phase = JSON_PHASE_VALUE;
+ goto JSON_PHASE_VALUE;
+ } else if (next_char == ']') {
+ state->cursor++;
+ long count = json_frame_entry_count(frame, state->value_stack);
+ state->current_nesting--;
+ state->in_array--;
+
+ json_push_value(state, config, json_decode_array(state, config, count));
+ json_frame_stack_pop(state->frames);
+ frame = json_frame_stack_peek(state->frames);
+
+ json_value_completed(frame);
+
+ switch (frame->phase) {
+ case JSON_PHASE_DONE: return true;
+ case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA;
+ case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA;
+ case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE;
+ case JSON_PHASE_OBJECT_KEY: JSON_UNREACHABLE_RETURN(false);
+ case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON;
+ }
+ } else if (resumable && eos(state)) {
+ return false;
+ } else {
+ raise_syntax_error("expected ',' or ']' after array value", state);
+ }
+ JSON_UNREACHABLE_RETURN(false);
+ }
+
+ JSON_PHASE_OBJECT_COMMA: {
+ JSON_ASSERT(frame->type == JSON_FRAME_OBJECT);
+
+ json_eat_whitespace(state, config, true);
+ const char next_char = peek(state);
+
+ if (RB_LIKELY(next_char == ',')) {
+ state->cursor++;
+ // Commit the phase before eating the whitespace that follows: an
+ // incomplete comment there would suspend the parse, and a phase not
+ // yet advanced past the ',' would drop it on resume. A trailing comma
+ // is recognized in JSON_PHASE_OBJECT_KEY once the '}' is in the buffer.
+ frame->phase = JSON_PHASE_OBJECT_KEY;
+ goto JSON_PHASE_OBJECT_KEY;
+ } else if (next_char == '}') {
+ state->cursor++;
+ state->current_nesting--;
+ size_t count = json_frame_entry_count(frame, state->value_stack);
+
+ // Temporary rewind cursor in case an error is raised
+ const char *final_cursor = state->cursor;
+ state->cursor = state->start + frame->start_offset;
+ VALUE object = json_decode_object(state, config, count);
+ state->cursor = final_cursor;
+
+ json_push_value(state, config, object);
+ json_frame_stack_pop(state->frames);
+ frame = json_frame_stack_peek(state->frames);
+ json_value_completed(frame);
+
+ switch (frame->phase) {
+ case JSON_PHASE_DONE: return true;
+ case JSON_PHASE_ARRAY_COMMA: goto JSON_PHASE_ARRAY_COMMA;
+ case JSON_PHASE_OBJECT_COMMA: goto JSON_PHASE_OBJECT_COMMA;
+ case JSON_PHASE_VALUE: goto JSON_PHASE_VALUE;
+ case JSON_PHASE_OBJECT_KEY: JSON_UNREACHABLE_RETURN(false);
+ case JSON_PHASE_OBJECT_COLON: goto JSON_PHASE_OBJECT_COLON;
+ }
+ } else if (resumable && eos(state)) {
+ return false;
+ } else {
+ raise_syntax_error("expected ',' or '}' after object value, got: %s", state);
+ }
+ JSON_UNREACHABLE_RETURN(false);
+ }
+
+ JSON_UNREACHABLE_RETURN(false);
+}
+
+static void json_ensure_eof(JSON_ParserState *state, JSON_ParserConfig *config)
+{
+ json_eat_whitespace(state, config, true);
+ if (!eos(state)) {
+ raise_syntax_error("unexpected token at end of stream %s", state);
+ }
+}
+
+/*
+ * Document-class: JSON::Ext::Parser
+ *
+ * This is the JSON parser implemented as a C extension. It can be configured
+ * to be used by setting
+ *
+ * JSON.parser = JSON::Ext::Parser
+ *
+ * with the method parser= in JSON.
+ *
+ */
+
+static VALUE convert_encoding(VALUE source)
+{
+ StringValue(source);
+ int encindex = RB_ENCODING_GET(source);
+
+ if (RB_LIKELY(encindex == utf8_encindex)) {
+ return source;
+ }
+
+ if (encindex == binary_encindex) {
+ // For historical reason, we silently reinterpret binary strings as UTF-8
+ return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
+ }
+
+ source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
+ StringValue(source);
+ return source;
+}
+
+struct parser_config_init_args {
+ JSON_ParserConfig *config;
+ VALUE self;
+ VALUE unknown_keywords;
+ bool strict;
+};
+
+static void parser_config_wb_write(VALUE self, VALUE *dest, VALUE val)
+{
+ *dest = val;
+ if (self) RB_OBJ_WRITTEN(self, Qundef, val);
+}
+
+static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
+{
+ struct parser_config_init_args *args = (struct parser_config_init_args *)data;
+ JSON_ParserConfig *config = args->config;
+ VALUE self = args->self;
+
+ if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
+ else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
+ else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
+ else if (key == sym_allow_comments) { config->on_comment = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
+ else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
+ else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
+ else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
+ else if (key == sym_freeze) { config->freeze = RTEST(val); }
+ else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
+ else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
+ else if (key == sym_decimal_class) {
+ if (RTEST(val)) {
+ if (rb_respond_to(val, i_try_convert)) {
+ parser_config_wb_write(self, &config->decimal_class, val);
+ config->decimal_method_id = i_try_convert;
+ } else if (rb_respond_to(val, i_new)) {
+ parser_config_wb_write(self, &config->decimal_class, val);
+ config->decimal_method_id = i_new;
+ } else if (RB_TYPE_P(val, T_CLASS)) {
+ VALUE name = rb_class_name(val);
+ const char *name_cstr = RSTRING_PTR(name);
+ const char *last_colon = strrchr(name_cstr, ':');
+ if (last_colon) {
+ const char *mod_path_end = last_colon - 1;
+ VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
+ parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
+
+ const char *method_name_beg = last_colon + 1;
+ long before_len = method_name_beg - name_cstr;
+ long len = RSTRING_LEN(name) - before_len;
+ VALUE method_name = rb_str_substr(name, before_len, len);
+ config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
+ } else {
+ parser_config_wb_write(self, &config->decimal_class, rb_mKernel);
+ config->decimal_method_id = SYM2ID(rb_str_intern(name));
+ }
+ }
+ }
+ }
+ else if (args->strict) {
+ if (!args->unknown_keywords) {
+ args->unknown_keywords = rb_obj_hide(rb_ary_new());
+ }
+ rb_ary_push(args->unknown_keywords, key);
+ }
+
+ return ST_CONTINUE;
+}
+
+static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self, bool strict)
+{
+ config->max_nesting = 100;
+
+ struct parser_config_init_args args = {
+ .config = config,
+ .self = self,
+ .strict = strict,
+ };
+
+ if (NIL_P(opts)) return;
+ Check_Type(opts, T_HASH);
+ if (RHASH_SIZE(opts) == 0) return;
+
+ // We assume in most cases few keys are set so it's faster to go over
+ // the provided keys than to check all possible keys.
+ rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
+
+ if (RB_UNLIKELY(args.unknown_keywords)) {
+ if (RARRAY_LEN(args.unknown_keywords) == 1) {
+ rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, RARRAY_AREF(args.unknown_keywords, 0));
+ }
+ else {
+ VALUE keywords = rb_ary_join(args.unknown_keywords, rb_utf8_str_new_cstr(", "));
+ rb_raise(rb_eArgError, "unknown keywords: %" PRIsVALUE, keywords);
+ }
+ }
+}
+
+/*
+ * call-seq: new(opts => {})
+ *
+ * Creates a new JSON::Ext::ParserConfig instance.
+ *
+ * Argument +opts+, if given, contains a \Hash of options for the parsing.
+ * See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ *
+ */
+static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
+{
+ rb_check_frozen(self);
+ GET_PARSER_CONFIG;
+
+ parser_config_init(config, opts, self, false);
+
+ return self;
+}
+
+static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
+{
+ VALUE Vsource = convert_encoding(src);
+
+ // Ensure the string isn't mutated under us.
+ // The classic API to use is `rb_str_locktmp`, but then we'd
+ // need to use `rb_protect` to make sure we always unlock.
+ if (Vsource == src) {
+ Vsource = rb_str_new_frozen(Vsource);
+ }
+
+ VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
+ rvalue_stack value_stack = {
+ .type = RVALUE_STACK_STACK_ALLOCATED,
+ .ptr = rvalue_stack_buffer,
+ .capa = RVALUE_STACK_INITIAL_CAPA,
+ };
+
+ // Seed the frame stack with the root frame, establishing the invariant that
+ // json_parse_any always has a top frame to dispatch on (so the stack is never
+ // empty mid-parse).
+ json_frame frame_stack_buffer[JSON_FRAME_STACK_INITIAL_CAPA];
+ frame_stack_buffer[0] = (json_frame){
+ .type = JSON_FRAME_ROOT,
+ .phase = JSON_PHASE_VALUE,
+ };
+ json_frame_stack frames = {
+ .type = RVALUE_STACK_STACK_ALLOCATED,
+ .ptr = frame_stack_buffer,
+ .capa = JSON_FRAME_STACK_INITIAL_CAPA,
+ .head = 1,
+ };
+
+ long len;
+ const char *start;
+
+ RSTRING_GETMEM(Vsource, start, len);
+
+ VALUE value_stack_handle = 0;
+ VALUE frame_stack_handle = 0;
+ JSON_ParserState _state = {
+ .start = start,
+ .cursor = start,
+ .end = start + len,
+ .value_stack = &value_stack,
+ .value_stack_handle = &value_stack_handle,
+ .frames = &frames,
+ .frame_stack_handle = &frame_stack_handle,
+ };
+ JSON_ParserState *state = &_state;
+
+ bool complete = json_parse_any(state, config, false);
+
+ // The root document value is parsed; it is the lone survivor on
+ // the rvalue stack.
+ VALUE result = complete ? *rvalue_stack_peek(state->value_stack, 1) : Qundef;
+
+ // This may be skipped in case of exception, but
+ // it won't cause a leak.
+ rvalue_stack_eagerly_release(value_stack_handle);
+ json_frame_stack_eagerly_release(frame_stack_handle);
+ RB_GC_GUARD(value_stack_handle);
+ RB_GC_GUARD(frame_stack_handle);
+ RB_GC_GUARD(Vsource);
+
+ if (complete) {
+ json_ensure_eof(state, config);
+ } else {
+ raise_eos_error("unexpected end of input", state);
+ }
+
+ return result;
+}
+
+/*
+ * call-seq: parse(source)
+ *
+ * Parses the current JSON text _source_ and returns the complete data
+ * structure as a result.
+ * It raises JSON::ParserError if fail to parse.
+ */
+static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
+{
+ GET_PARSER_CONFIG;
+ return cParser_parse(config, Vsource);
+}
+
+static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
+{
+ JSON_ParserConfig _config = {0};
+ JSON_ParserConfig *config = &_config;
+ parser_config_init(config, opts, Qfalse, false);
+
+ return cParser_parse(config, Vsource);
+}
+
+static void JSON_ParserConfig_mark(void *ptr)
+{
+ JSON_ParserConfig *config = ptr;
+ rb_gc_mark_movable(config->on_load_proc);
+ rb_gc_mark_movable(config->decimal_class);
+}
+
+static size_t JSON_ParserConfig_memsize(const void *ptr)
+{
+#ifdef HAVE_RUBY_TYPED_EMBEDDABLE
+ return 0;
+#else
+ return sizeof(JSON_ParserConfig);
+#endif
+}
+
+static void JSON_ParserConfig_compact(void *ptr)
+{
+ JSON_ParserConfig *config = ptr;
+ config->on_load_proc = rb_gc_location(config->on_load_proc);
+ config->decimal_class = rb_gc_location(config->decimal_class);
+}
+
+static const rb_data_type_t JSON_ParserConfig_type = {
+ .wrap_struct_name = "JSON::Ext::Parser/ParserConfig",
+ .function = {
+ .dmark = JSON_ParserConfig_mark,
+ .dfree = RUBY_DEFAULT_FREE,
+ .dsize = JSON_ParserConfig_memsize,
+ .dcompact = JSON_ParserConfig_compact,
+ },
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_EMBEDDABLE,
+};
+
+static VALUE cJSON_parser_s_allocate(VALUE klass)
+{
+ JSON_ParserConfig *config;
+ return TypedData_Make_Struct(klass, JSON_ParserConfig, &JSON_ParserConfig_type, config);
+}
+
+static void json_str_clear(VALUE str)
+{
+ if (RB_OBJ_FROZEN_RAW(str)) {
+ return;
+ }
+ rb_str_replace(str, JSON_empty_string);
+}
+
+typedef struct JSON_ResumableParserStruct {
+ JSON_ParserConfig config;
+ JSON_ParserState state;
+ rvalue_stack value_stack;
+ json_frame_stack frames;
+ VALUE buffer;
+ size_t parsed_bytes;
+ size_t incomplete_bytes;
+ bool complete;
+ bool in_use;
+} JSON_ResumableParser;
+
+static void JSON_ResumableParser_mark(void *ptr)
+{
+ JSON_ResumableParser *parser = (JSON_ResumableParser *)ptr;
+ JSON_ParserConfig_mark(&parser->config);
+ rvalue_stack_mark(&parser->value_stack);
+ rvalue_cache_mark(&parser->state.name_cache);
+ rb_gc_mark(parser->buffer); // pin the buffer
+ rb_gc_mark_movable(parser->state.parser);
+}
+
+static void JSON_ResumableParser_free(void *ptr)
+{
+ JSON_ResumableParser *parser = (JSON_ResumableParser *)ptr;
+ rvalue_stack_free_buffer(&parser->value_stack);
+ json_frame_stack_free_buffer(&parser->frames);
+}
+
+static size_t JSON_ResumableParser_memsize(const void *ptr)
+{
+ const JSON_ResumableParser *parser = (const JSON_ResumableParser *)ptr;
+ size_t memsize = JSON_ParserConfig_memsize(&parser->config);
+ memsize += rvalue_stack_memsize(&parser->value_stack);
+ memsize += json_frame_stack_memsize(&parser->frames);
+#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
+ memsize += (
+ sizeof(JSON_ResumableParser)
+ - sizeof(JSON_ParserState)
+ - sizeof(JSON_ParserConfig)
+ - sizeof(rvalue_stack)
+ - sizeof(json_frame_stack)
+ );
+#endif
+ return memsize;
+}
+
+static void JSON_ResumableParser_compact(void *ptr)
+{
+ JSON_ResumableParser *parser = (JSON_ResumableParser *)ptr;
+ JSON_ParserConfig_compact(&parser->config);
+ rvalue_stack_compact(&parser->value_stack);
+ rvalue_cache_compact(&parser->state.name_cache);
+ parser->buffer = rb_gc_location(parser->buffer);
+ parser->state.parser = rb_gc_location(parser->state.parser);
+}
+
+static const rb_data_type_t JSON_ResumableParser_type = {
+ .wrap_struct_name = "JSON::Ext::ResumableParser",
+ .function = {
+ JSON_ResumableParser_mark,
+ JSON_ResumableParser_free,
+ JSON_ResumableParser_memsize,
+ JSON_ResumableParser_compact,
+ },
+ // RUBY_TYPED_WB_PROTECTED is deliberately not declared because
+ // this is a superset of JSON_Parser_rvalue_stack_type, so we'd need
+ // to trigger a lot of write barriers.
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_EMBEDDABLE,
+};
+
+static VALUE cResumableParser_allocate(VALUE klass)
+{
+ JSON_ResumableParser *parser;
+ VALUE obj = TypedData_Make_Struct(klass, JSON_ResumableParser, &JSON_ResumableParser_type, parser);
+ parser->state.in_array++;
+ parser->state.parser = obj;
+ return obj;
+}
+
+static inline JSON_ResumableParser *cResumableParser_get(VALUE self)
+{
+ JSON_ResumableParser *parser;
+ TypedData_Get_Struct(self, JSON_ResumableParser, &JSON_ResumableParser_type, parser);
+ return parser;
+}
+
+/*
+ * call-seq: new(opts => {})
+ *
+ * Creates a new JSON::ResumableParser instance.
+ *
+ * Argument +opts+, if given, contains a \Hash of options for the parsing.
+ * See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ *
+ * A ResumableParser is able to parse partial documents and resume parsing later
+ * when more of the document is provided:
+ *
+ * parser = JSON::ResumableParser.new
+ * parser << '{"user": "george", "role": "ad'
+ * parser.parse # => false
+ * parser.eos? # => true
+ * parser.partial_value # => { "user" => "george", "role" => nil }
+ * parser.rest # => '"ad'
+ *
+ * parser << 'min" }[1, 2, 3]'
+ * parser.parse # => true
+ * parser.value # => { "user" => "george", "role" => "admin" }
+ *
+ * parser.parse # => true
+ * parser.value # => [1, 2, 3]
+ *
+ * === Limitations
+ *
+ * While ResumableParser is able to parse streams of documents without any
+ * explicit separators between them, it is highly recommended to separate documents
+ * by either spaces or newlines, as otherwise the \JSON syntax for numbers may be ambiguous.
+ * When parsing a number, ResumableParser will not consider the number complete until something follows:
+ *
+ * parser << '123'
+ * parser.parse # => false
+ * parser << ' '
+ * parser.parse # => true
+ * parser.value # => 123
+ *
+ * === Security
+ *
+ * An incomplete document is buffered in full and there is no size limit, so when reading
+ * from an untrusted source the caller is responsible for bounding how much data is fed.
+ * For example:
+ *
+ * loop do
+ * if parser.parsed_bytes > DOCUMENT_MAX_SIZE
+ * raise "document too large"
+ * end
+ *
+ * parser << read_chunk
+ * while parser.parse
+ * process(parser.value)
+ * end
+ * end
+ */
+static VALUE cResumableParser_initialize(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_frozen(self);
+
+ VALUE opts = Qfalse;
+ rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "0:", &opts);
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+
+ opts = argc > 0 ? argv[0] : Qnil;
+ parser_config_init(&parser->config, opts, self, true);
+
+ return self;
+}
+
+static JSON_ResumableParser *ResumableParser_acquire(VALUE self, bool lock);
+
+/*
+ * call-seq: self << string -> self
+ *
+ * Appends the given string to the parser's buffer.
+ */
+static VALUE cResumableParser_feed(VALUE self, VALUE str)
+{
+ rb_check_frozen(self);
+
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
+
+ str = convert_encoding(str);
+ if (!RSTRING_LEN(str)) {
+ return self;
+ }
+
+ size_t offset = parser->state.cursor - parser->state.start;
+ const size_t remaining = parser->state.end - parser->state.cursor;
+
+ if (!remaining) {
+ if (parser->buffer) {
+ json_str_clear(parser->buffer);
+ }
+ parser->buffer = RB_OBJ_FROZEN_RAW(str) ? str : rb_obj_hide(rb_str_new_shared(str));
+ offset = 0;
+ } else {
+ JSON_ASSERT(parser->buffer);
+
+ const size_t size = parser->state.end - parser->state.start;
+ const size_t consumed = size - remaining;
+
+ if (RB_OBJ_FROZEN_RAW(parser->buffer)) {
+ VALUE new_buffer = rb_obj_hide(rb_str_buf_new(remaining + RSTRING_LEN(str)));
+ rb_enc_associate_index(new_buffer, utf8_encindex);
+
+ char *old_ptr = RSTRING_PTR(parser->buffer);
+ memcpy(RSTRING_PTR(new_buffer), old_ptr + consumed, remaining);
+ rb_str_set_len(new_buffer, remaining);
+ offset = 0;
+ parser->buffer = new_buffer;
+ } else if (consumed > (size / 2) && size >= 512) {
+ rb_str_modify(parser->buffer);
+ char *old_ptr = RSTRING_PTR(parser->buffer);
+ memmove(old_ptr, old_ptr + consumed, remaining);
+ rb_str_set_len(parser->buffer, remaining);
+ offset = 0;
+ }
+ rb_str_append(parser->buffer, str);
+ }
+
+ long len;
+ const char *start;
+ RSTRING_GETMEM(parser->buffer, start, len);
+ parser->state.start = start;
+ parser->state.end = start + len;
+ parser->state.cursor = parser->state.start + offset;
+
+ return self;
+}
+
+struct json_parse_any_args {
+ JSON_ParserState *state;
+ JSON_ParserConfig *config;
+ VALUE parser;
+};
+
+static VALUE json_parse_any_resumable_safe0(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, _args))
+{
+ struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
+ return (VALUE)json_parse_any(args->state, args->config, true);
+}
+
+static VALUE json_parse_any_resumable_safe(VALUE _args)
+{
+ struct json_parse_any_args *args = (struct json_parse_any_args *)_args;
+ VALUE result = rb_catch_obj(args->parser, json_parse_any_resumable_safe0, _args);
+ return result == args->parser ? Qfalse : result;
+}
+
+static JSON_ResumableParser *ResumableParser_acquire(VALUE self, bool lock)
+{
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+
+ if (parser->in_use) {
+ rb_raise(rb_eArgError, "ResumableParser can't be used recursively");
+ }
+
+ if (lock) {
+ parser->in_use = true;
+ }
+
+ // self may have moved, so we need to update all pointers
+ // Investigate: We might be better off keeping JSON_ParserState on the stack
+ // and only persist what we need.
+ parser->state.value_stack = &parser->value_stack;
+ parser->state.frames = &parser->frames;
+
+ return parser;
+}
+
+/*
+ * call-seq: parse -> true or false
+ *
+ * Attemps to parse a JSON document from the internal buffer.
+ * Returns whether a complete document could be parsed.
+ *
+ * It does raise +JSON::ParserError+ when encountering invalid \JSON syntax.
+ *
+ * The parsed object can be retrieved by calling #value
+ */
+static VALUE cResumableParser_parse(VALUE self)
+{
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, true);
+
+ if (parser->complete) {
+ parser->parsed_bytes = 0;
+ parser->incomplete_bytes = 0;
+ parser->complete = false;
+ }
+
+ if (!parser->buffer) {
+ parser->in_use = false;
+ return Qfalse;
+ }
+
+ if (parser->frames.head == 0) {
+ json_frame_stack_push(&parser->state, (json_frame){
+ .type = JSON_FRAME_ROOT,
+ .phase = JSON_PHASE_VALUE,
+ });
+ }
+
+ VALUE Vsource = parser->buffer; // Prevent compaction
+
+ json_frame *frame = json_frame_stack_peek(&parser->frames);
+
+ if (frame->phase == JSON_PHASE_DONE) {
+ JSON_ASSERT(parser->value_stack.head == 1);
+ JSON_ASSERT(parser->frames.head == 1);
+
+ frame->phase = JSON_PHASE_VALUE;
+ rvalue_stack_pop(parser->state.value_stack, 1);
+ }
+
+ struct json_parse_any_args args = {
+ .state = &parser->state,
+ .config = &parser->config,
+ .parser = self,
+ };
+ int status;
+ const char *initial_cursor = parser->state.cursor;
+ parser->complete = rb_protect(json_parse_any_resumable_safe, (VALUE)&args, &status);
+
+ if (status) {
+ parser->complete = true; // a parse error is considered complete
+ }
+
+ parser->parsed_bytes += parser->state.cursor - initial_cursor;
+ parser->incomplete_bytes = parser->complete ? 0 : parser->state.end - parser->state.cursor;
+
+ json_eat_whitespace(&parser->state, &parser->config, false);
+ if (eos(&parser->state)) {
+ json_str_clear(parser->buffer);
+ parser->buffer = Qfalse;
+ }
+ parser->in_use = false;
+
+ if (status) {
+ rb_jump_tag(status); // reraise
+ }
+ RB_GC_GUARD(Vsource);
+ return parser->complete ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: value? -> true or false
+ *
+ * Returns whether a parsed value is available.
+ */
+static VALUE cResumableParser_value_p(VALUE self)
+{
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
+
+ if (parser->value_stack.head > 0) {
+ json_frame *frame = json_frame_stack_peek(&parser->frames);
+ if (frame->phase == JSON_PHASE_DONE) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq: value -> object
+ *
+ * Returns and consume the last parsed value.
+ * Raises ArgumentError if there is no parsed value or if it was already retrieved:
+ * parser << '[1][2]'
+ * parser.value # ArgumentError no ready value
+ * parser.parse # => true
+ * parser.value # => [1]
+ * parser.value # ArgumentError no ready value
+ */
+static VALUE cResumableParser_value(VALUE self)
+{
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
+
+ if (parser->frames.head > 0) {
+ json_frame *frame = json_frame_stack_peek(&parser->frames);
+
+ if (frame->phase == JSON_PHASE_DONE) {
+ VALUE result = *rvalue_stack_peek(parser->state.value_stack, 1);
+ rvalue_stack_pop(parser->state.value_stack, 1);
+ json_frame_stack_pop(parser->state.frames);
+ return result;
+ }
+ }
+ rb_raise(rb_eArgError, "no ready value");
+}
+
+/*
+ * call-seq: clear -> self
+ *
+ * Entirely reset the parser state and buffer.
+ */
+static VALUE cResumableParser_clear(VALUE self)
+{
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, false);
+ parser->buffer = 0;
+ parser->complete = true;
+ parser->parsed_bytes = 0;
+ parser->incomplete_bytes = 0;
+ parser->frames.head = 0;
+ parser->value_stack.head = 0;
+ parser->state.name_cache.length = 0;
+ parser->state.current_nesting = 0;
+ parser->state.in_array = 1;
+ parser->state.emitted_deprecations = 0;
+ parser->state.start = parser->state.cursor = parser->state.end = NULL;
+ return self;
+}
+
+static VALUE cResumableParser_partial_value_body(VALUE self)
+{
+ JSON_ResumableParser *original_parser = cResumableParser_get(self);
+ JSON_ResumableParser parser = *original_parser;
+
+ parser.state.frames = &parser.frames;
+ parser.state.value_stack = &parser.value_stack;
+
+ if (parser.value_stack.head == 0) {
+ return Qnil;
+ }
+
+ json_frame *frame = json_frame_stack_peek(parser.state.frames);
+ long missing_object_value = 0;
+ if (frame->type == JSON_FRAME_OBJECT && (frame->phase == JSON_PHASE_VALUE || frame->phase == JSON_PHASE_OBJECT_COLON)) {
+ missing_object_value = 1;
+ }
+
+ // Copy the value stack as we need to mutate it. The collapse loop folds each
+ // open container by popping its entries and pushing the single result, so a
+ // parent always reclaims its child's slot; head exceeds its live size by at
+ // most one, either for the missing-value placeholder pushed below or for the
+ // result of folding an empty innermost container. That one spare slot keeps
+ // rvalue_stack_push from growing (reallocating) this ALLOCV buffer.
+ long capa = parser.value_stack.head;
+ parser.value_stack.capa = capa + 1;
+ VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, parser.value_stack.capa);
+ MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, capa);
+ parser.value_stack.ptr = value_stack_buffer;
+
+ JSON_ParserState *state = &parser.state;
+ JSON_ParserConfig *config = &parser.config;
+
+ if (missing_object_value) {
+ rvalue_stack_push(state->value_stack, Qnil, NULL, &state->value_stack);
+ }
+
+ VALUE partial_result = Qundef;
+
+ while (UNDEF_P(partial_result)) {
+ frame = json_frame_stack_peek(state->frames);
+
+ switch (frame->type) {
+ case JSON_FRAME_ROOT: {
+ partial_result = *rvalue_stack_peek(state->value_stack, 1);
+ break;
+ }
+
+ case JSON_FRAME_ARRAY: {
+ long count = json_frame_entry_count(frame, state->value_stack);
+ json_push_value(state, config, json_decode_array(state, config, count));
+ json_frame_stack_pop(state->frames);
+
+ break;
+ }
+
+ case JSON_FRAME_OBJECT: {
+ long count = json_frame_entry_count(frame, state->value_stack);
+ json_push_value(state, config, json_decode_object(state, config, count));
+ json_frame_stack_pop(state->frames);
+ break;
+ }
+
+ default: {
+ JSON_UNREACHABLE_RETURN(Qundef);
+ break;
+ }
+ }
+ }
+
+ ALLOCV_END(tmpbuf);
+ return partial_result;
+}
+
+/*
+ * call-seq: partial_value -> object
+ *
+ * Returns the Ruby objects parsed up to this point:
+ * parser << '[1, [2, 3,'
+ * parser.parse # => false
+ * parser.value # ArgumentError no ready value
+ * parser.partial_value # => [1, [2, 3]]
+ */
+static VALUE cResumableParser_partial_value(VALUE self)
+{
+ JSON_ResumableParser *parser = ResumableParser_acquire(self, true);
+
+ int status;
+ VALUE result = rb_protect(cResumableParser_partial_value_body, self, &status);
+ parser->in_use = false;
+ if (status) {
+ rb_jump_tag(status);
+ }
+ return result;
+}
+
+/*
+ * call-seq: rest -> string
+ *
+ * Returns a string containing what remains to be parsed in the buffer
+ * parser << '{ "message": "unterminated message'
+ * parser.parse # => false
+ * parser.rest # => '"unterminated message"'
+ */
+static VALUE cResumableParser_rest(VALUE self)
+{
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+
+ if (!parser->buffer) {
+ return rb_utf8_str_new("", 0);
+ }
+
+ size_t offset = parser->state.cursor - parser->state.start;
+ const char *ptr;
+ long len;
+ RSTRING_GETMEM(parser->buffer, ptr, len);
+ return rb_utf8_str_new(ptr + offset, len - offset);
+}
+
+/*
+ * call-seq: eos? -> true or false
+ *
+ * Returns whether the internal buffer has been entirely consumed.
+ */
+static VALUE cResumableParser_eos_p(VALUE self)
+{
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+ return eos(&parser->state) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq: partial_value? -> true or false
+ *
+ * Returns whether a document is currently under construction: an unclosed
+ * container, a key awaiting its value, etc.
+ *
+ * It answers the same question as <tt>!partial_value.nil?</tt>, but as a
+ * cheap predicate on the parser's internal state, without materializing the
+ * partially parsed Ruby objects:
+ * parser << '{"a":1,'
+ * parser.parse # => false
+ * parser.partial_value? # => true
+ *
+ * A fully parsed document whose value hasn't been retrieved yet is not under
+ * construction: #value? returns true and #partial_value? returns false.
+ */
+static VALUE cResumableParser_partial_value_p(VALUE self)
+{
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+
+ // Mirror of #value?: values on the stack while the document isn't DONE
+ // belong to a partially built document. A container whose first key or
+ // element hasn't been parsed yet has no frame nor value registered (the
+ // tokenizer rewinds to the container start on EOS), so that state is
+ // observable through the buffer (#eos?/#rest) instead, keeping this
+ // predicate consistent with #partial_value returning nil.
+ if (parser->value_stack.head > 0) {
+ json_frame *frame = json_frame_stack_peek(&parser->frames);
+ if (frame->phase != JSON_PHASE_DONE) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq: parsed_bytes -> integer
+ *
+ * Returns the number of bytes parsed since the start of the current partial value.
+ * This is intended to be used for securing against untrusted input:
+ *
+ * loop do
+ * if parser.parsed_bytes > DOCUMENT_MAX_SIZE
+ * raise "document too large"
+ * end
+ *
+ * parser << read_chunk
+ * while parser.parse
+ * process(parser.value)
+ * end
+ * end
+ */
+static VALUE cResumableParser_parsed_bytes(VALUE self)
+{
+ JSON_ResumableParser *parser = cResumableParser_get(self);
+ return ULL2NUM(parser->parsed_bytes + parser->incomplete_bytes);
+}
+
+void Init_parser(void)
+{
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
+
+#undef rb_intern
+ rb_require("json/common");
+ mJSON = rb_define_module("JSON");
+ VALUE mExt = rb_define_module_under(mJSON, "Ext");
+ VALUE cParserConfig = rb_define_class_under(mExt, "ParserConfig", rb_cObject);
+
+ rb_global_variable(&eParserError);
+ eParserError = rb_path2class("JSON::ParserError");
+
+ rb_global_variable(&eNestingError);
+ eNestingError = rb_path2class("JSON::NestingError");
+
+ rb_define_alloc_func(cParserConfig, cJSON_parser_s_allocate);
+ rb_define_private_method(cParserConfig, "initialize", cParserConfig_initialize, 1);
+ rb_define_method(cParserConfig, "parse", cParserConfig_parse, 1);
+
+ VALUE cParser = rb_define_class_under(mExt, "Parser", rb_cObject);
+ rb_define_singleton_method(cParser, "parse", cParser_m_parse, 2);
+
+ VALUE cResumableParser = rb_define_class_under(mJSON, "ResumableParser", rb_cObject);
+ rb_define_alloc_func(cResumableParser, cResumableParser_allocate);
+ rb_define_private_method(cResumableParser, "initialize", cResumableParser_initialize, -1);
+ rb_define_method(cResumableParser, "<<", cResumableParser_feed, 1);
+ rb_define_method(cResumableParser, "parse", cResumableParser_parse, 0);
+ rb_define_method(cResumableParser, "value", cResumableParser_value, 0);
+ rb_define_method(cResumableParser, "value?", cResumableParser_value_p, 0);
+ rb_define_method(cResumableParser, "partial_value", cResumableParser_partial_value, 0);
+ rb_define_method(cResumableParser, "partial_value?", cResumableParser_partial_value_p, 0);
+ rb_define_method(cResumableParser, "clear", cResumableParser_clear, 0);
+ rb_define_method(cResumableParser, "rest", cResumableParser_rest, 0);
+ rb_define_method(cResumableParser, "eos?", cResumableParser_eos_p, 0);
+ rb_define_method(cResumableParser, "parsed_bytes", cResumableParser_parsed_bytes, 0);
+
+ rb_global_variable(&CNaN);
+ CNaN = rb_const_get(mJSON, rb_intern("NaN"));
+
+ rb_global_variable(&CInfinity);
+ CInfinity = rb_const_get(mJSON, rb_intern("Infinity"));
+
+ rb_global_variable(&CMinusInfinity);
+ CMinusInfinity = rb_const_get(mJSON, rb_intern("MinusInfinity"));
+
+ rb_global_variable(&Encoding_UTF_8);
+ Encoding_UTF_8 = rb_const_get(rb_path2class("Encoding"), rb_intern("UTF_8"));
+
+ rb_global_variable(&JSON_empty_string);
+ JSON_empty_string = rb_obj_hide(rb_utf8_str_new("", 0));
+
+ sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
+ sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
+ sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
+ sym_allow_comments = ID2SYM(rb_intern("allow_comments"));
+ sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
+ sym_allow_invalid_escape = ID2SYM(rb_intern("allow_invalid_escape"));
+ sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
+ sym_freeze = ID2SYM(rb_intern("freeze"));
+ sym_on_load = ID2SYM(rb_intern("on_load"));
+ sym_decimal_class = ID2SYM(rb_intern("decimal_class"));
+ sym_allow_duplicate_key = ID2SYM(rb_intern("allow_duplicate_key"));
+
+ i_new = rb_intern("new");
+ i_try_convert = rb_intern("try_convert");
+#ifndef HAVE_RB_STR_TO_INTERNED_STR
+ i_uminus = rb_intern("-@");
+#endif
+ i_encode = rb_intern("encode");
+ i_at_line = rb_intern("@line");
+ i_at_column = rb_intern("@column");
+
+ binary_encindex = rb_ascii8bit_encindex();
+ utf8_encindex = rb_utf8_encindex();
+ enc_utf8 = rb_utf8_encoding();
+
+#ifdef HAVE_SIMD
+ simd_impl = find_simd_implementation();
+#endif
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/conf.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/conf.rb
new file mode 100644
index 0000000..76f774b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/conf.rb
@@ -0,0 +1,24 @@
+case RbConfig::CONFIG['host_cpu']
+when /^(arm|aarch64)/
+ # Try to compile a small program using NEON instructions
+ header, type, init, extra = 'arm_neon.h', 'uint8x16_t', 'vdupq_n_u8(32)', nil
+when /^(x86_64|x64)/
+ header, type, init, extra = 'x86intrin.h', '__m128i', '_mm_set1_epi8(32)', 'if (__builtin_cpu_supports("sse2")) { printf("OK"); }'
+end
+if header
+ if have_header(header) && try_compile(<<~SRC, '-Werror=implicit-function-declaration')
+ #{cpp_include(header)}
+ int main(int argc, char **argv) {
+ #{type} test = #{init};
+ #{extra}
+ if (argc > 100000) printf("%p", &test);
+ return 0;
+ }
+ SRC
+ $defs.push("-DJSON_ENABLE_SIMD")
+ else
+ puts "Disable SIMD"
+ end
+end
+
+have_header('cpuid.h')
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/simd.h b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/simd.h
new file mode 100644
index 0000000..611b41b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/simd/simd.h
@@ -0,0 +1,208 @@
+#include "../json.h"
+
+typedef enum {
+ SIMD_NONE,
+ SIMD_NEON,
+ SIMD_SSE2
+} SIMD_Implementation;
+
+#ifndef __has_builtin // Optional of course.
+ #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+#ifdef __clang__
+# if __has_builtin(__builtin_ctzll)
+# define HAVE_BUILTIN_CTZLL 1
+# else
+# define HAVE_BUILTIN_CTZLL 0
+# endif
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+# define HAVE_BUILTIN_CTZLL 1
+#else
+# define HAVE_BUILTIN_CTZLL 0
+#endif
+
+static inline uint32_t trailing_zeros64(uint64_t input)
+{
+ JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior
+
+#if HAVE_BUILTIN_CTZLL
+ return __builtin_ctzll(input);
+#else
+ uint32_t trailing_zeros = 0;
+ uint64_t temp = input;
+ while ((temp & 1) == 0 && temp > 0) {
+ trailing_zeros++;
+ temp >>= 1;
+ }
+ return trailing_zeros;
+#endif
+}
+
+static inline int trailing_zeros(int input)
+{
+ JSON_ASSERT(input > 0); // __builtin_ctz(0) is undefined behavior
+
+#if HAVE_BUILTIN_CTZLL
+ return __builtin_ctz(input);
+#else
+ int trailing_zeros = 0;
+ int temp = input;
+ while ((temp & 1) == 0 && temp > 0) {
+ trailing_zeros++;
+ temp >>= 1;
+ }
+ return trailing_zeros;
+#endif
+}
+
+#ifdef JSON_ENABLE_SIMD
+
+#define SIMD_MINIMUM_THRESHOLD 4
+
+ALWAYS_INLINE(static) void json_fast_memcpy16(char *dst, const char *src, size_t len)
+{
+ RBIMPL_ASSERT_OR_ASSUME(len < 16);
+ RBIMPL_ASSERT_OR_ASSUME(len >= SIMD_MINIMUM_THRESHOLD); // 4
+#if defined(__has_builtin) && __has_builtin(__builtin_memcpy)
+ // If __builtin_memcpy is available, use it to copy between SIMD_MINIMUM_THRESHOLD (4) and vec_len-1 (15) bytes.
+ // These copies overlap. The first copy will copy the first 8 (or 4) bytes. The second copy will copy
+ // the last 8 (or 4) bytes but overlap with the first copy. The overlapping bytes will be in the correct
+ // position in both copies.
+
+ // Please do not attempt to replace __builtin_memcpy with memcpy without profiling and/or looking at the
+ // generated assembly. On clang-specifically (tested on Apple clang version 17.0.0 (clang-1700.0.13.3)),
+ // when using memcpy, the compiler will notice the only difference is a 4 or 8 and generate a conditional
+ // select instruction instead of direct loads and stores with a branch. This ends up slower than the branch
+ // plus two loads and stores generated when using __builtin_memcpy.
+ if (len >= 8) {
+ __builtin_memcpy(dst, src, 8);
+ __builtin_memcpy(dst + len - 8, src + len - 8, 8);
+ } else {
+ __builtin_memcpy(dst, src, 4);
+ __builtin_memcpy(dst + len - 4, src + len - 4, 4);
+ }
+#else
+ MEMCPY(dst, src, char, len);
+#endif
+}
+
+#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64)
+#include <arm_neon.h>
+
+#define FIND_SIMD_IMPLEMENTATION_DEFINED 1
+static inline SIMD_Implementation find_simd_implementation(void)
+{
+ return SIMD_NEON;
+}
+
+#define HAVE_SIMD 1
+#define HAVE_SIMD_NEON 1
+
+// See: https://community.arm.com/arm-community-blogs/b/servers-and-cloud-computing-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
+ALWAYS_INLINE(static) uint64_t neon_match_mask(uint8x16_t matches)
+{
+ const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(matches), 4);
+ const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
+ return mask & 0x8888888888888888ull;
+}
+
+ALWAYS_INLINE(static) uint64_t compute_chunk_mask_neon(const char *ptr)
+{
+ uint8x16_t chunk = vld1q_u8((const unsigned char *)ptr);
+
+ // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
+ // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
+ const uint8x16_t too_low_or_dbl_quote = vcltq_u8(veorq_u8(chunk, vdupq_n_u8(2)), vdupq_n_u8(33));
+
+ uint8x16_t has_backslash = vceqq_u8(chunk, vdupq_n_u8('\\'));
+ uint8x16_t needs_escape = vorrq_u8(too_low_or_dbl_quote, has_backslash);
+ return neon_match_mask(needs_escape);
+}
+
+ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *end, uint64_t *mask)
+{
+ while (*ptr + sizeof(uint8x16_t) <= end) {
+ uint64_t chunk_mask = compute_chunk_mask_neon(*ptr);
+ if (chunk_mask) {
+ *mask = chunk_mask;
+ return 1;
+ }
+ *ptr += sizeof(uint8x16_t);
+ }
+ return 0;
+}
+
+#endif /* ARM Neon Support.*/
+
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
+
+#ifdef HAVE_X86INTRIN_H
+#include <x86intrin.h>
+
+#define HAVE_SIMD 1
+#define HAVE_SIMD_SSE2 1
+
+#ifdef HAVE_CPUID_H
+#define FIND_SIMD_IMPLEMENTATION_DEFINED 1
+
+#if defined(__clang__) || defined(__GNUC__)
+#define TARGET_SSE2 __attribute__((target("sse2")))
+#else
+#define TARGET_SSE2
+#endif
+
+#define _mm_cmpge_epu8(a, b) _mm_cmpeq_epi8(_mm_max_epu8(a, b), a)
+#define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a)
+#define _mm_cmpgt_epu8(a, b) _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))
+#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)
+
+ALWAYS_INLINE(static) TARGET_SSE2 int compute_chunk_mask_sse2(const char *ptr)
+{
+ __m128i chunk = _mm_loadu_si128((__m128i const*)ptr);
+ // Trick: c < 32 || c == 34 can be factored as c ^ 2 < 33
+ // https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/
+ __m128i too_low_or_dbl_quote = _mm_cmplt_epu8(_mm_xor_si128(chunk, _mm_set1_epi8(2)), _mm_set1_epi8(33));
+ __m128i has_backslash = _mm_cmpeq_epi8(chunk, _mm_set1_epi8('\\'));
+ __m128i needs_escape = _mm_or_si128(too_low_or_dbl_quote, has_backslash);
+ return _mm_movemask_epi8(needs_escape);
+}
+
+ALWAYS_INLINE(static) TARGET_SSE2 int string_scan_simd_sse2(const char **ptr, const char *end, int *mask)
+{
+ while (*ptr + sizeof(__m128i) <= end) {
+ int chunk_mask = compute_chunk_mask_sse2(*ptr);
+ if (chunk_mask) {
+ *mask = chunk_mask;
+ return 1;
+ }
+ *ptr += sizeof(__m128i);
+ }
+
+ return 0;
+}
+
+#include <cpuid.h>
+#endif /* HAVE_CPUID_H */
+
+static inline SIMD_Implementation find_simd_implementation(void)
+{
+ // TODO Revisit. I think the SSE version now only uses SSE2 instructions.
+ if (__builtin_cpu_supports("sse2")) {
+ return SIMD_SSE2;
+ }
+
+ return SIMD_NONE;
+}
+
+#endif /* HAVE_X86INTRIN_H */
+#endif /* X86_64 Support */
+
+#endif /* JSON_ENABLE_SIMD */
+
+#ifndef FIND_SIMD_IMPLEMENTATION_DEFINED
+static inline SIMD_Implementation find_simd_implementation(void)
+{
+ return SIMD_NONE;
+}
+#endif
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fast_float_parser.h b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fast_float_parser.h
new file mode 100644
index 0000000..6e43b25
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fast_float_parser.h
@@ -0,0 +1,814 @@
+/* Eisel-Lemire decimal->double, ported from fast_float:
+ * include/fast_float/decimal_to_binary.h, the compute_float<binary_format<double>>
+ * + compute_product_approximation routines.
+ *
+ * Algorithm authors: Michael Eisel (original approach) and Daniel Lemire
+ * (formalization, proof, and the fast_float implementation) — hence "Eisel-Lemire".
+ *
+ * Copyright (c) 2021 The fast_float authors. Tri-licensed Apache-2.0 / MIT / BSL-1.0;
+ * used here under MIT — see LICENSE-fast_float-MIT in this directory.
+ *
+ * C99 Port by Tilo Sloboda.
+ */
+
+#ifndef FFP_H
+#define FFP_H
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define FFP_SMALLEST_POWER_OF_FIVE (-342)
+#define FFP_LARGEST_POWER_OF_FIVE (308)
+static const uint64_t ffp_power_of_five_128[2 * (308 - (-342) + 1)] = {
+ 0xeef453d6923bd65a, 0x113faa2906a13b3f,
+ 0x9558b4661b6565f8, 0x4ac7ca59a424c507,
+ 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
+ 0xe95a99df8ace6f53, 0xf4d82c2c107973dc,
+ 0x91d8a02bb6c10594, 0x79071b9b8a4be869,
+ 0xb64ec836a47146f9, 0x9748e2826cdee284,
+ 0xe3e27a444d8d98b7, 0xfd1b1b2308169b25,
+ 0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7,
+ 0xb208ef855c969f4f, 0xbdbd2d335e51a935,
+ 0xde8b2b66b3bc4723, 0xad2c788035e61382,
+ 0x8b16fb203055ac76, 0x4c3bcb5021afcc31,
+ 0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d,
+ 0xd953e8624b85dd78, 0xd71d6dad34a2af0d,
+ 0x87d4713d6f33aa6b, 0x8672648c40e5ad68,
+ 0xa9c98d8ccb009506, 0x680efdaf511f18c2,
+ 0xd43bf0effdc0ba48, 0x212bd1b2566def2,
+ 0x84a57695fe98746d, 0x14bb630f7604b57,
+ 0xa5ced43b7e3e9188, 0x419ea3bd35385e2d,
+ 0xcf42894a5dce35ea, 0x52064cac828675b9,
+ 0x818995ce7aa0e1b2, 0x7343efebd1940993,
+ 0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8,
+ 0xca66fa129f9b60a6, 0xd41a26e077774ef6,
+ 0xfd00b897478238d0, 0x8920b098955522b4,
+ 0x9e20735e8cb16382, 0x55b46e5f5d5535b0,
+ 0xc5a890362fddbc62, 0xeb2189f734aa831d,
+ 0xf712b443bbd52b7b, 0xa5e9ec7501d523e4,
+ 0x9a6bb0aa55653b2d, 0x47b233c92125366e,
+ 0xc1069cd4eabe89f8, 0x999ec0bb696e840a,
+ 0xf148440a256e2c76, 0xc00670ea43ca250d,
+ 0x96cd2a865764dbca, 0x380406926a5e5728,
+ 0xbc807527ed3e12bc, 0xc605083704f5ecf2,
+ 0xeba09271e88d976b, 0xf7864a44c633682e,
+ 0x93445b8731587ea3, 0x7ab3ee6afbe0211d,
+ 0xb8157268fdae9e4c, 0x5960ea05bad82964,
+ 0xe61acf033d1a45df, 0x6fb92487298e33bd,
+ 0x8fd0c16206306bab, 0xa5d3b6d479f8e056,
+ 0xb3c4f1ba87bc8696, 0x8f48a4899877186c,
+ 0xe0b62e2929aba83c, 0x331acdabfe94de87,
+ 0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14,
+ 0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9,
+ 0xdb71e91432b1a24a, 0xc9e82cd9f69d6150,
+ 0x892731ac9faf056e, 0xbe311c083a225cd2,
+ 0xab70fe17c79ac6ca, 0x6dbd630a48aaf406,
+ 0xd64d3d9db981787d, 0x92cbbccdad5b108,
+ 0x85f0468293f0eb4e, 0x25bbf56008c58ea5,
+ 0xa76c582338ed2621, 0xaf2af2b80af6f24e,
+ 0xd1476e2c07286faa, 0x1af5af660db4aee1,
+ 0x82cca4db847945ca, 0x50d98d9fc890ed4d,
+ 0xa37fce126597973c, 0xe50ff107bab528a0,
+ 0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8,
+ 0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a,
+ 0x9faacf3df73609b1, 0x77b191618c54e9ac,
+ 0xc795830d75038c1d, 0xd59df5b9ef6a2417,
+ 0xf97ae3d0d2446f25, 0x4b0573286b44ad1d,
+ 0x9becce62836ac577, 0x4ee367f9430aec32,
+ 0xc2e801fb244576d5, 0x229c41f793cda73f,
+ 0xf3a20279ed56d48a, 0x6b43527578c1110f,
+ 0x9845418c345644d6, 0x830a13896b78aaa9,
+ 0xbe5691ef416bd60c, 0x23cc986bc656d553,
+ 0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8,
+ 0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9,
+ 0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53,
+ 0xe858ad248f5c22c9, 0xd1b3400f8f9cff68,
+ 0x91376c36d99995be, 0x23100809b9c21fa1,
+ 0xb58547448ffffb2d, 0xabd40a0c2832a78a,
+ 0xe2e69915b3fff9f9, 0x16c90c8f323f516c,
+ 0x8dd01fad907ffc3b, 0xae3da7d97f6792e3,
+ 0xb1442798f49ffb4a, 0x99cd11cfdf41779c,
+ 0xdd95317f31c7fa1d, 0x40405643d711d583,
+ 0x8a7d3eef7f1cfc52, 0x482835ea666b2572,
+ 0xad1c8eab5ee43b66, 0xda3243650005eecf,
+ 0xd863b256369d4a40, 0x90bed43e40076a82,
+ 0x873e4f75e2224e68, 0x5a7744a6e804a291,
+ 0xa90de3535aaae202, 0x711515d0a205cb36,
+ 0xd3515c2831559a83, 0xd5a5b44ca873e03,
+ 0x8412d9991ed58091, 0xe858790afe9486c2,
+ 0xa5178fff668ae0b6, 0x626e974dbe39a872,
+ 0xce5d73ff402d98e3, 0xfb0a3d212dc8128f,
+ 0x80fa687f881c7f8e, 0x7ce66634bc9d0b99,
+ 0xa139029f6a239f72, 0x1c1fffc1ebc44e80,
+ 0xc987434744ac874e, 0xa327ffb266b56220,
+ 0xfbe9141915d7a922, 0x4bf1ff9f0062baa8,
+ 0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9,
+ 0xc4ce17b399107c22, 0xcb550fb4384d21d3,
+ 0xf6019da07f549b2b, 0x7e2a53a146606a48,
+ 0x99c102844f94e0fb, 0x2eda7444cbfc426d,
+ 0xc0314325637a1939, 0xfa911155fefb5308,
+ 0xf03d93eebc589f88, 0x793555ab7eba27ca,
+ 0x96267c7535b763b5, 0x4bc1558b2f3458de,
+ 0xbbb01b9283253ca2, 0x9eb1aaedfb016f16,
+ 0xea9c227723ee8bcb, 0x465e15a979c1cadc,
+ 0x92a1958a7675175f, 0xbfacd89ec191ec9,
+ 0xb749faed14125d36, 0xcef980ec671f667b,
+ 0xe51c79a85916f484, 0x82b7e12780e7401a,
+ 0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810,
+ 0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15,
+ 0xdfbdcece67006ac9, 0x67a791e093e1d49a,
+ 0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0,
+ 0xaecc49914078536d, 0x58fae9f773886e18,
+ 0xda7f5bf590966848, 0xaf39a475506a899e,
+ 0x888f99797a5e012d, 0x6d8406c952429603,
+ 0xaab37fd7d8f58178, 0xc8e5087ba6d33b83,
+ 0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64,
+ 0x855c3be0a17fcd26, 0x5cf2eea09a55067f,
+ 0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e,
+ 0xd0601d8efc57b08b, 0xf13b94daf124da26,
+ 0x823c12795db6ce57, 0x76c53d08d6b70858,
+ 0xa2cb1717b52481ed, 0x54768c4b0c64ca6e,
+ 0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09,
+ 0xfe5d54150b090b02, 0xd3f93b35435d7c4c,
+ 0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf,
+ 0xc6b8e9b0709f109a, 0x359ab6419ca1091b,
+ 0xf867241c8cc6d4c0, 0xc30163d203c94b62,
+ 0x9b407691d7fc44f8, 0x79e0de63425dcf1d,
+ 0xc21094364dfb5636, 0x985915fc12f542e4,
+ 0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d,
+ 0x979cf3ca6cec5b5a, 0xa705992ceecf9c42,
+ 0xbd8430bd08277231, 0x50c6ff782a838353,
+ 0xece53cec4a314ebd, 0xa4f8bf5635246428,
+ 0x940f4613ae5ed136, 0x871b7795e136be99,
+ 0xb913179899f68584, 0x28e2557b59846e3f,
+ 0xe757dd7ec07426e5, 0x331aeada2fe589cf,
+ 0x9096ea6f3848984f, 0x3ff0d2c85def7621,
+ 0xb4bca50b065abe63, 0xfed077a756b53a9,
+ 0xe1ebce4dc7f16dfb, 0xd3e8495912c62894,
+ 0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c,
+ 0xb080392cc4349dec, 0xbd8d794d96aacfb3,
+ 0xdca04777f541c567, 0xecf0d7a0fc5583a0,
+ 0x89e42caaf9491b60, 0xf41686c49db57244,
+ 0xac5d37d5b79b6239, 0x311c2875c522ced5,
+ 0xd77485cb25823ac7, 0x7d633293366b828b,
+ 0x86a8d39ef77164bc, 0xae5dff9c02033197,
+ 0xa8530886b54dbdeb, 0xd9f57f830283fdfc,
+ 0xd267caa862a12d66, 0xd072df63c324fd7b,
+ 0x8380dea93da4bc60, 0x4247cb9e59f71e6d,
+ 0xa46116538d0deb78, 0x52d9be85f074e608,
+ 0xcd795be870516656, 0x67902e276c921f8b,
+ 0x806bd9714632dff6, 0xba1cd8a3db53b6,
+ 0xa086cfcd97bf97f3, 0x80e8a40eccd228a4,
+ 0xc8a883c0fdaf7df0, 0x6122cd128006b2cd,
+ 0xfad2a4b13d1b5d6c, 0x796b805720085f81,
+ 0x9cc3a6eec6311a63, 0xcbe3303674053bb0,
+ 0xc3f490aa77bd60fc, 0xbedbfc4411068a9c,
+ 0xf4f1b4d515acb93b, 0xee92fb5515482d44,
+ 0x991711052d8bf3c5, 0x751bdd152d4d1c4a,
+ 0xbf5cd54678eef0b6, 0xd262d45a78a0635d,
+ 0xef340a98172aace4, 0x86fb897116c87c34,
+ 0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0,
+ 0xbae0a846d2195712, 0x8974836059cca109,
+ 0xe998d258869facd7, 0x2bd1a438703fc94b,
+ 0x91ff83775423cc06, 0x7b6306a34627ddcf,
+ 0xb67f6455292cbf08, 0x1a3bc84c17b1d542,
+ 0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93,
+ 0x8e938662882af53e, 0x547eb47b7282ee9c,
+ 0xb23867fb2a35b28d, 0xe99e619a4f23aa43,
+ 0xdec681f9f4c31f31, 0x6405fa00e2ec94d4,
+ 0x8b3c113c38f9f37e, 0xde83bc408dd3dd04,
+ 0xae0b158b4738705e, 0x9624ab50b148d445,
+ 0xd98ddaee19068c76, 0x3badd624dd9b0957,
+ 0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6,
+ 0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c,
+ 0xd47487cc8470652b, 0x7647c3200069671f,
+ 0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073,
+ 0xa5fb0a17c777cf09, 0xf468107100525890,
+ 0xcf79cc9db955c2cc, 0x7182148d4066eeb4,
+ 0x81ac1fe293d599bf, 0xc6f14cd848405530,
+ 0xa21727db38cb002f, 0xb8ada00e5a506a7c,
+ 0xca9cf1d206fdc03b, 0xa6d90811f0e4851c,
+ 0xfd442e4688bd304a, 0x908f4a166d1da663,
+ 0x9e4a9cec15763e2e, 0x9a598e4e043287fe,
+ 0xc5dd44271ad3cdba, 0x40eff1e1853f29fd,
+ 0xf7549530e188c128, 0xd12bee59e68ef47c,
+ 0x9a94dd3e8cf578b9, 0x82bb74f8301958ce,
+ 0xc13a148e3032d6e7, 0xe36a52363c1faf01,
+ 0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1,
+ 0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9,
+ 0xbcb2b812db11a5de, 0x7415d448f6b6f0e7,
+ 0xebdf661791d60f56, 0x111b495b3464ad21,
+ 0x936b9fcebb25c995, 0xcab10dd900beec34,
+ 0xb84687c269ef3bfb, 0x3d5d514f40eea742,
+ 0xe65829b3046b0afa, 0xcb4a5a3112a5112,
+ 0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab,
+ 0xb3f4e093db73a093, 0x59ed216765690f56,
+ 0xe0f218b8d25088b8, 0x306869c13ec3532c,
+ 0x8c974f7383725573, 0x1e414218c73a13fb,
+ 0xafbd2350644eeacf, 0xe5d1929ef90898fa,
+ 0xdbac6c247d62a583, 0xdf45f746b74abf39,
+ 0x894bc396ce5da772, 0x6b8bba8c328eb783,
+ 0xab9eb47c81f5114f, 0x66ea92f3f326564,
+ 0xd686619ba27255a2, 0xc80a537b0efefebd,
+ 0x8613fd0145877585, 0xbd06742ce95f5f36,
+ 0xa798fc4196e952e7, 0x2c48113823b73704,
+ 0xd17f3b51fca3a7a0, 0xf75a15862ca504c5,
+ 0x82ef85133de648c4, 0x9a984d73dbe722fb,
+ 0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba,
+ 0xcc963fee10b7d1b3, 0x318df905079926a8,
+ 0xffbbcfe994e5c61f, 0xfdf17746497f7052,
+ 0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633,
+ 0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0,
+ 0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0,
+ 0x9c1661a651213e2d, 0x6bea10ca65c084e,
+ 0xc31bfa0fe5698db8, 0x486e494fcff30a62,
+ 0xf3e2f893dec3f126, 0x5a89dba3c3efccfa,
+ 0x986ddb5c6b3a76b7, 0xf89629465a75e01c,
+ 0xbe89523386091465, 0xf6bbb397f1135823,
+ 0xee2ba6c0678b597f, 0x746aa07ded582e2c,
+ 0x94db483840b717ef, 0xa8c2a44eb4571cdc,
+ 0xba121a4650e4ddeb, 0x92f34d62616ce413,
+ 0xe896a0d7e51e1566, 0x77b020baf9c81d17,
+ 0x915e2486ef32cd60, 0xace1474dc1d122e,
+ 0xb5b5ada8aaff80b8, 0xd819992132456ba,
+ 0xe3231912d5bf60e6, 0x10e1fff697ed6c69,
+ 0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1,
+ 0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2,
+ 0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde,
+ 0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b,
+ 0xad4ab7112eb3929d, 0x86c16c98d2c953c6,
+ 0xd89d64d57a607744, 0xe871c7bf077ba8b7,
+ 0x87625f056c7c4a8b, 0x11471cd764ad4972,
+ 0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf,
+ 0xd389b47879823479, 0x4aff1d108d4ec2c3,
+ 0x843610cb4bf160cb, 0xcedf722a585139ba,
+ 0xa54394fe1eedb8fe, 0xc2974eb4ee658828,
+ 0xce947a3da6a9273e, 0x733d226229feea32,
+ 0x811ccc668829b887, 0x806357d5a3f525f,
+ 0xa163ff802a3426a8, 0xca07c2dcb0cf26f7,
+ 0xc9bcff6034c13052, 0xfc89b393dd02f0b5,
+ 0xfc2c3f3841f17c67, 0xbbac2078d443ace2,
+ 0x9d9ba7832936edc0, 0xd54b944b84aa4c0d,
+ 0xc5029163f384a931, 0xa9e795e65d4df11,
+ 0xf64335bcf065d37d, 0x4d4617b5ff4a16d5,
+ 0x99ea0196163fa42e, 0x504bced1bf8e4e45,
+ 0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6,
+ 0xf07da27a82c37088, 0x5d767327bb4e5a4c,
+ 0x964e858c91ba2655, 0x3a6a07f8d510f86f,
+ 0xbbe226efb628afea, 0x890489f70a55368b,
+ 0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e,
+ 0x92c8ae6b464fc96f, 0x3b0b8bc90012929d,
+ 0xb77ada0617e3bbcb, 0x9ce6ebb40173744,
+ 0xe55990879ddcaabd, 0xcc420a6a101d0515,
+ 0x8f57fa54c2a9eab6, 0x9fa946824a12232d,
+ 0xb32df8e9f3546564, 0x47939822dc96abf9,
+ 0xdff9772470297ebd, 0x59787e2b93bc56f7,
+ 0x8bfbea76c619ef36, 0x57eb4edb3c55b65a,
+ 0xaefae51477a06b03, 0xede622920b6b23f1,
+ 0xdab99e59958885c4, 0xe95fab368e45eced,
+ 0x88b402f7fd75539b, 0x11dbcb0218ebb414,
+ 0xaae103b5fcd2a881, 0xd652bdc29f26a119,
+ 0xd59944a37c0752a2, 0x4be76d3346f0495f,
+ 0x857fcae62d8493a5, 0x6f70a4400c562ddb,
+ 0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952,
+ 0xd097ad07a71f26b2, 0x7e2000a41346a7a7,
+ 0x825ecc24c873782f, 0x8ed400668c0c28c8,
+ 0xa2f67f2dfa90563b, 0x728900802f0f32fa,
+ 0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9,
+ 0xfea126b7d78186bc, 0xe2f610c84987bfa8,
+ 0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9,
+ 0xc6ede63fa05d3143, 0x91503d1c79720dbb,
+ 0xf8a95fcf88747d94, 0x75a44c6397ce912a,
+ 0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba,
+ 0xc24452da229b021b, 0xfbe85badce996168,
+ 0xf2d56790ab41c2a2, 0xfae27299423fb9c3,
+ 0x97c560ba6b0919a5, 0xdccd879fc967d41a,
+ 0xbdb6b8e905cb600f, 0x5400e987bbc1c920,
+ 0xed246723473e3813, 0x290123e9aab23b68,
+ 0x9436c0760c86e30b, 0xf9a0b6720aaf6521,
+ 0xb94470938fa89bce, 0xf808e40e8d5b3e69,
+ 0xe7958cb87392c2c2, 0xb60b1d1230b20e04,
+ 0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2,
+ 0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3,
+ 0xe2280b6c20dd5232, 0x25c6da63c38de1b0,
+ 0x8d590723948a535f, 0x579c487e5a38ad0e,
+ 0xb0af48ec79ace837, 0x2d835a9df0c6d851,
+ 0xdcdb1b2798182244, 0xf8e431456cf88e65,
+ 0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff,
+ 0xac8b2d36eed2dac5, 0xe272467e3d222f3f,
+ 0xd7adf884aa879177, 0x5b0ed81dcc6abb0f,
+ 0x86ccbb52ea94baea, 0x98e947129fc2b4e9,
+ 0xa87fea27a539e9a5, 0x3f2398d747b36224,
+ 0xd29fe4b18e88640e, 0x8eec7f0d19a03aad,
+ 0x83a3eeeef9153e89, 0x1953cf68300424ac,
+ 0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7,
+ 0xcdb02555653131b6, 0x3792f412cb06794d,
+ 0x808e17555f3ebf11, 0xe2bbd88bbee40bd0,
+ 0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4,
+ 0xc8de047564d20a8b, 0xf245825a5a445275,
+ 0xfb158592be068d2e, 0xeed6e2f0f0d56712,
+ 0x9ced737bb6c4183d, 0x55464dd69685606b,
+ 0xc428d05aa4751e4c, 0xaa97e14c3c26b886,
+ 0xf53304714d9265df, 0xd53dd99f4b3066a8,
+ 0x993fe2c6d07b7fab, 0xe546a8038efe4029,
+ 0xbf8fdb78849a5f96, 0xde98520472bdd033,
+ 0xef73d256a5c0f77c, 0x963e66858f6d4440,
+ 0x95a8637627989aad, 0xdde7001379a44aa8,
+ 0xbb127c53b17ec159, 0x5560c018580d5d52,
+ 0xe9d71b689dde71af, 0xaab8f01e6e10b4a6,
+ 0x9226712162ab070d, 0xcab3961304ca70e8,
+ 0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22,
+ 0xe45c10c42a2b3b05, 0x8cb89a7db77c506a,
+ 0x8eb98a7a9a5b04e3, 0x77f3608e92adb242,
+ 0xb267ed1940f1c61c, 0x55f038b237591ed3,
+ 0xdf01e85f912e37a3, 0x6b6c46dec52f6688,
+ 0x8b61313bbabce2c6, 0x2323ac4b3b3da015,
+ 0xae397d8aa96c1b77, 0xabec975e0a0d081a,
+ 0xd9c7dced53c72255, 0x96e7bd358c904a21,
+ 0x881cea14545c7575, 0x7e50d64177da2e54,
+ 0xaa242499697392d2, 0xdde50bd1d5d0b9e9,
+ 0xd4ad2dbfc3d07787, 0x955e4ec64b44e864,
+ 0x84ec3c97da624ab4, 0xbd5af13bef0b113e,
+ 0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e,
+ 0xcfb11ead453994ba, 0x67de18eda5814af2,
+ 0x81ceb32c4b43fcf4, 0x80eacf948770ced7,
+ 0xa2425ff75e14fc31, 0xa1258379a94d028d,
+ 0xcad2f7f5359a3b3e, 0x96ee45813a04330,
+ 0xfd87b5f28300ca0d, 0x8bca9d6e188853fc,
+ 0x9e74d1b791e07e48, 0x775ea264cf55347e,
+ 0xc612062576589dda, 0x95364afe032a819e,
+ 0xf79687aed3eec551, 0x3a83ddbd83f52205,
+ 0x9abe14cd44753b52, 0xc4926a9672793543,
+ 0xc16d9a0095928a27, 0x75b7053c0f178294,
+ 0xf1c90080baf72cb1, 0x5324c68b12dd6339,
+ 0x971da05074da7bee, 0xd3f6fc16ebca5e04,
+ 0xbce5086492111aea, 0x88f4bb1ca6bcf585,
+ 0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6,
+ 0x9392ee8e921d5d07, 0x3aff322e62439fd0,
+ 0xb877aa3236a4b449, 0x9befeb9fad487c3,
+ 0xe69594bec44de15b, 0x4c2ebe687989a9b4,
+ 0x901d7cf73ab0acd9, 0xf9d37014bf60a11,
+ 0xb424dc35095cd80f, 0x538484c19ef38c95,
+ 0xe12e13424bb40e13, 0x2865a5f206b06fba,
+ 0x8cbccc096f5088cb, 0xf93f87b7442e45d4,
+ 0xafebff0bcb24aafe, 0xf78f69a51539d749,
+ 0xdbe6fecebdedd5be, 0xb573440e5a884d1c,
+ 0x89705f4136b4a597, 0x31680a88f8953031,
+ 0xabcc77118461cefc, 0xfdc20d2b36ba7c3e,
+ 0xd6bf94d5e57a42bc, 0x3d32907604691b4d,
+ 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110,
+ 0xa7c5ac471b478423, 0xfcf80dc33721d54,
+ 0xd1b71758e219652b, 0xd3c36113404ea4a9,
+ 0x83126e978d4fdf3b, 0x645a1cac083126ea,
+ 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4,
+ 0xcccccccccccccccc, 0xcccccccccccccccd,
+ 0x8000000000000000, 0x0,
+ 0xa000000000000000, 0x0,
+ 0xc800000000000000, 0x0,
+ 0xfa00000000000000, 0x0,
+ 0x9c40000000000000, 0x0,
+ 0xc350000000000000, 0x0,
+ 0xf424000000000000, 0x0,
+ 0x9896800000000000, 0x0,
+ 0xbebc200000000000, 0x0,
+ 0xee6b280000000000, 0x0,
+ 0x9502f90000000000, 0x0,
+ 0xba43b74000000000, 0x0,
+ 0xe8d4a51000000000, 0x0,
+ 0x9184e72a00000000, 0x0,
+ 0xb5e620f480000000, 0x0,
+ 0xe35fa931a0000000, 0x0,
+ 0x8e1bc9bf04000000, 0x0,
+ 0xb1a2bc2ec5000000, 0x0,
+ 0xde0b6b3a76400000, 0x0,
+ 0x8ac7230489e80000, 0x0,
+ 0xad78ebc5ac620000, 0x0,
+ 0xd8d726b7177a8000, 0x0,
+ 0x878678326eac9000, 0x0,
+ 0xa968163f0a57b400, 0x0,
+ 0xd3c21bcecceda100, 0x0,
+ 0x84595161401484a0, 0x0,
+ 0xa56fa5b99019a5c8, 0x0,
+ 0xcecb8f27f4200f3a, 0x0,
+ 0x813f3978f8940984, 0x4000000000000000,
+ 0xa18f07d736b90be5, 0x5000000000000000,
+ 0xc9f2c9cd04674ede, 0xa400000000000000,
+ 0xfc6f7c4045812296, 0x4d00000000000000,
+ 0x9dc5ada82b70b59d, 0xf020000000000000,
+ 0xc5371912364ce305, 0x6c28000000000000,
+ 0xf684df56c3e01bc6, 0xc732000000000000,
+ 0x9a130b963a6c115c, 0x3c7f400000000000,
+ 0xc097ce7bc90715b3, 0x4b9f100000000000,
+ 0xf0bdc21abb48db20, 0x1e86d40000000000,
+ 0x96769950b50d88f4, 0x1314448000000000,
+ 0xbc143fa4e250eb31, 0x17d955a000000000,
+ 0xeb194f8e1ae525fd, 0x5dcfab0800000000,
+ 0x92efd1b8d0cf37be, 0x5aa1cae500000000,
+ 0xb7abc627050305ad, 0xf14a3d9e40000000,
+ 0xe596b7b0c643c719, 0x6d9ccd05d0000000,
+ 0x8f7e32ce7bea5c6f, 0xe4820023a2000000,
+ 0xb35dbf821ae4f38b, 0xdda2802c8a800000,
+ 0xe0352f62a19e306e, 0xd50b2037ad200000,
+ 0x8c213d9da502de45, 0x4526f422cc340000,
+ 0xaf298d050e4395d6, 0x9670b12b7f410000,
+ 0xdaf3f04651d47b4c, 0x3c0cdd765f114000,
+ 0x88d8762bf324cd0f, 0xa5880a69fb6ac800,
+ 0xab0e93b6efee0053, 0x8eea0d047a457a00,
+ 0xd5d238a4abe98068, 0x72a4904598d6d880,
+ 0x85a36366eb71f041, 0x47a6da2b7f864750,
+ 0xa70c3c40a64e6c51, 0x999090b65f67d924,
+ 0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d,
+ 0x82818f1281ed449f, 0xbff8f10e7a8921a4,
+ 0xa321f2d7226895c7, 0xaff72d52192b6a0d,
+ 0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490,
+ 0xfee50b7025c36a08, 0x2f236d04753d5b4,
+ 0x9f4f2726179a2245, 0x1d762422c946590,
+ 0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5,
+ 0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2,
+ 0x9b934c3b330c8577, 0x63cc55f49f88eb2f,
+ 0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb,
+ 0xf316271c7fc3908a, 0x8bef464e3945ef7a,
+ 0x97edd871cfda3a56, 0x97758bf0e3cbb5ac,
+ 0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317,
+ 0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd,
+ 0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a,
+ 0xb975d6b6ee39e436, 0xb3e2fd538e122b44,
+ 0xe7d34c64a9c85d44, 0x60dbbca87196b616,
+ 0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd,
+ 0xb51d13aea4a488dd, 0x6babab6398bdbe41,
+ 0xe264589a4dcdab14, 0xc696963c7eed2dd1,
+ 0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2,
+ 0xb0de65388cc8ada8, 0x3b25a55f43294bcb,
+ 0xdd15fe86affad912, 0x49ef0eb713f39ebe,
+ 0x8a2dbf142dfcc7ab, 0x6e3569326c784337,
+ 0xacb92ed9397bf996, 0x49c2c37f07965404,
+ 0xd7e77a8f87daf7fb, 0xdc33745ec97be906,
+ 0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3,
+ 0xa8acd7c0222311bc, 0xc40832ea0d68ce0c,
+ 0xd2d80db02aabd62b, 0xf50a3fa490c30190,
+ 0x83c7088e1aab65db, 0x792667c6da79e0fa,
+ 0xa4b8cab1a1563f52, 0x577001b891185938,
+ 0xcde6fd5e09abcf26, 0xed4c0226b55e6f86,
+ 0x80b05e5ac60b6178, 0x544f8158315b05b4,
+ 0xa0dc75f1778e39d6, 0x696361ae3db1c721,
+ 0xc913936dd571c84c, 0x3bc3a19cd1e38e9,
+ 0xfb5878494ace3a5f, 0x4ab48a04065c723,
+ 0x9d174b2dcec0e47b, 0x62eb0d64283f9c76,
+ 0xc45d1df942711d9a, 0x3ba5d0bd324f8394,
+ 0xf5746577930d6500, 0xca8f44ec7ee36479,
+ 0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb,
+ 0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e,
+ 0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e,
+ 0x95d04aee3b80ece5, 0xbba1f1d158724a12,
+ 0xbb445da9ca61281f, 0x2a8a6e45ae8edc97,
+ 0xea1575143cf97226, 0xf52d09d71a3293bd,
+ 0x924d692ca61be758, 0x593c2626705f9c56,
+ 0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c,
+ 0xe498f455c38b997a, 0xb6dfb9c0f956447,
+ 0x8edf98b59a373fec, 0x4724bd4189bd5eac,
+ 0xb2977ee300c50fe7, 0x58edec91ec2cb657,
+ 0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed,
+ 0x8b865b215899f46c, 0xbd79e0d20082ee74,
+ 0xae67f1e9aec07187, 0xecd8590680a3aa11,
+ 0xda01ee641a708de9, 0xe80e6f4820cc9495,
+ 0x884134fe908658b2, 0x3109058d147fdcdd,
+ 0xaa51823e34a7eede, 0xbd4b46f0599fd415,
+ 0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a,
+ 0x850fadc09923329e, 0x3e2cf6bc604ddb0,
+ 0xa6539930bf6bff45, 0x84db8346b786151c,
+ 0xcfe87f7cef46ff16, 0xe612641865679a63,
+ 0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e,
+ 0xa26da3999aef7749, 0xe3be5e330f38f09d,
+ 0xcb090c8001ab551c, 0x5cadf5bfd3072cc5,
+ 0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6,
+ 0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa,
+ 0xc646d63501a1511d, 0xb281e1fd541501b8,
+ 0xf7d88bc24209a565, 0x1f225a7ca91a4226,
+ 0x9ae757596946075f, 0x3375788de9b06958,
+ 0xc1a12d2fc3978937, 0x52d6b1641c83ae,
+ 0xf209787bb47d6b84, 0xc0678c5dbd23a49a,
+ 0x9745eb4d50ce6332, 0xf840b7ba963646e0,
+ 0xbd176620a501fbff, 0xb650e5a93bc3d898,
+ 0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe,
+ 0x93ba47c980e98cdf, 0xc66f336c36b10137,
+ 0xb8a8d9bbe123f017, 0xb80b0047445d4184,
+ 0xe6d3102ad96cec1d, 0xa60dc059157491e5,
+ 0x9043ea1ac7e41392, 0x87c89837ad68db2f,
+ 0xb454e4a179dd1877, 0x29babe4598c311fb,
+ 0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a,
+ 0x8ce2529e2734bb1d, 0x1899e4a65f58660c,
+ 0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f,
+ 0xdc21a1171d42645d, 0x76707543f4fa1f73,
+ 0x899504ae72497eba, 0x6a06494a791c53a8,
+ 0xabfa45da0edbde69, 0x487db9d17636892,
+ 0xd6f8d7509292d603, 0x45a9d2845d3c42b6,
+ 0x865b86925b9bc5c2, 0xb8a2392ba45a9b2,
+ 0xa7f26836f282b732, 0x8e6cac7768d7141e,
+ 0xd1ef0244af2364ff, 0x3207d795430cd926,
+ 0x8335616aed761f1f, 0x7f44e6bd49e807b8,
+ 0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6,
+ 0xcd036837130890a1, 0x36dba887c37a8c0f,
+ 0x802221226be55a64, 0xc2494954da2c9789,
+ 0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c,
+ 0xc83553c5c8965d3d, 0x6f92829494e5acc7,
+ 0xfa42a8b73abbf48c, 0xcb772339ba1f17f9,
+ 0x9c69a97284b578d7, 0xff2a760414536efb,
+ 0xc38413cf25e2d70d, 0xfef5138519684aba,
+ 0xf46518c2ef5b8cd1, 0x7eb258665fc25d69,
+ 0x98bf2f79d5993802, 0xef2f773ffbd97a61,
+ 0xbeeefb584aff8603, 0xaafb550ffacfd8fa,
+ 0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38,
+ 0x952ab45cfa97a0b2, 0xdd945a747bf26183,
+ 0xba756174393d88df, 0x94f971119aeef9e4,
+ 0xe912b9d1478ceb17, 0x7a37cd5601aab85d,
+ 0x91abb422ccb812ee, 0xac62e055c10ab33a,
+ 0xb616a12b7fe617aa, 0x577b986b314d6009,
+ 0xe39c49765fdf9d94, 0xed5a7e85fda0b80b,
+ 0x8e41ade9fbebc27d, 0x14588f13be847307,
+ 0xb1d219647ae6b31c, 0x596eb2d8ae258fc8,
+ 0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb,
+ 0x8aec23d680043bee, 0x25de7bb9480d5854,
+ 0xada72ccc20054ae9, 0xaf561aa79a10ae6a,
+ 0xd910f7ff28069da4, 0x1b2ba1518094da04,
+ 0x87aa9aff79042286, 0x90fb44d2f05d0842,
+ 0xa99541bf57452b28, 0x353a1607ac744a53,
+ 0xd3fa922f2d1675f2, 0x42889b8997915ce8,
+ 0x847c9b5d7c2e09b7, 0x69956135febada11,
+ 0xa59bc234db398c25, 0x43fab9837e699095,
+ 0xcf02b2c21207ef2e, 0x94f967e45e03f4bb,
+ 0x8161afb94b44f57d, 0x1d1be0eebac278f5,
+ 0xa1ba1ba79e1632dc, 0x6462d92a69731732,
+ 0xca28a291859bbf93, 0x7d7b8f7503cfdcfe,
+ 0xfcb2cb35e702af78, 0x5cda735244c3d43e,
+ 0x9defbf01b061adab, 0x3a0888136afa64a7,
+ 0xc56baec21c7a1916, 0x88aaa1845b8fdd0,
+ 0xf6c69a72a3989f5b, 0x8aad549e57273d45,
+ 0x9a3c2087a63f6399, 0x36ac54e2f678864b,
+ 0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd,
+ 0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5,
+ 0x969eb7c47859e743, 0x9f644ae5a4b1b325,
+ 0xbc4665b596706114, 0x873d5d9f0dde1fee,
+ 0xeb57ff22fc0c7959, 0xa90cb506d155a7ea,
+ 0x9316ff75dd87cbd8, 0x9a7f12442d588f2,
+ 0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f,
+ 0xe5d3ef282a242e81, 0x8f1668c8a86da5fa,
+ 0x8fa475791a569d10, 0xf96e017d694487bc,
+ 0xb38d92d760ec4455, 0x37c981dcc395a9ac,
+ 0xe070f78d3927556a, 0x85bbe253f47b1417,
+ 0x8c469ab843b89562, 0x93956d7478ccec8e,
+ 0xaf58416654a6babb, 0x387ac8d1970027b2,
+ 0xdb2e51bfe9d0696a, 0x6997b05fcc0319e,
+ 0x88fcf317f22241e2, 0x441fece3bdf81f03,
+ 0xab3c2fddeeaad25a, 0xd527e81cad7626c3,
+ 0xd60b3bd56a5586f1, 0x8a71e223d8d3b074,
+ 0x85c7056562757456, 0xf6872d5667844e49,
+ 0xa738c6bebb12d16c, 0xb428f8ac016561db,
+ 0xd106f86e69d785c7, 0xe13336d701beba52,
+ 0x82a45b450226b39c, 0xecc0024661173473,
+ 0xa34d721642b06084, 0x27f002d7f95d0190,
+ 0xcc20ce9bd35c78a5, 0x31ec038df7b441f4,
+ 0xff290242c83396ce, 0x7e67047175a15271,
+ 0x9f79a169bd203e41, 0xf0062c6e984d386,
+ 0xc75809c42c684dd1, 0x52c07b78a3e60868,
+ 0xf92e0c3537826145, 0xa7709a56ccdf8a82,
+ 0x9bbcc7a142b17ccb, 0x88a66076400bb691,
+ 0xc2abf989935ddbfe, 0x6acff893d00ea435,
+ 0xf356f7ebf83552fe, 0x583f6b8c4124d43,
+ 0x98165af37b2153de, 0xc3727a337a8b704a,
+ 0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c,
+ 0xeda2ee1c7064130c, 0x1162def06f79df73,
+ 0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8,
+ 0xb9a74a0637ce2ee1, 0x6d953e2bd7173692,
+ 0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437,
+ 0x910ab1d4db9914a0, 0x1d9c9892400a22a2,
+ 0xb54d5e4a127f59c8, 0x2503beb6d00cab4b,
+ 0xe2a0b5dc971f303a, 0x2e44ae64840fd61d,
+ 0x8da471a9de737e24, 0x5ceaecfed289e5d2,
+ 0xb10d8e1456105dad, 0x7425a83e872c5f47,
+ 0xdd50f1996b947518, 0xd12f124e28f77719,
+ 0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f,
+ 0xace73cbfdc0bfb7b, 0x636cc64d1001550b,
+ 0xd8210befd30efa5a, 0x3c47f7e05401aa4e,
+ 0x8714a775e3e95c78, 0x65acfaec34810a71,
+ 0xa8d9d1535ce3b396, 0x7f1839a741a14d0d,
+ 0xd31045a8341ca07c, 0x1ede48111209a050,
+ 0x83ea2b892091e44d, 0x934aed0aab460432,
+ 0xa4e4b66b68b65d60, 0xf81da84d5617853f,
+ 0xce1de40642e3f4b9, 0x36251260ab9d668e,
+ 0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019,
+ 0xa1075a24e4421730, 0xb24cf65b8612f81f,
+ 0xc94930ae1d529cfc, 0xdee033f26797b627,
+ 0xfb9b7cd9a4a7443c, 0x169840ef017da3b1,
+ 0x9d412e0806e88aa5, 0x8e1f289560ee864e,
+ 0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2,
+ 0xf5b5d7ec8acb58a2, 0xae10af696774b1db,
+ 0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29,
+ 0xbff610b0cc6edd3f, 0x17fd090a58d32af3,
+ 0xeff394dcff8a948e, 0xddfc4b4cef07f5b0,
+ 0x95f83d0a1fb69cd9, 0x4abdaf101564f98e,
+ 0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1,
+ 0xea53df5fd18d5513, 0x84c86189216dc5ed,
+ 0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4,
+ 0xb7118682dbb66a77, 0x3fbc8c33221dc2a1,
+ 0xe4d5e82392a40515, 0xfabaf3feaa5334a,
+ 0x8f05b1163ba6832d, 0x29cb4d87f2a7400e,
+ 0xb2c71d5bca9023f8, 0x743e20e9ef511012,
+ 0xdf78e4b2bd342cf6, 0x914da9246b255416,
+ 0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e,
+ 0xae9672aba3d0c320, 0xa184ac2473b529b1,
+ 0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e,
+ 0x8865899617fb1871, 0x7e2fa67c7a658892,
+ 0xaa7eebfb9df9de8d, 0xddbb901b98feeab7,
+ 0xd51ea6fa85785631, 0x552a74227f3ea565,
+ 0x8533285c936b35de, 0xd53a88958f87275f,
+ 0xa67ff273b8460356, 0x8a892abaf368f137,
+ 0xd01fef10a657842c, 0x2d2b7569b0432d85,
+ 0x8213f56a67f6b29b, 0x9c3b29620e29fc73,
+ 0xa298f2c501f45f42, 0x8349f3ba91b47b8f,
+ 0xcb3f2f7642717713, 0x241c70a936219a73,
+ 0xfe0efb53d30dd4d7, 0xed238cd383aa0110,
+ 0x9ec95d1463e8a506, 0xf4363804324a40aa,
+ 0xc67bb4597ce2ce48, 0xb143c6053edcd0d5,
+ 0xf81aa16fdc1b81da, 0xdd94b7868e94050a,
+ 0x9b10a4e5e9913128, 0xca7cf2b4191c8326,
+ 0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0,
+ 0xf24a01a73cf2dccf, 0xbc633b39673c8cec,
+ 0x976e41088617ca01, 0xd5be0503e085d813,
+ 0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18,
+ 0xec9c459d51852ba2, 0xddf8e7d60ed1219e,
+ 0x93e1ab8252f33b45, 0xcabb90e5c942b503,
+ 0xb8da1662e7b00a17, 0x3d6a751f3b936243,
+ 0xe7109bfba19c0c9d, 0xcc512670a783ad4,
+ 0x906a617d450187e2, 0x27fb2b80668b24c5,
+ 0xb484f9dc9641e9da, 0xb1f9f660802dedf6,
+ 0xe1a63853bbd26451, 0x5e7873f8a0396973,
+ 0x8d07e33455637eb2, 0xdb0b487b6423e1e8,
+ 0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62,
+ 0xdc5c5301c56b75f7, 0x7641a140cc7810fb,
+ 0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d,
+ 0xac2820d9623bf429, 0x546345fa9fbdcd44,
+ 0xd732290fbacaf133, 0xa97c177947ad4095,
+ 0x867f59a9d4bed6c0, 0x49ed8eabcccc485d,
+ 0xa81f301449ee8c70, 0x5c68f256bfff5a74,
+ 0xd226fc195c6a2f8c, 0x73832eec6fff3111,
+ 0x83585d8fd9c25db7, 0xc831fd53c5ff7eab,
+ 0xa42e74f3d032f525, 0xba3e7ca8b77f5e55,
+ 0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb,
+ 0x80444b5e7aa7cf85, 0x7980d163cf5b81b3,
+ 0xa0555e361951c366, 0xd7e105bcc332621f,
+ 0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7,
+ 0xfa856334878fc150, 0xb14f98f6f0feb951,
+ 0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3,
+ 0xc3b8358109e84f07, 0xa862f80ec4700c8,
+ 0xf4a642e14c6262c8, 0xcd27bb612758c0fa,
+ 0x98e7e9cccfbd7dbd, 0x8038d51cb897789c,
+ 0xbf21e44003acdd2c, 0xe0470a63e6bd56c3,
+ 0xeeea5d5004981478, 0x1858ccfce06cac74,
+ 0x95527a5202df0ccb, 0xf37801e0c43ebc8,
+ 0xbaa718e68396cffd, 0xd30560258f54e6ba,
+ 0xe950df20247c83fd, 0x47c6b82ef32a2069,
+ 0x91d28b7416cdd27e, 0x4cdc331d57fa5441,
+ 0xb6472e511c81471d, 0xe0133fe4adf8e952,
+ 0xe3d8f9e563a198e5, 0x58180fddd97723a6,
+ 0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648,
+};
+
+/* binary_format<double> constants from fast_float. */
+#define FFP_MANTISSA_BITS 52
+#define FFP_MIN_EXPONENT (-1023)
+#define FFP_INFINITE_POWER 0x7FF
+#define FFP_SMALLEST_POW10 (-342)
+#define FFP_LARGEST_POW10 308
+#define FFP_MIN_RTE (-4) /* min_exponent_round_to_even */
+#define FFP_MAX_RTE 23 /* max_exponent_round_to_even */
+
+/* (((152170 + 65536) * q) >> 16) + 63 == floor(log2(10^q)) + q + 63, see paper. */
+static inline int32_t ffp_power(int32_t q) {
+ return (((152170 + 65536) * q) >> 16) + 63;
+}
+
+static inline void ffp_mul128(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) {
+#if defined(__SIZEOF_INT128__)
+ __uint128_t p = (__uint128_t)a * (__uint128_t)b;
+ *lo = (uint64_t)p;
+ *hi = (uint64_t)(p >> 64);
+#else
+ uint64_t a0 = (uint32_t)a, a1 = a >> 32, b0 = (uint32_t)b, b1 = b >> 32;
+ uint64_t p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
+ uint64_t mid = p10 + (p00 >> 32) + (uint32_t)p01;
+ *hi = p11 + (mid >> 32) + (p01 >> 32);
+ *lo = (mid << 32) | (uint32_t)p00;
+#endif
+}
+
+static inline double ffp_bits2double(uint64_t bits) {
+ double d;
+ memcpy(&d, &bits, sizeof(d));
+ return d;
+}
+
+#if defined(__x86_64__) && defined(HAVE_X86INTRIN_H)
+# include <x86intrin.h>
+#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H)
+# include <intrin.h>
+#endif
+
+#if defined(_MSC_VER) && defined(__AVX2__)
+# pragma intrinsic(__lzcnt64)
+#endif
+
+static inline unsigned int
+ffc_nlz_int64(uint64_t x)
+{
+#if defined(HAVE_BUILTIN___BUILTIN_CLZLL) && !(defined(__sun) && defined(__sparc))
+ return (unsigned int)__builtin_clzll((unsigned long long)x);
+
+#elif defined(_MSC_VER) && defined(__AVX2__) && defined(HAVE___LZCNT64)
+ return (unsigned int)__lzcnt64(x);
+
+#elif defined(__x86_64__) && defined(__LZCNT__) && defined(HAVE__LZCNT_U64)
+ return (unsigned int)_lzcnt_u64(x);
+
+#elif defined(_WIN64) && defined(_MSC_VER) && defined(HAVE__BITSCANREVERSE64)
+ unsigned long r;
+ return _BitScanReverse64(&r, x) ? (63u - (unsigned int)r) : 64;
+
+#else
+ uint64_t y;
+ unsigned int n = 64;
+ y = x >> 32; if (y) {n -= 32; x = y;}
+ y = x >> 16; if (y) {n -= 16; x = y;}
+ y = x >> 8; if (y) {n -= 8; x = y;}
+ y = x >> 4; if (y) {n -= 4; x = y;}
+ y = x >> 2; if (y) {n -= 2; x = y;}
+ y = x >> 1; if (y) {return n - 2;}
+ return (unsigned int)(n - x);
+
+#endif
+}
+
+/* q = power of ten, w = mantissa (exact, fits in uint64). neg = sign. */
+static inline double ffp_s2d(int64_t q, uint64_t w, bool neg) {
+ if (w == 0) {
+ return neg ? -0.0 : 0.0;
+ }
+
+ const uint64_t sign = (uint64_t)(neg != 0) << 63;
+ uint64_t mantissa, prod_hi, prod_lo, sp_hi, sp_lo;
+ int32_t power2;
+ int upperbit, shift, index;
+
+ if (q < FFP_SMALLEST_POW10) return ffp_bits2double(sign); /* underflow -> 0 */
+ if (q > FFP_LARGEST_POW10) {
+ return ffp_bits2double(sign | ((uint64_t)FFP_INFINITE_POWER << FFP_MANTISSA_BITS));
+ }
+
+ unsigned int lz = ffc_nlz_int64(w);
+ w <<= lz;
+
+ /* compute_product_approximation<mantissa_bits + 3 = 55>: precision_mask = 0x1FF. */
+ index = 2 * (int)(q - FFP_SMALLEST_POWER_OF_FIVE);
+ ffp_mul128(w, ffp_power_of_five_128[index], &prod_hi, &prod_lo);
+ if ((prod_hi & 0x1FF) == 0x1FF) {
+ ffp_mul128(w, ffp_power_of_five_128[index + 1], &sp_hi, &sp_lo);
+ prod_lo += sp_hi;
+ if (sp_hi > prod_lo) {
+ prod_hi++;
+ }
+ }
+
+ upperbit = (int)(prod_hi >> 63);
+ shift = upperbit + 64 - FFP_MANTISSA_BITS - 3; /* upperbit + 9 */
+ mantissa = prod_hi >> shift;
+ power2 = (int32_t)(ffp_power((int32_t)q) + upperbit - lz - FFP_MIN_EXPONENT);
+
+ if (power2 <= 0) { /* subnormal */
+ if (-power2 + 1 >= 64) return ffp_bits2double(sign); /* far below min -> 0 */
+ mantissa >>= (-power2 + 1);
+ mantissa += (mantissa & 1);
+ mantissa >>= 1;
+ power2 = (mantissa < ((uint64_t)1 << FFP_MANTISSA_BITS)) ? 0 : 1;
+ return ffp_bits2double(sign | ((uint64_t)power2 << FFP_MANTISSA_BITS) | mantissa);
+ }
+
+ /* round-to-even: if we land exactly between two doubles, round down. */
+ if ((prod_lo <= 1) && (q >= FFP_MIN_RTE) && (q <= FFP_MAX_RTE) &&
+ ((mantissa & 3) == 1) && ((mantissa << shift) == prod_hi)) {
+ mantissa &= ~(uint64_t)1;
+ }
+
+ mantissa += (mantissa & 1);
+ mantissa >>= 1;
+ if (mantissa >= ((uint64_t)2 << FFP_MANTISSA_BITS)) {
+ mantissa = (uint64_t)1 << FFP_MANTISSA_BITS;
+ power2++;
+ }
+
+ mantissa &= ~((uint64_t)1 << FFP_MANTISSA_BITS); /* drop implicit bit */
+ if (power2 >= FFP_INFINITE_POWER) {
+ return ffp_bits2double(sign | ((uint64_t)FFP_INFINITE_POWER << FFP_MANTISSA_BITS));
+ }
+ return ffp_bits2double(sign | ((uint64_t)power2 << FFP_MANTISSA_BITS) | mantissa);
+}
+
+#endif // FFP_H
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fpconv.c b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fpconv.c
new file mode 100644
index 0000000..6c9bc2c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/fpconv.c
@@ -0,0 +1,480 @@
+// Boost Software License - Version 1.0 - August 17th, 2003
+//
+// Permission is hereby granted, free of charge, to any person or organization
+// obtaining a copy of the software and accompanying documentation covered by
+// this license (the "Software") to use, reproduce, display, distribute,
+// execute, and transmit the Software, and to prepare derivative works of the
+// Software, and to permit third-parties to whom the Software is furnished to
+// do so, all subject to the following:
+//
+// The copyright notices in the Software and this entire statement, including
+// the above license grant, this restriction and the following disclaimer,
+// must be included in all copies of the Software, in whole or in part, and
+// all derivative works of the Software, unless such copies or derivative
+// works are solely in the form of machine-executable object code generated by
+// a source language processor.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+// The contents of this file is extracted from https://github.com/night-shift/fpconv
+// It was slightly modified to append ".0" to plain floats, for use with the https://github.com/ruby/json package.
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+
+#if JSON_DEBUG
+#include <assert.h>
+#endif
+
+#define npowers 87
+#define steppowers 8
+#define firstpower -348 /* 10 ^ -348 */
+
+#define expmax -32
+#define expmin -60
+
+typedef struct Fp {
+ uint64_t frac;
+ int exp;
+} Fp;
+
+static const Fp powers_ten[] = {
+ { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 },
+ { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 },
+ { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 },
+ { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 },
+ { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 },
+ { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 },
+ { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 },
+ { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 },
+ { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 },
+ { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 },
+ { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 },
+ { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 },
+ { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 },
+ { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 },
+ { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 },
+ { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 },
+ { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 },
+ { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 },
+ { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 },
+ { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 },
+ { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 },
+ { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 },
+ { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 },
+ { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 },
+ { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 },
+ { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 },
+ { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 },
+ { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 },
+ { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 },
+ { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 },
+ { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 },
+ { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 },
+ { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 },
+ { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 },
+ { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 },
+ { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 },
+ { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 },
+ { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 },
+ { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 },
+ { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 },
+ { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 },
+ { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 },
+ { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 },
+ { 12648080533535911531U, 1066 }
+};
+
+static Fp find_cachedpow10(int exp, int* k)
+{
+ const double one_log_ten = 0.30102999566398114;
+
+ int approx = (int)(-(exp + npowers) * one_log_ten);
+ int idx = (approx - firstpower) / steppowers;
+
+ while(1) {
+ int current = exp + powers_ten[idx].exp + 64;
+
+ if(current < expmin) {
+ idx++;
+ continue;
+ }
+
+ if(current > expmax) {
+ idx--;
+ continue;
+ }
+
+ *k = (firstpower + idx * steppowers);
+
+ return powers_ten[idx];
+ }
+}
+
+#define fracmask 0x000FFFFFFFFFFFFFU
+#define expmask 0x7FF0000000000000U
+#define hiddenbit 0x0010000000000000U
+#define signmask 0x8000000000000000U
+#define expbias (1023 + 52)
+
+#define absv(n) ((n) < 0 ? -(n) : (n))
+#define minv(a, b) ((a) < (b) ? (a) : (b))
+
+static const uint64_t tens[] = {
+ 10000000000000000000U, 1000000000000000000U, 100000000000000000U,
+ 10000000000000000U, 1000000000000000U, 100000000000000U,
+ 10000000000000U, 1000000000000U, 100000000000U,
+ 10000000000U, 1000000000U, 100000000U,
+ 10000000U, 1000000U, 100000U,
+ 10000U, 1000U, 100U,
+ 10U, 1U
+};
+
+static inline uint64_t get_dbits(double d)
+{
+ union {
+ double dbl;
+ uint64_t i;
+ } dbl_bits = { d };
+
+ return dbl_bits.i;
+}
+
+static Fp build_fp(double d)
+{
+ uint64_t bits = get_dbits(d);
+
+ Fp fp;
+ fp.frac = bits & fracmask;
+ fp.exp = (bits & expmask) >> 52;
+
+ if(fp.exp) {
+ fp.frac += hiddenbit;
+ fp.exp -= expbias;
+
+ } else {
+ fp.exp = -expbias + 1;
+ }
+
+ return fp;
+}
+
+static void normalize(Fp* fp)
+{
+ while ((fp->frac & hiddenbit) == 0) {
+ fp->frac <<= 1;
+ fp->exp--;
+ }
+
+ int shift = 64 - 52 - 1;
+ fp->frac <<= shift;
+ fp->exp -= shift;
+}
+
+static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper)
+{
+ upper->frac = (fp->frac << 1) + 1;
+ upper->exp = fp->exp - 1;
+
+ while ((upper->frac & (hiddenbit << 1)) == 0) {
+ upper->frac <<= 1;
+ upper->exp--;
+ }
+
+ int u_shift = 64 - 52 - 2;
+
+ upper->frac <<= u_shift;
+ upper->exp = upper->exp - u_shift;
+
+
+ int l_shift = fp->frac == hiddenbit ? 2 : 1;
+
+ lower->frac = (fp->frac << l_shift) - 1;
+ lower->exp = fp->exp - l_shift;
+
+
+ lower->frac <<= lower->exp - upper->exp;
+ lower->exp = upper->exp;
+}
+
+static Fp multiply(Fp* a, Fp* b)
+{
+ const uint64_t lomask = 0x00000000FFFFFFFF;
+
+ uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask);
+ uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32);
+ uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask);
+ uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32);
+
+ uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32);
+ /* round up */
+ tmp += 1U << 31;
+
+ Fp fp = {
+ ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32),
+ a->exp + b->exp + 64
+ };
+
+ return fp;
+}
+
+static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac)
+{
+ while (rem < frac && delta - rem >= kappa &&
+ (rem + kappa < frac || frac - rem > rem + kappa - frac)) {
+
+ digits[ndigits - 1]--;
+ rem += kappa;
+ }
+}
+
+static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K)
+{
+ uint64_t wfrac = upper->frac - fp->frac;
+ uint64_t delta = upper->frac - lower->frac;
+
+ Fp one;
+ one.frac = 1ULL << -upper->exp;
+ one.exp = upper->exp;
+
+ uint64_t part1 = upper->frac >> -one.exp;
+ uint64_t part2 = upper->frac & (one.frac - 1);
+
+ int idx = 0, kappa = 10;
+ const uint64_t* divp;
+ /* 1000000000 */
+ for(divp = tens + 10; kappa > 0; divp++) {
+
+ uint64_t div = *divp;
+ unsigned digit = (unsigned) (part1 / div);
+
+ if (digit || idx) {
+ digits[idx++] = digit + '0';
+ }
+
+ part1 -= digit * div;
+ kappa--;
+
+ uint64_t tmp = (part1 <<-one.exp) + part2;
+ if (tmp <= delta) {
+ *K += kappa;
+ round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac);
+
+ return idx;
+ }
+ }
+
+ /* 10 */
+ const uint64_t* unit = tens + 18;
+
+ while(true) {
+ part2 *= 10;
+ delta *= 10;
+ kappa--;
+
+ unsigned digit = (unsigned) (part2 >> -one.exp);
+ if (digit || idx) {
+ digits[idx++] = digit + '0';
+ }
+
+ part2 &= one.frac - 1;
+ if (part2 < delta) {
+ *K += kappa;
+ round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit);
+
+ return idx;
+ }
+
+ unit--;
+ }
+}
+
+static int grisu2(double d, char* digits, int* K)
+{
+ Fp w = build_fp(d);
+
+ Fp lower, upper;
+ get_normalized_boundaries(&w, &lower, &upper);
+
+ normalize(&w);
+
+ int k;
+ Fp cp = find_cachedpow10(upper.exp, &k);
+
+ w = multiply(&w, &cp);
+ upper = multiply(&upper, &cp);
+ lower = multiply(&lower, &cp);
+
+ lower.frac++;
+ upper.frac--;
+
+ *K = -k;
+
+ return generate_digits(&w, &upper, &lower, digits, K);
+}
+
+static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg)
+{
+ int exp = absv(K + ndigits - 1);
+
+ if(K >= 0 && exp < 15) {
+ memcpy(dest, digits, ndigits);
+ memset(dest + ndigits, '0', K);
+
+ /* add a .0 to mark this as a float. */
+ dest[ndigits + K] = '.';
+ dest[ndigits + K + 1] = '0';
+
+ return ndigits + K + 2;
+ }
+
+ /* write decimal w/o scientific notation */
+ if(K < 0 && (K > -7 || exp < 10)) {
+ int offset = ndigits - absv(K);
+ /* fp < 1.0 -> write leading zero */
+ if(offset <= 0) {
+ offset = -offset;
+ dest[0] = '0';
+ dest[1] = '.';
+ memset(dest + 2, '0', offset);
+ memcpy(dest + offset + 2, digits, ndigits);
+
+ return ndigits + 2 + offset;
+
+ /* fp > 1.0 */
+ } else {
+ memcpy(dest, digits, offset);
+ dest[offset] = '.';
+ memcpy(dest + offset + 1, digits + offset, ndigits - offset);
+
+ return ndigits + 1;
+ }
+ }
+
+ /* write decimal w/ scientific notation */
+ ndigits = minv(ndigits, 18 - neg);
+
+ int idx = 0;
+ dest[idx++] = digits[0];
+
+ if(ndigits > 1) {
+ dest[idx++] = '.';
+ memcpy(dest + idx, digits + 1, ndigits - 1);
+ idx += ndigits - 1;
+ }
+
+ dest[idx++] = 'e';
+
+ char sign = K + ndigits - 1 < 0 ? '-' : '+';
+ dest[idx++] = sign;
+
+ int cent = 0;
+
+ if(exp > 99) {
+ cent = exp / 100;
+ dest[idx++] = cent + '0';
+ exp -= cent * 100;
+ }
+ if(exp > 9) {
+ int dec = exp / 10;
+ dest[idx++] = dec + '0';
+ exp -= dec * 10;
+
+ } else if(cent) {
+ dest[idx++] = '0';
+ }
+
+ dest[idx++] = exp % 10 + '0';
+
+ return idx;
+}
+
+static int filter_special(double fp, char* dest)
+{
+ if(fp == 0.0) {
+ dest[0] = '0';
+ dest[1] = '.';
+ dest[2] = '0';
+ return 3;
+ }
+
+ uint64_t bits = get_dbits(fp);
+
+ bool nan = (bits & expmask) == expmask;
+
+ if(!nan) {
+ return 0;
+ }
+
+ if(bits & fracmask) {
+ dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n';
+
+ } else {
+ dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f';
+ }
+
+ return 3;
+}
+
+/* Fast and accurate double to string conversion based on Florian Loitsch's
+ * Grisu-algorithm[1].
+ *
+ * Input:
+ * fp -> the double to convert, dest -> destination buffer.
+ * The generated string will never be longer than 32 characters.
+ * Make sure to pass a pointer to at least 32 bytes of memory.
+ * The emitted string will not be null terminated.
+ *
+ *
+ *
+ * Output:
+ * The number of written characters.
+ *
+ * Exemplary usage:
+ *
+ * void print(double d)
+ * {
+ * char buf[28 + 1] // plus null terminator
+ * int str_len = fpconv_dtoa(d, buf);
+ *
+ * buf[str_len] = '\0';
+ * printf("%s", buf);
+ * }
+ *
+ */
+static int fpconv_dtoa(double d, char dest[32])
+{
+ char digits[18];
+
+ int str_len = 0;
+ bool neg = false;
+
+ if(get_dbits(d) & signmask) {
+ dest[0] = '-';
+ str_len++;
+ neg = true;
+ }
+
+ int spec = filter_special(d, dest + str_len);
+
+ if(spec) {
+ return str_len + spec;
+ }
+
+ int K = 0;
+ int ndigits = grisu2(d, digits, &K);
+
+ str_len += emit_digits(digits, ndigits, dest + str_len, K, neg);
+#if JSON_DEBUG
+ assert(str_len <= 32);
+#endif
+
+ return str_len;
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/jeaiii-ltoa.h b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/jeaiii-ltoa.h
new file mode 100644
index 0000000..ba4f497
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/ext/json/ext/vendor/jeaiii-ltoa.h
@@ -0,0 +1,267 @@
+/*
+
+This file is released under the terms of the MIT License. It is based on the
+work of James Edward Anhalt III, with the original license listed below.
+
+MIT License
+
+Copyright (c) 2024,2025 Enrico Thierbach - https://github.com/radiospiel
+Copyright (c) 2022 James Edward Anhalt III - https://github.com/jeaiii/itoa
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#ifndef JEAIII_TO_TEXT_H_
+#define JEAIII_TO_TEXT_H_
+
+#include <stdint.h>
+
+typedef uint_fast32_t u32_t;
+typedef uint_fast64_t u64_t;
+
+#define u32(x) ((u32_t)(x))
+#define u64(x) ((u64_t)(x))
+
+struct digit_pair
+{
+ char dd[2];
+};
+
+static const struct digit_pair *digits_dd = (struct digit_pair *)(
+ "00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
+ "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
+ "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
+ "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
+ "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
+ "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
+ "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
+ "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
+ "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
+ "90" "91" "92" "93" "94" "95" "96" "97" "98" "99"
+);
+
+static const struct digit_pair *digits_fd = (struct digit_pair *)(
+ "0_" "1_" "2_" "3_" "4_" "5_" "6_" "7_" "8_" "9_"
+ "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
+ "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
+ "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
+ "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
+ "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
+ "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
+ "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
+ "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
+ "90" "91" "92" "93" "94" "95" "96" "97" "98" "99"
+);
+
+static const u64_t mask24 = (u64(1) << 24) - 1;
+static const u64_t mask32 = (u64(1) << 32) - 1;
+static const u64_t mask57 = (u64(1) << 57) - 1;
+
+#define COPY(buffer, digits) memcpy(buffer, &(digits), sizeof(struct digit_pair))
+
+static char *
+jeaiii_ultoa(char *b, u64_t n)
+{
+ if (n < u32(1e2)) {
+ COPY(b, digits_fd[n]);
+ return n < 10 ? b + 1 : b + 2;
+ }
+
+ if (n < u32(1e6)) {
+ if (n < u32(1e4)) {
+ u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * n);
+ COPY(b, digits_fd[f0 >> 24]);
+
+ b -= n < u32(1e3);
+ u32_t f2 = (f0 & mask24) * 100;
+ COPY(b + 2, digits_dd[f2 >> 24]);
+
+ return b + 4;
+ }
+
+ u64_t f0 = u64(10 * (1ull << 32ull)/ 1e5 + 1) * n;
+ COPY(b, digits_fd[f0 >> 32]);
+
+ b -= n < u32(1e5);
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+ return b + 6;
+ }
+
+ if (n < u64(1ull << 32ull)) {
+ if (n < u32(1e8)) {
+ u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * n >> 16;
+ COPY(b, digits_fd[f0 >> 32]);
+
+ b -= n < u32(1e7);
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+
+ u64_t f6 = (f4 & mask32) * 100;
+ COPY(b + 6, digits_dd[f6 >> 32]);
+
+ return b + 8;
+ }
+
+ u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * n;
+ COPY(b, digits_fd[f0 >> 57]);
+
+ b -= n < u32(1e9);
+ u64_t f2 = (f0 & mask57) * 100;
+ COPY(b + 2, digits_dd[f2 >> 57]);
+
+ u64_t f4 = (f2 & mask57) * 100;
+ COPY(b + 4, digits_dd[f4 >> 57]);
+
+ u64_t f6 = (f4 & mask57) * 100;
+ COPY(b + 6, digits_dd[f6 >> 57]);
+
+ u64_t f8 = (f6 & mask57) * 100;
+ COPY(b + 8, digits_dd[f8 >> 57]);
+
+ return b + 10;
+ }
+
+ // if we get here U must be u64 but some compilers don't know that, so reassign n to a u64 to avoid warnings
+ u32_t z = n % u32(1e8);
+ u64_t u = n / u32(1e8);
+
+ if (u < u32(1e2)) {
+ // u can't be 1 digit (if u < 10 it would have been handled above as a 9 digit 32bit number)
+ COPY(b, digits_dd[u]);
+ b += 2;
+ }
+ else if (u < u32(1e6)) {
+ if (u < u32(1e4)) {
+ u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u);
+ COPY(b, digits_fd[f0 >> 24]);
+
+ b -= u < u32(1e3);
+ u32_t f2 = (f0 & mask24) * 100;
+ COPY(b + 2, digits_dd[f2 >> 24]);
+ b += 4;
+ }
+ else {
+ u64_t f0 = u64(10 * (1ull << 32ull) / 1e5 + 1) * u;
+ COPY(b, digits_fd[f0 >> 32]);
+
+ b -= u < u32(1e5);
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+ b += 6;
+ }
+ }
+ else if (u < u32(1e8)) {
+ u64_t f0 = u64(10 * (1ull << 48ull) / 1e7 + 1) * u >> 16;
+ COPY(b, digits_fd[f0 >> 32]);
+
+ b -= u < u32(1e7);
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+
+ u64_t f6 = (f4 & mask32) * 100;
+ COPY(b + 6, digits_dd[f6 >> 32]);
+
+ b += 8;
+ }
+ else if (u < u64(1ull << 32ull)) {
+ u64_t f0 = u64(10 * (1ull << 57ull) / 1e9 + 1) * u;
+ COPY(b, digits_fd[f0 >> 57]);
+
+ b -= u < u32(1e9);
+ u64_t f2 = (f0 & mask57) * 100;
+ COPY(b + 2, digits_dd[f2 >> 57]);
+
+ u64_t f4 = (f2 & mask57) * 100;
+ COPY(b + 4, digits_dd[f4 >> 57]);
+
+ u64_t f6 = (f4 & mask57) * 100;
+ COPY(b + 6, digits_dd[f6 >> 57]);
+
+ u64_t f8 = (f6 & mask57) * 100;
+ COPY(b + 8, digits_dd[f8 >> 57]);
+ b += 10;
+ }
+ else {
+ u32_t y = u % u32(1e8);
+ u /= u32(1e8);
+
+ // u is 2, 3, or 4 digits (if u < 10 it would have been handled above)
+ if (u < u32(1e2)) {
+ COPY(b, digits_dd[u]);
+ b += 2;
+ }
+ else {
+ u32_t f0 = u32((10 * (1 << 24) / 1e3 + 1) * u);
+ COPY(b, digits_fd[f0 >> 24]);
+
+ b -= u < u32(1e3);
+ u32_t f2 = (f0 & mask24) * 100;
+ COPY(b + 2, digits_dd[f2 >> 24]);
+
+ b += 4;
+ }
+ // do 8 digits
+ u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * y >> 16) + 1;
+ COPY(b, digits_dd[f0 >> 32]);
+
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+
+ u64_t f6 = (f4 & mask32) * 100;
+ COPY(b + 6, digits_dd[f6 >> 32]);
+ b += 8;
+ }
+
+ // do 8 digits
+ u64_t f0 = (u64((1ull << 48ull) / 1e6 + 1) * z >> 16) + 1;
+ COPY(b, digits_dd[f0 >> 32]);
+
+ u64_t f2 = (f0 & mask32) * 100;
+ COPY(b + 2, digits_dd[f2 >> 32]);
+
+ u64_t f4 = (f2 & mask32) * 100;
+ COPY(b + 4, digits_dd[f4 >> 32]);
+
+ u64_t f6 = (f4 & mask32) * 100;
+ COPY(b + 6, digits_dd[f6 >> 32]);
+
+ return b + 8;
+}
+
+#undef u32
+#undef u64
+#undef COPY
+
+#endif // JEAIII_TO_TEXT_H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/json.gemspec b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/json.gemspec
new file mode 100644
index 0000000..5575731
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/json.gemspec
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+version = File.foreach(File.join(__dir__, "lib/json/version.rb")) do |line|
+ /^\s*VERSION\s*=\s*'(.*)'/ =~ line and break $1
+end rescue nil
+
+spec = Gem::Specification.new do |s|
+ java_ext = Gem::Platform === s.platform && s.platform =~ 'java' || RUBY_ENGINE == 'jruby'
+
+ s.name = "json"
+ s.version = version
+
+ s.summary = "JSON Implementation for Ruby"
+ s.homepage = "https://github.com/ruby/json"
+ s.metadata = {
+ 'bug_tracker_uri' => 'https://github.com/ruby/json/issues',
+ 'changelog_uri' => 'https://github.com/ruby/json/blob/master/CHANGES.md',
+ 'documentation_uri' => 'https://docs.ruby-lang.org/en/master/JSON.html',
+ 'homepage_uri' => s.homepage,
+ 'source_code_uri' => 'https://github.com/ruby/json',
+ }
+
+ s.required_ruby_version = Gem::Requirement.new(">= 2.7")
+
+ if java_ext
+ s.description = "A JSON implementation as a JRuby extension."
+ s.author = "Daniel Luz"
+ s.email = "dev+ruby@mernen.com"
+ else
+ s.description = "This is a JSON implementation as a Ruby extension in C."
+ s.authors = ["Florian Frank"]
+ s.email = "flori@ping.de"
+ end
+
+ s.licenses = ["Ruby"]
+
+ s.extra_rdoc_files = ["README.md"]
+ s.rdoc_options = ["--title", "JSON implementation for Ruby", "--main", "README.md"]
+
+ s.files = [
+ "CHANGES.md",
+ "COPYING",
+ "BSDL",
+ "LEGAL",
+ "README.md",
+ "json.gemspec",
+ ] + Dir.glob("lib/**/*.rb", base: File.expand_path("..", __FILE__))
+
+ if java_ext
+ s.platform = 'java'
+ s.files += Dir["lib/json/ext/**/*.jar"]
+ else
+ s.extensions = Dir["ext/json/**/extconf.rb"]
+ s.files += Dir["ext/json/**/*.{c,h,rb}"]
+ end
+end
+
+if RUBY_ENGINE == 'jruby' && $0 == __FILE__
+ Gem::Builder.new(spec).build
+else
+ spec
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json.rb
new file mode 100644
index 0000000..d1e9414
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json.rb
@@ -0,0 +1,693 @@
+# frozen_string_literal: true
+require 'json/common'
+
+##
+# = JavaScript \Object Notation (\JSON)
+#
+# \JSON is a lightweight data-interchange format.
+#
+# \JSON is easy for us humans to read and write,
+# and equally simple for machines to read (parse) and write (generate).
+#
+# \JSON is language-independent, making it an ideal interchange format
+# for applications in differing programming languages
+# and on differing operating systems.
+#
+# == \JSON Values
+#
+# A \JSON value is one of the following:
+# - Double-quoted text: <tt>"foo"</tt>.
+# - Number: +1+, +1.0+, +2.0e2+.
+# - Boolean: +true+, +false+.
+# - Null: +null+.
+# - \Array: an ordered list of values, enclosed by square brackets:
+# ["foo", 1, 1.0, 2.0e2, true, false, null]
+#
+# - \Object: a collection of name/value pairs, enclosed by curly braces;
+# each name is double-quoted text;
+# the values may be any \JSON values:
+# {"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
+#
+# A \JSON array or object may contain nested arrays, objects, and scalars
+# to any depth:
+# {"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}
+# [{"foo": 0, "bar": 1}, ["baz", 2]]
+#
+# == Using \Module \JSON
+#
+# To make module \JSON available in your code, begin with:
+# require 'json'
+#
+# All examples here assume that this has been done.
+#
+# === Parsing \JSON
+#
+# You can parse a \String containing \JSON data using
+# either of two methods:
+# - <tt>JSON.parse(source, opts)</tt>
+# - <tt>JSON.parse!(source, opts)</tt>
+#
+# where
+# - +source+ is a Ruby object.
+# - +opts+ is a \Hash object containing options
+# that control both input allowed and output formatting.
+#
+# The difference between the two methods
+# is that JSON.parse! omits some checks
+# and may not be safe for some +source+ data;
+# use it only for data from trusted sources.
+# Use the safer method JSON.parse for less trusted sources.
+#
+# ==== Parsing \JSON Arrays
+#
+# When +source+ is a \JSON array, JSON.parse by default returns a Ruby \Array:
+# json = '["foo", 1, 1.0, 2.0e2, true, false, null]'
+# ruby = JSON.parse(json)
+# ruby # => ["foo", 1, 1.0, 200.0, true, false, nil]
+# ruby.class # => Array
+#
+# The \JSON array may contain nested arrays, objects, and scalars
+# to any depth:
+# json = '[{"foo": 0, "bar": 1}, ["baz", 2]]'
+# JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
+#
+# ==== Parsing \JSON \Objects
+#
+# When the source is a \JSON object, JSON.parse by default returns a Ruby \Hash:
+# json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}'
+# ruby = JSON.parse(json)
+# ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil}
+# ruby.class # => Hash
+#
+# The \JSON object may contain nested arrays, objects, and scalars
+# to any depth:
+# json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}'
+# JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
+#
+# ==== Parsing \JSON Scalars
+#
+# When the source is a \JSON scalar (not an array or object),
+# JSON.parse returns a Ruby scalar.
+#
+# \String:
+# ruby = JSON.parse('"foo"')
+# ruby # => 'foo'
+# ruby.class # => String
+# \Integer:
+# ruby = JSON.parse('1')
+# ruby # => 1
+# ruby.class # => Integer
+# \Float:
+# ruby = JSON.parse('1.0')
+# ruby # => 1.0
+# ruby.class # => Float
+# ruby = JSON.parse('2.0e2')
+# ruby # => 200
+# ruby.class # => Float
+# Boolean:
+# ruby = JSON.parse('true')
+# ruby # => true
+# ruby.class # => TrueClass
+# ruby = JSON.parse('false')
+# ruby # => false
+# ruby.class # => FalseClass
+# Null:
+# ruby = JSON.parse('null')
+# ruby # => nil
+# ruby.class # => NilClass
+#
+# ==== Parsing Options
+#
+# ====== Input Options
+#
+# Option +max_nesting+ (\Integer) specifies the maximum nesting depth allowed;
+# defaults to +100+; specify +false+ to disable depth checking.
+#
+# With the default, +false+:
+# source = '[0, [1, [2, [3]]]]'
+# ruby = JSON.parse(source)
+# ruby # => [0, [1, [2, [3]]]]
+# Too deep:
+# # Raises JSON::NestingError (nesting of 2 is too deep):
+# JSON.parse(source, {max_nesting: 1})
+# Bad value:
+# # Raises TypeError (wrong argument type Symbol (expected Fixnum)):
+# JSON.parse(source, {max_nesting: :foo})
+#
+# ---
+#
+# Option +allow_duplicate_key+ specifies whether duplicate keys in objects
+# should be ignored or cause an error to be raised:
+#
+# When not specified:
+# # The last value is used and a deprecation warning emitted.
+# JSON.parse('{"a": 1, "a":2}') => {"a" => 2}
+# # warning: detected duplicate keys in JSON object.
+# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
+#
+# When set to +true+:
+# # The last value is used.
+# JSON.parse('{"a": 1, "a":2}') => {"a" => 2}
+#
+# When set to +false+, the future default:
+# JSON.parse('{"a": 1, "a":2}') => duplicate key at line 1 column 1 (JSON::ParserError)
+#
+# ---
+#
+# Option +allow_nan+ (boolean) specifies whether to allow
+# NaN, Infinity, and MinusInfinity in +source+;
+# defaults to +false+.
+#
+# With the default, +false+:
+# # Raises JSON::ParserError (225: unexpected token at '[NaN]'):
+# JSON.parse('[NaN]')
+# # Raises JSON::ParserError (232: unexpected token at '[Infinity]'):
+# JSON.parse('[Infinity]')
+# # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'):
+# JSON.parse('[-Infinity]')
+# Allow:
+# source = '[NaN, Infinity, -Infinity]'
+# ruby = JSON.parse(source, {allow_nan: true})
+# ruby # => [NaN, Infinity, -Infinity]
+#
+# ---
+#
+# Option +allow_trailing_comma+ (boolean) specifies whether to allow
+# trailing commas in objects and arrays;
+# defaults to +false+.
+#
+# With the default, +false+:
+# JSON.parse('[1,]') # unexpected character: ']' at line 1 column 4 (JSON::ParserError)
+#
+# When enabled:
+# JSON.parse('[1,]', allow_trailing_comma: true) # => [1]
+#
+# ---
+#
+# Option +allow_comments+ (boolean) specifies whether to allow
+# JavaScript style comments (either <tt>// comment</tt> or <tt>/* comment */</tt>);
+# defaults to +false+.
+#
+# When not specified, a deprecation warning is emitted if a comment is encountered.
+#
+# When set to +true+, comments are ignored:
+# JSON.parse('/* comment */ {"a": 1, "a":2}') # => {"a" => 2}
+#
+# When set to +false+, the future default:
+# JSON.parse('/* comment */ {"a": 1, "a":2}') # unexpected character: '/' at line 1 column 1 (JSON::ParserError)
+#
+# ---
+#
+# Option +allow_control_characters+ (boolean) specifies whether to allow
+# unescaped ASCII control characters, such as newlines, in strings;
+# defaults to +false+.
+#
+# With the default, +false+:
+# JSON.parse(%{"Hello\nWorld"}) # invalid ASCII control character in string (JSON::ParserError)
+#
+# When enabled:
+# JSON.parse(%{"Hello\nWorld"}, allow_control_characters: true) # => "Hello\nWorld"
+#
+# ---
+#
+# Option +allow_invalid_escape+ (boolean) specifies whether to ignore backslahes that are followed
+# by an invalid escape character in strings;
+# defaults to +false+.
+#
+# With the default, +false+:
+# JSON.parse('"Hell\o"') # invalid escape character in string (JSON::ParserError)
+#
+# When enabled:
+# JSON.parse('"Hell\o"', allow_invalid_escape: true) # => "Hello"
+#
+# ====== Output Options
+#
+# Option +freeze+ (boolean) specifies whether the returned objects will be frozen;
+# defaults to +false+.
+#
+# Option +symbolize_names+ (boolean) specifies whether returned \Hash keys
+# should be Symbols;
+# defaults to +false+ (use Strings).
+#
+# With the default, +false+:
+# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+# ruby = JSON.parse(source)
+# ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
+# Use Symbols:
+# ruby = JSON.parse(source, {symbolize_names: true})
+# ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
+#
+# ---
+#
+# Option +object_class+ (\Class) specifies the Ruby class to be used
+# for each \JSON object;
+# defaults to \Hash.
+#
+# With the default, \Hash:
+# source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+# ruby = JSON.parse(source)
+# ruby.class # => Hash
+# Use class \OpenStruct:
+# ruby = JSON.parse(source, {object_class: OpenStruct})
+# ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
+#
+# ---
+#
+# Option +array_class+ (\Class) specifies the Ruby class to be used
+# for each \JSON array;
+# defaults to \Array.
+#
+# With the default, \Array:
+# source = '["foo", 1.0, true, false, null]'
+# ruby = JSON.parse(source)
+# ruby.class # => Array
+# Use class \Set:
+# ruby = JSON.parse(source, {array_class: Set})
+# ruby # => #<Set: {"foo", 1.0, true, false, nil}>
+#
+# ---
+#
+# Option +create_additions+ (boolean) specifies whether to use \JSON additions in parsing.
+# See {\JSON Additions}[#module-JSON-label-JSON+Additions].
+#
+# === Generating \JSON
+#
+# To generate a Ruby \String containing \JSON data,
+# use method <tt>JSON.generate(source, opts)</tt>, where
+# - +source+ is a Ruby object.
+# - +opts+ is a \Hash object containing options
+# that control both input allowed and output formatting.
+#
+# ==== Generating \JSON from Arrays
+#
+# When the source is a Ruby \Array, JSON.generate returns
+# a \String containing a \JSON array:
+# ruby = [0, 's', :foo]
+# json = JSON.generate(ruby)
+# json # => '[0,"s","foo"]'
+#
+# The Ruby \Array array may contain nested arrays, hashes, and scalars
+# to any depth:
+# ruby = [0, [1, 2], {foo: 3, bar: 4}]
+# json = JSON.generate(ruby)
+# json # => '[0,[1,2],{"foo":3,"bar":4}]'
+#
+# ==== Generating \JSON from Hashes
+#
+# When the source is a Ruby \Hash, JSON.generate returns
+# a \String containing a \JSON object:
+# ruby = {foo: 0, bar: 's', baz: :bat}
+# json = JSON.generate(ruby)
+# json # => '{"foo":0,"bar":"s","baz":"bat"}'
+#
+# The Ruby \Hash array may contain nested arrays, hashes, and scalars
+# to any depth:
+# ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
+# json = JSON.generate(ruby)
+# json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
+#
+# ==== Generating \JSON from Other Objects
+#
+# When the source is neither an \Array nor a \Hash,
+# the generated \JSON data depends on the class of the source.
+#
+# When the source is a Ruby \Integer or \Float, JSON.generate returns
+# a \String containing a \JSON number:
+# JSON.generate(42) # => '42'
+# JSON.generate(0.42) # => '0.42'
+#
+# When the source is a Ruby \String, JSON.generate returns
+# a \String containing a \JSON string (with double-quotes):
+# JSON.generate('A string') # => '"A string"'
+#
+# When the source is +true+, +false+ or +nil+, JSON.generate returns
+# a \String containing the corresponding \JSON token:
+# JSON.generate(true) # => 'true'
+# JSON.generate(false) # => 'false'
+# JSON.generate(nil) # => 'null'
+#
+# When the source is none of the above, JSON.generate returns
+# a \String containing a \JSON string representation of the source:
+# JSON.generate(:foo) # => '"foo"'
+# JSON.generate(Complex(0, 0)) # => '"0+0i"'
+# JSON.generate(Dir.new('.')) # => '"#<Dir>"'
+#
+# ==== Generating Options
+#
+# ====== Input Options
+#
+# Option +allow_nan+ (boolean) specifies whether
+# +NaN+, +Infinity+, and <tt>-Infinity</tt> may be generated;
+# defaults to +false+.
+#
+# With the default, +false+:
+# # Raises JSON::GeneratorError (920: NaN not allowed in JSON):
+# JSON.generate(JSON::NaN)
+# # Raises JSON::GeneratorError (917: Infinity not allowed in JSON):
+# JSON.generate(JSON::Infinity)
+# # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON):
+# JSON.generate(JSON::MinusInfinity)
+#
+# Allow:
+# ruby = [Float::NAN, Float::INFINITY, JSON::NaN, JSON::Infinity, JSON::MinusInfinity]
+# JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,NaN,Infinity,-Infinity]'
+#
+# ---
+#
+# Option +allow_duplicate_key+ (boolean) specifies whether
+# hashes with duplicate keys should be allowed or produce an error.
+# defaults to emit a deprecation warning.
+#
+# With the default, (not set):
+# Warning[:deprecated] = true
+# JSON.generate({ foo: 1, "foo" => 2 })
+# # warning: detected duplicate key "foo" in {foo: 1, "foo" => 2}.
+# # This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`
+# # => '{"foo":1,"foo":2}'
+#
+# With <tt>false</tt>
+# JSON.generate({ foo: 1, "foo" => 2 }, allow_duplicate_key: false)
+# # detected duplicate key "foo" in {foo: 1, "foo" => 2} (JSON::GeneratorError)
+#
+# In version 3.0, <tt>false</tt> will become the default.
+#
+# ---
+#
+# Option +max_nesting+ (\Integer) specifies the maximum nesting depth
+# in +obj+; defaults to +100+.
+#
+# With the default, +100+:
+# obj = [[[[[[0]]]]]]
+# JSON.generate(obj) # => '[[[[[[0]]]]]]'
+#
+# Too deep:
+# # Raises JSON::NestingError (nesting of 2 is too deep):
+# JSON.generate(obj, max_nesting: 2)
+#
+# ====== Escaping Options
+#
+# Options +script_safe+ (boolean) specifies wether <tt>'\u2028'</tt>, <tt>'\u2029'</tt>
+# and <tt>'/'</tt> should be escaped as to make the JSON object safe to interpolate in script
+# tags.
+#
+# Options +ascii_only+ (boolean) specifies wether all characters outside the ASCII range
+# should be escaped.
+#
+# ====== Output Options
+#
+# The default formatting options generate the most compact
+# \JSON data, all on one line and with no whitespace.
+#
+# You can use these formatting options to generate
+# \JSON data in a more open format, using whitespace.
+# See also JSON.pretty_generate.
+#
+# - Option +array_nl+ (\String) specifies a string (usually a newline)
+# to be inserted after each \JSON array; defaults to the empty \String, <tt>''</tt>.
+# - Option +object_nl+ (\String) specifies a string (usually a newline)
+# to be inserted after each \JSON object; defaults to the empty \String, <tt>''</tt>.
+# - Option +indent+ (\String) specifies the string (usually spaces) to be
+# used for indentation; defaults to the empty \String, <tt>''</tt>;
+# has no effect unless options +array_nl+ or +object_nl+ specify newlines.
+# - Option +space+ (\String) specifies a string (usually a space) to be
+# inserted after the colon in each \JSON object's pair;
+# defaults to the empty \String, <tt>''</tt>.
+# - Option +space_before+ (\String) specifies a string (usually a space) to be
+# inserted before the colon in each \JSON object's pair;
+# defaults to the empty \String, <tt>''</tt>.
+# - Option +sort_keys+ (boolean or \Proc) controls whether and how the keys of a
+# hash are sorted when generating the output; defaults to <tt>false</tt>.
+# When +true+, keys are sorted lexicographically. When a \Proc, it receives
+# the entire \Hash and must return a \Hash with its pairs in the desired
+# order, allowing for arbitrary sort orders.
+#
+# In this example, +obj+ is used first to generate the shortest
+# \JSON data (no whitespace), then again with all formatting options
+# specified:
+#
+# obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
+# json = JSON.generate(obj)
+# puts 'Compact:', json
+# opts = {
+# array_nl: "\n",
+# object_nl: "\n",
+# indent: ' ',
+# space_before: ' ',
+# space: ' '
+# }
+# puts 'Open:', JSON.generate(obj, opts)
+#
+# Output:
+# Compact:
+# {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}}
+# Open:
+# {
+# "foo" : [
+# "bar",
+# "baz"
+# ],
+# "bat" : {
+# "bam" : 0,
+# "bad" : 1
+# }
+# }
+#
+# == \JSON Additions
+#
+# Note that JSON Additions must only be used with trusted data, and is
+# deprecated.
+#
+# When you "round trip" a non-\String object from Ruby to \JSON and back,
+# you have a new \String, instead of the object you began with:
+# ruby0 = Range.new(0, 2)
+# json = JSON.generate(ruby0)
+# json # => '0..2"'
+# ruby1 = JSON.parse(json)
+# ruby1 # => '0..2'
+# ruby1.class # => String
+#
+# You can use \JSON _additions_ to preserve the original object.
+# The addition is an extension of a ruby class, so that:
+# - \JSON.generate stores more information in the \JSON string.
+# - \JSON.parse, called with option +create_additions+,
+# uses that information to create a proper Ruby object.
+#
+# This example shows a \Range being generated into \JSON
+# and parsed back into Ruby, both without and with
+# the addition for \Range:
+# ruby = Range.new(0, 2)
+# # This passage does not use the addition for Range.
+# json0 = JSON.generate(ruby)
+# ruby0 = JSON.parse(json0)
+# # This passage uses the addition for Range.
+# require 'json/add/range'
+# json1 = JSON.generate(ruby)
+# ruby1 = JSON.parse(json1, create_additions: true)
+# # Make a nice display.
+# display = <<~EOT
+# Generated JSON:
+# Without addition: #{json0} (#{json0.class})
+# With addition: #{json1} (#{json1.class})
+# Parsed JSON:
+# Without addition: #{ruby0.inspect} (#{ruby0.class})
+# With addition: #{ruby1.inspect} (#{ruby1.class})
+# EOT
+# puts display
+#
+# This output shows the different results:
+# Generated JSON:
+# Without addition: "0..2" (String)
+# With addition: {"json_class":"Range","a":[0,2,false]} (String)
+# Parsed JSON:
+# Without addition: "0..2" (String)
+# With addition: 0..2 (Range)
+#
+# The \JSON module includes additions for certain classes.
+# You can also craft custom additions.
+# See {Custom \JSON Additions}[#module-JSON-label-Custom+JSON+Additions].
+#
+# === Built-in Additions
+#
+# The \JSON module includes additions for certain classes.
+# To use an addition, +require+ its source:
+# - BigDecimal: <tt>require 'json/add/bigdecimal'</tt>
+# - Complex: <tt>require 'json/add/complex'</tt>
+# - Date: <tt>require 'json/add/date'</tt>
+# - DateTime: <tt>require 'json/add/date_time'</tt>
+# - Exception: <tt>require 'json/add/exception'</tt>
+# - OpenStruct: <tt>require 'json/add/ostruct'</tt>
+# - Range: <tt>require 'json/add/range'</tt>
+# - Rational: <tt>require 'json/add/rational'</tt>
+# - Regexp: <tt>require 'json/add/regexp'</tt>
+# - Set: <tt>require 'json/add/set'</tt>
+# - Struct: <tt>require 'json/add/struct'</tt>
+# - Symbol: <tt>require 'json/add/symbol'</tt>
+# - Time: <tt>require 'json/add/time'</tt>
+#
+# To reduce punctuation clutter, the examples below
+# show the generated \JSON via +puts+, rather than the usual +inspect+,
+#
+# \BigDecimal:
+# require 'json/add/bigdecimal'
+# ruby0 = BigDecimal(0) # 0.0
+# json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"}
+# ruby1 = JSON.parse(json, create_additions: true) # 0.0
+# ruby1.class # => BigDecimal
+#
+# \Complex:
+# require 'json/add/complex'
+# ruby0 = Complex(1+0i) # 1+0i
+# json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0}
+# ruby1 = JSON.parse(json, create_additions: true) # 1+0i
+# ruby1.class # Complex
+#
+# \Date:
+# require 'json/add/date'
+# ruby0 = Date.today # 2020-05-02
+# json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02
+# ruby1.class # Date
+#
+# \DateTime:
+# require 'json/add/date_time'
+# ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00
+# json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00
+# ruby1.class # DateTime
+#
+# \Exception (and its subclasses including \RuntimeError):
+# require 'json/add/exception'
+# ruby0 = Exception.new('A message') # A message
+# json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null}
+# ruby1 = JSON.parse(json, create_additions: true) # A message
+# ruby1.class # Exception
+# ruby0 = RuntimeError.new('Another message') # Another message
+# json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null}
+# ruby1 = JSON.parse(json, create_additions: true) # Another message
+# ruby1.class # RuntimeError
+#
+# \OpenStruct:
+# require 'json/add/ostruct'
+# ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby">
+# json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}}
+# ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby">
+# ruby1.class # OpenStruct
+#
+# \Range:
+# require 'json/add/range'
+# ruby0 = Range.new(0, 2) # 0..2
+# json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]}
+# ruby1 = JSON.parse(json, create_additions: true) # 0..2
+# ruby1.class # Range
+#
+# \Rational:
+# require 'json/add/rational'
+# ruby0 = Rational(1, 3) # 1/3
+# json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3}
+# ruby1 = JSON.parse(json, create_additions: true) # 1/3
+# ruby1.class # Rational
+#
+# \Regexp:
+# require 'json/add/regexp'
+# ruby0 = Regexp.new('foo') # (?-mix:foo)
+# json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"}
+# ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo)
+# ruby1.class # Regexp
+#
+# \Set:
+# require 'json/add/set'
+# ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}>
+# json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]}
+# ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}>
+# ruby1.class # Set
+#
+# \Struct:
+# require 'json/add/struct'
+# Customer = Struct.new(:name, :address) # Customer
+# ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main">
+# json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]}
+# ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main">
+# ruby1.class # Customer
+#
+# \Symbol:
+# require 'json/add/symbol'
+# ruby0 = :foo # foo
+# json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"}
+# ruby1 = JSON.parse(json, create_additions: true) # foo
+# ruby1.class # Symbol
+#
+# \Time:
+# require 'json/add/time'
+# ruby0 = Time.now # 2020-05-02 11:28:26 -0500
+# json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000}
+# ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500
+# ruby1.class # Time
+#
+#
+# === Custom \JSON Additions
+#
+# In addition to the \JSON additions provided,
+# you can craft \JSON additions of your own,
+# either for Ruby built-in classes or for user-defined classes.
+#
+# Here's a user-defined class +Foo+:
+# class Foo
+# attr_accessor :bar, :baz
+# def initialize(bar, baz)
+# self.bar = bar
+# self.baz = baz
+# end
+# end
+#
+# Here's the \JSON addition for it:
+# # Extend class Foo with JSON addition.
+# class Foo
+# # Serialize Foo object with its class name and arguments
+# def to_json(*args)
+# {
+# JSON.create_id => self.class.name,
+# 'a' => [ bar, baz ]
+# }.to_json(*args)
+# end
+# # Deserialize JSON string by constructing new Foo object with arguments.
+# def self.json_create(object)
+# new(*object['a'])
+# end
+# end
+#
+# Demonstration:
+# require 'json'
+# # This Foo object has no custom addition.
+# foo0 = Foo.new(0, 1)
+# json0 = JSON.generate(foo0)
+# obj0 = JSON.parse(json0)
+# # Lood the custom addition.
+# require_relative 'foo_addition'
+# # This foo has the custom addition.
+# foo1 = Foo.new(0, 1)
+# json1 = JSON.generate(foo1)
+# obj1 = JSON.parse(json1, create_additions: true)
+# # Make a nice display.
+# display = <<~EOT
+# Generated JSON:
+# Without custom addition: #{json0} (#{json0.class})
+# With custom addition: #{json1} (#{json1.class})
+# Parsed JSON:
+# Without custom addition: #{obj0.inspect} (#{obj0.class})
+# With custom addition: #{obj1.inspect} (#{obj1.class})
+# EOT
+# puts display
+#
+# Output:
+#
+# Generated JSON:
+# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
+# With custom addition: {"json_class":"Foo","a":[0,1]} (String)
+# Parsed JSON:
+# Without custom addition: "#<Foo:0x0000000006534e80>" (String)
+# With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)
+#
+module JSON
+ require 'json/version'
+ require 'json/ext'
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/bigdecimal.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/bigdecimal.rb
new file mode 100644
index 0000000..5dbc12c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/bigdecimal.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+begin
+ require 'bigdecimal'
+rescue LoadError
+end
+
+class BigDecimal
+
+ # See #as_json.
+ def self.json_create(object)
+ BigDecimal._load object['b']
+ end
+
+ # Methods <tt>BigDecimal#as_json</tt> and +BigDecimal.json_create+ may be used
+ # to serialize and deserialize a \BigDecimal object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>BigDecimal#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/bigdecimal'
+ # x = BigDecimal(2).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
+ # y = BigDecimal(2.0, 4).as_json # => {"json_class"=>"BigDecimal", "b"=>"36:0.2e1"}
+ # z = BigDecimal(Complex(2, 0)).as_json # => {"json_class"=>"BigDecimal", "b"=>"27:0.2e1"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \BigDecimal object:
+ #
+ # BigDecimal.json_create(x) # => 0.2e1
+ # BigDecimal.json_create(y) # => 0.2e1
+ # BigDecimal.json_create(z) # => 0.2e1
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'b' => _dump.force_encoding(Encoding::UTF_8),
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/bigdecimal'
+ # puts BigDecimal(2).to_json
+ # puts BigDecimal(2.0, 4).to_json
+ # puts BigDecimal(Complex(2, 0)).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"BigDecimal","b":"27:0.2e1"}
+ # {"json_class":"BigDecimal","b":"36:0.2e1"}
+ # {"json_class":"BigDecimal","b":"27:0.2e1"}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end if defined?(::BigDecimal)
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/complex.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/complex.rb
new file mode 100644
index 0000000..a69002e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/complex.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Complex
+
+ # See #as_json.
+ def self.json_create(object)
+ Complex(object['r'], object['i'])
+ end
+
+ # Methods <tt>Complex#as_json</tt> and +Complex.json_create+ may be used
+ # to serialize and deserialize a \Complex object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Complex#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/complex'
+ # x = Complex(2).as_json # => {"json_class"=>"Complex", "r"=>2, "i"=>0}
+ # y = Complex(2.0, 4).as_json # => {"json_class"=>"Complex", "r"=>2.0, "i"=>4}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Complex object:
+ #
+ # Complex.json_create(x) # => (2+0i)
+ # Complex.json_create(y) # => (2.0+4i)
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'r' => real,
+ 'i' => imag,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/complex'
+ # puts Complex(2).to_json
+ # puts Complex(2.0, 4).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Complex","r":2,"i":0}
+ # {"json_class":"Complex","r":2.0,"i":4}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/core.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/core.rb
new file mode 100644
index 0000000..61ff454
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/core.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+# This file requires the implementations of ruby core's custom objects for
+# serialisation/deserialisation.
+
+require 'json/add/date'
+require 'json/add/date_time'
+require 'json/add/exception'
+require 'json/add/range'
+require 'json/add/regexp'
+require 'json/add/string'
+require 'json/add/struct'
+require 'json/add/symbol'
+require 'json/add/time'
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date.rb
new file mode 100644
index 0000000..66965d4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+require 'date'
+
+class Date
+
+ # See #as_json.
+ def self.json_create(object)
+ civil(*object.values_at('y', 'm', 'd', 'sg'))
+ end
+
+ alias start sg unless method_defined?(:start)
+
+ # Methods <tt>Date#as_json</tt> and +Date.json_create+ may be used
+ # to serialize and deserialize a \Date object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Date#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/date'
+ # x = Date.today.as_json
+ # # => {"json_class"=>"Date", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Date object:
+ #
+ # Date.json_create(x)
+ # # => #<Date: 2023-11-21 ((2460270j,0s,0n),+0s,2299161j)>
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'y' => year,
+ 'm' => month,
+ 'd' => day,
+ 'sg' => start,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/date'
+ # puts Date.today.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Date","y":2023,"m":11,"d":21,"sg":2299161.0}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date_time.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date_time.rb
new file mode 100644
index 0000000..569f6ec
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/date_time.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+require 'date'
+
+class DateTime
+
+ # See #as_json.
+ def self.json_create(object)
+ args = object.values_at('y', 'm', 'd', 'H', 'M', 'S')
+ of_a, of_b = object['of'].split('/')
+ if of_b and of_b != '0'
+ args << Rational(of_a.to_i, of_b.to_i)
+ else
+ args << of_a
+ end
+ args << object['sg']
+ civil(*args)
+ end
+
+ alias start sg unless method_defined?(:start)
+
+ # Methods <tt>DateTime#as_json</tt> and +DateTime.json_create+ may be used
+ # to serialize and deserialize a \DateTime object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>DateTime#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/datetime'
+ # x = DateTime.now.as_json
+ # # => {"json_class"=>"DateTime", "y"=>2023, "m"=>11, "d"=>21, "sg"=>2299161.0}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \DateTime object:
+ #
+ # DateTime.json_create(x) # BUG? Raises Date::Error "invalid date"
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'y' => year,
+ 'm' => month,
+ 'd' => day,
+ 'H' => hour,
+ 'M' => min,
+ 'S' => sec,
+ 'of' => offset.to_s,
+ 'sg' => start,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/datetime'
+ # puts DateTime.now.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"DateTime","y":2023,"m":11,"d":21,"sg":2299161.0}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/exception.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/exception.rb
new file mode 100644
index 0000000..5338ff8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/exception.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Exception
+
+ # See #as_json.
+ def self.json_create(object)
+ result = new(object['m'])
+ result.set_backtrace object['b']
+ result
+ end
+
+ # Methods <tt>Exception#as_json</tt> and +Exception.json_create+ may be used
+ # to serialize and deserialize a \Exception object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Exception#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/exception'
+ # x = Exception.new('Foo').as_json # => {"json_class"=>"Exception", "m"=>"Foo", "b"=>nil}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Exception object:
+ #
+ # Exception.json_create(x) # => #<Exception: Foo>
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'm' => message,
+ 'b' => backtrace,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/exception'
+ # puts Exception.new('Foo').to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Exception","m":"Foo","b":null}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/ostruct.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/ostruct.rb
new file mode 100644
index 0000000..534403b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/ostruct.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+begin
+ require 'ostruct'
+rescue LoadError
+end
+
+class OpenStruct
+
+ # See #as_json.
+ def self.json_create(object)
+ new(object['t'] || object[:t])
+ end
+
+ # Methods <tt>OpenStruct#as_json</tt> and +OpenStruct.json_create+ may be used
+ # to serialize and deserialize a \OpenStruct object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>OpenStruct#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/ostruct'
+ # x = OpenStruct.new('name' => 'Rowdy', :age => nil).as_json
+ # # => {"json_class"=>"OpenStruct", "t"=>{:name=>'Rowdy', :age=>nil}}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \OpenStruct object:
+ #
+ # OpenStruct.json_create(x)
+ # # => #<OpenStruct name='Rowdy', age=nil>
+ #
+ def as_json(*)
+ klass = self.class.name
+ klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
+ {
+ JSON.create_id => klass,
+ 't' => table,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/ostruct'
+ # puts OpenStruct.new('name' => 'Rowdy', :age => nil).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"OpenStruct","t":{'name':'Rowdy',"age":null}}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end if defined?(::OpenStruct)
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/range.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/range.rb
new file mode 100644
index 0000000..eb4b29a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/range.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Range
+
+ # See #as_json.
+ def self.json_create(object)
+ new(*object['a'])
+ end
+
+ # Methods <tt>Range#as_json</tt> and +Range.json_create+ may be used
+ # to serialize and deserialize a \Range object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Range#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/range'
+ # x = (1..4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, false]}
+ # y = (1...4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, true]}
+ # z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Range object:
+ #
+ # Range.json_create(x) # => 1..4
+ # Range.json_create(y) # => 1...4
+ # Range.json_create(z) # => "a".."d"
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'a' => [ first, last, exclude_end? ]
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/range'
+ # puts (1..4).to_json
+ # puts (1...4).to_json
+ # puts ('a'..'d').to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Range","a":[1,4,false]}
+ # {"json_class":"Range","a":[1,4,true]}
+ # {"json_class":"Range","a":["a","d",false]}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/rational.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/rational.rb
new file mode 100644
index 0000000..1eb2314
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/rational.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Rational
+
+ # See #as_json.
+ def self.json_create(object)
+ Rational(object['n'], object['d'])
+ end
+
+ # Methods <tt>Rational#as_json</tt> and +Rational.json_create+ may be used
+ # to serialize and deserialize a \Rational object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Rational#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/rational'
+ # x = Rational(2, 3).as_json
+ # # => {"json_class"=>"Rational", "n"=>2, "d"=>3}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Rational object:
+ #
+ # Rational.json_create(x)
+ # # => (2/3)
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'n' => numerator,
+ 'd' => denominator,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/rational'
+ # puts Rational(2, 3).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Rational","n":2,"d":3}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/regexp.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/regexp.rb
new file mode 100644
index 0000000..f033dd1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/regexp.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Regexp
+
+ # See #as_json.
+ def self.json_create(object)
+ new(object['s'], object['o'])
+ end
+
+ # Methods <tt>Regexp#as_json</tt> and +Regexp.json_create+ may be used
+ # to serialize and deserialize a \Regexp object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Regexp#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/regexp'
+ # x = /foo/.as_json
+ # # => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Regexp object:
+ #
+ # Regexp.json_create(x) # => /foo/
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'o' => options,
+ 's' => source,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/regexp'
+ # puts /foo/.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Regexp","o":0,"s":"foo"}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/set.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/set.rb
new file mode 100644
index 0000000..c521d8b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/set.rb
@@ -0,0 +1,48 @@
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+defined?(::Set) or require 'set'
+
+class Set
+
+ # See #as_json.
+ def self.json_create(object)
+ new object['a']
+ end
+
+ # Methods <tt>Set#as_json</tt> and +Set.json_create+ may be used
+ # to serialize and deserialize a \Set object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Set#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/set'
+ # x = Set.new(%w/foo bar baz/).as_json
+ # # => {"json_class"=>"Set", "a"=>["foo", "bar", "baz"]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Set object:
+ #
+ # Set.json_create(x) # => #<Set: {"foo", "bar", "baz"}>
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 'a' => to_a,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/set'
+ # puts Set.new(%w/foo bar baz/).to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Set","a":["foo","bar","baz"]}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/string.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/string.rb
new file mode 100644
index 0000000..9c3bde2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/string.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class String
+ # call-seq: json_create(o)
+ #
+ # Raw Strings are JSON Objects (the raw bytes are stored in an array for the
+ # key "raw"). The Ruby String can be created by this class method.
+ def self.json_create(object)
+ object["raw"].pack("C*")
+ end
+
+ # call-seq: to_json_raw_object()
+ #
+ # This method creates a raw object hash, that can be nested into
+ # other data structures and will be generated as a raw string. This
+ # method should be used, if you want to convert raw strings to JSON
+ # instead of UTF-8 strings, e. g. binary data.
+ def to_json_raw_object
+ {
+ JSON.create_id => self.class.name,
+ "raw" => unpack("C*"),
+ }
+ end
+
+ # call-seq: to_json_raw(*args)
+ #
+ # This method creates a JSON text from the result of a call to
+ # to_json_raw_object of this String.
+ def to_json_raw(...)
+ to_json_raw_object.to_json(...)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/struct.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/struct.rb
new file mode 100644
index 0000000..98c38d3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/struct.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Struct
+
+ # See #as_json.
+ def self.json_create(object)
+ new(*object['v'])
+ end
+
+ # Methods <tt>Struct#as_json</tt> and +Struct.json_create+ may be used
+ # to serialize and deserialize a \Struct object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Struct#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/struct'
+ # Customer = Struct.new('Customer', :name, :address, :zip)
+ # x = Struct::Customer.new.as_json
+ # # => {"json_class"=>"Struct::Customer", "v"=>[nil, nil, nil]}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Struct object:
+ #
+ # Struct::Customer.json_create(x)
+ # # => #<struct Struct::Customer name=nil, address=nil, zip=nil>
+ #
+ def as_json(*)
+ klass = self.class.name
+ klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!"
+ {
+ JSON.create_id => klass,
+ 'v' => values,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/struct'
+ # Customer = Struct.new('Customer', :name, :address, :zip)
+ # puts Struct::Customer.new.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Struct","t":{'name':'Rowdy',"age":null}}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/symbol.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/symbol.rb
new file mode 100644
index 0000000..20dd594
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/symbol.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Symbol
+
+ # Methods <tt>Symbol#as_json</tt> and +Symbol.json_create+ may be used
+ # to serialize and deserialize a \Symbol object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Symbol#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/symbol'
+ # x = :foo.as_json
+ # # => {"json_class"=>"Symbol", "s"=>"foo"}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Symbol object:
+ #
+ # Symbol.json_create(x) # => :foo
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 's' => to_s,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/symbol'
+ # puts :foo.to_json
+ #
+ # Output:
+ #
+ # # {"json_class":"Symbol","s":"foo"}
+ #
+ def to_json(state = nil, *a)
+ state = ::JSON::State.from_state(state)
+ if state.strict?
+ super
+ else
+ as_json.to_json(state, *a)
+ end
+ end
+
+ # See #as_json.
+ def self.json_create(o)
+ o['s'].to_sym
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/time.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/time.rb
new file mode 100644
index 0000000..05a1f24
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/add/time.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED
+ require 'json'
+end
+
+class Time
+
+ # See #as_json.
+ def self.json_create(object)
+ if usec = object.delete('u') # used to be tv_usec -> tv_nsec
+ object['n'] = usec * 1000
+ end
+ at(object['s'], Rational(object['n'], 1000))
+ end
+
+ # Methods <tt>Time#as_json</tt> and +Time.json_create+ may be used
+ # to serialize and deserialize a \Time object;
+ # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html].
+ #
+ # \Method <tt>Time#as_json</tt> serializes +self+,
+ # returning a 2-element hash representing +self+:
+ #
+ # require 'json/add/time'
+ # x = Time.now.as_json
+ # # => {"json_class"=>"Time", "s"=>1700931656, "n"=>472846644}
+ #
+ # \Method +JSON.create+ deserializes such a hash, returning a \Time object:
+ #
+ # Time.json_create(x)
+ # # => 2023-11-25 11:00:56.472846644 -0600
+ #
+ def as_json(*)
+ {
+ JSON.create_id => self.class.name,
+ 's' => tv_sec,
+ 'n' => tv_nsec,
+ }
+ end
+
+ # Returns a JSON string representing +self+:
+ #
+ # require 'json/add/time'
+ # puts Time.now.to_json
+ #
+ # Output:
+ #
+ # {"json_class":"Time","s":1700931678,"n":980650786}
+ #
+ def to_json(*args)
+ as_json.to_json(*args)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/common.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/common.rb
new file mode 100644
index 0000000..fdcb860
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/common.rb
@@ -0,0 +1,1182 @@
+# frozen_string_literal: true
+
+require 'json/version'
+
+module JSON
+ autoload :GenericObject, 'json/generic_object'
+
+ module ParserOptions # :nodoc:
+ class << self
+ def prepare(opts)
+ if opts[:object_class] || opts[:array_class]
+ opts = opts.dup
+ on_load = opts[:on_load]
+
+ on_load = object_class_proc(opts[:object_class], on_load) if opts[:object_class]
+ on_load = array_class_proc(opts[:array_class], on_load) if opts[:array_class]
+ opts[:on_load] = on_load
+ end
+
+ if opts.fetch(:create_additions, false) != false
+ opts = create_additions_proc(opts)
+ end
+
+ opts
+ end
+
+ private
+
+ def object_class_proc(object_class, on_load)
+ ->(obj) do
+ if Hash === obj
+ object = object_class.new
+ obj.each { |k, v| object[k] = v }
+ obj = object
+ end
+ on_load.nil? ? obj : on_load.call(obj)
+ end
+ end
+
+ def array_class_proc(array_class, on_load)
+ ->(obj) do
+ if Array === obj
+ array = array_class.new
+ obj.each { |v| array << v }
+ obj = array
+ end
+ on_load.nil? ? obj : on_load.call(obj)
+ end
+ end
+
+ # TODO: extract :create_additions support to another gem for version 3.0
+ def create_additions_proc(opts)
+ if opts[:symbolize_names]
+ raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
+ end
+
+ opts = opts.dup
+ create_additions = opts.fetch(:create_additions, false)
+ on_load = opts[:on_load]
+ object_class = opts[:object_class] || Hash
+
+ opts[:on_load] = ->(object) do
+ case object
+ when String
+ opts[:match_string]&.each do |pattern, klass|
+ if match = pattern.match(object)
+ create_additions_warning if create_additions.nil?
+ object = klass.json_create(object)
+ break
+ end
+ end
+ when object_class
+ if opts[:create_additions] != false
+ if class_path = object[JSON.create_id]
+ klass = begin
+ Object.const_get(class_path)
+ rescue NameError => e
+ raise ArgumentError, "can't get const #{class_path}: #{e}"
+ end
+
+ if klass.respond_to?(:json_creatable?) ? klass.json_creatable? : klass.respond_to?(:json_create)
+ create_additions_warning if create_additions.nil?
+ object = klass.json_create(object)
+ end
+ end
+ end
+ end
+
+ on_load.nil? ? object : on_load.call(object)
+ end
+
+ opts
+ end
+
+ def create_additions_warning
+ JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
+ "and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
+ "pass `create_additions: true`"
+ end
+ end
+ end
+
+ class << self
+ def deprecation_warning(message, uplevel = 3) # :nodoc:
+ gem_root = File.expand_path("..", __dir__) + "/"
+ caller_locations(uplevel, 10).each do |frame|
+ if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
+ uplevel += 1
+ else
+ break
+ end
+ end
+
+ if RUBY_VERSION >= "3.0"
+ warn(message, uplevel: uplevel, category: :deprecated)
+ else
+ warn(message, uplevel: uplevel)
+ end
+ end
+
+ # :call-seq:
+ # JSON[object] -> new_array or new_string
+ #
+ # If +object+ is a \String,
+ # calls JSON.parse with +object+ and +opts+ (see method #parse):
+ # json = '[0, 1, null]'
+ # JSON[json]# => [0, 1, nil]
+ #
+ # Otherwise, calls JSON.generate with +object+ and +opts+ (see method #generate):
+ # ruby = [0, 1, nil]
+ # JSON[ruby] # => '[0,1,null]'
+ def [](object, opts = nil)
+ if object.is_a?(String)
+ return JSON.parse(object, opts)
+ elsif object.respond_to?(:to_str)
+ str = object.to_str
+ if str.is_a?(String)
+ return JSON.parse(str, opts)
+ end
+ end
+
+ JSON.generate(object, opts)
+ end
+
+ # Returns the JSON parser class that is used by JSON.
+ attr_reader :parser
+
+ # Set the JSON parser class _parser_ to be used by JSON.
+ def parser=(parser) # :nodoc:
+ @parser = parser
+ remove_const :Parser if const_defined?(:Parser, false)
+ const_set :Parser, parser
+ end
+
+ # Set the module _generator_ to be used by JSON.
+ def generator=(generator) # :nodoc:
+ old, $VERBOSE = $VERBOSE, nil
+
+ # The default proc used when the +sort_keys+ generation option is +true+.
+ # It returns a new hash with the entries sorted by their keys.
+ sort_keys_proc = ->(hash) { hash.sort.to_h }
+ if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
+ sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
+ end
+ generator::State.default_sort_keys_proc = sort_keys_proc
+
+ @generator = generator
+ if generator.const_defined?(:GeneratorMethods)
+ generator_methods = generator::GeneratorMethods
+ for const in generator_methods.constants
+ klass = const_get(const)
+ modul = generator_methods.const_get(const)
+ klass.class_eval do
+ instance_methods(false).each do |m|
+ m.to_s == 'to_json' and remove_method m
+ end
+ include modul
+ end
+ end
+ end
+ self.state = generator::State
+ const_set :State, state
+ ensure
+ $VERBOSE = old
+ end
+
+ # Returns the JSON generator module that is used by JSON.
+ attr_reader :generator
+
+ # Sets or Returns the JSON generator state class that is used by JSON.
+ attr_accessor :state
+
+ private
+
+ # Called from the extension when a hash has both string and symbol keys
+ def on_mixed_keys_hash(hash, do_raise)
+ set = {}
+ hash.each_key do |key|
+ key_str = key.to_s
+
+ if set[key_str]
+ message = "detected duplicate key #{key_str.inspect} in #{hash.inspect}"
+ if do_raise
+ raise GeneratorError, message
+ else
+ deprecation_warning("#{message}.\nThis will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`")
+ end
+ else
+ set[key_str] = true
+ end
+ end
+ end
+
+ def deprecated_singleton_attr_accessor(*attrs)
+ args = RUBY_VERSION >= "3.0" ? ", category: :deprecated" : ""
+ attrs.each do |attr|
+ singleton_class.class_eval <<~RUBY
+ def #{attr}
+ warn "JSON.#{attr} is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
+ @#{attr}
+ end
+
+ def #{attr}=(val)
+ warn "JSON.#{attr}= is deprecated and will be removed in json 3.0.0", uplevel: 1 #{args}
+ @#{attr} = val
+ end
+
+ def _#{attr}
+ @#{attr}
+ end
+ RUBY
+ end
+ end
+ end
+
+ # Sets create identifier, which is used to decide if the _json_create_
+ # hook of a class should be called; initial value is +json_class+:
+ # JSON.create_id # => 'json_class'
+ def self.create_id=(new_value)
+ Thread.current[:"JSON.create_id"] = new_value.dup.freeze
+ end
+
+ # Returns the current create identifier.
+ # See also JSON.create_id=.
+ def self.create_id
+ Thread.current[:"JSON.create_id"] || 'json_class'
+ end
+
+ NaN = Float::NAN
+
+ Infinity = Float::INFINITY
+
+ MinusInfinity = -Infinity
+
+ # The base exception for JSON errors.
+ class JSONError < StandardError; end
+
+ # This exception is raised if a parser error occurs.
+ class ParserError < JSONError
+ attr_reader :line, :column
+ end
+
+ # This exception is raised if the nesting of parsed data structures is too
+ # deep.
+ class NestingError < ParserError; end
+
+ # This exception is raised if a generator or unparser error occurs.
+ class GeneratorError < JSONError
+ attr_reader :invalid_object
+
+ def initialize(message, invalid_object = nil)
+ super(message)
+ @invalid_object = invalid_object
+ end
+
+ def detailed_message(...)
+ # Exception#detailed_message doesn't exist until Ruby 3.2
+ super_message = defined?(super) ? super : message
+
+ if @invalid_object.nil?
+ super_message
+ else
+ "#{super_message}\nInvalid object: #{@invalid_object.inspect}"
+ end
+ end
+ end
+
+ # Fragment of JSON document that is to be included as is:
+ # fragment = JSON::Fragment.new("[1, 2, 3]")
+ # JSON.generate({ count: 3, items: fragments })
+ #
+ # This allows to easily assemble multiple JSON fragments that have
+ # been persisted somewhere without having to parse them nor resorting
+ # to string interpolation.
+ #
+ # Note: no validation is performed on the provided string. It is the
+ # responsibility of the caller to ensure the string contains valid JSON.
+ Fragment = Struct.new(:json) do
+ def initialize(json)
+ unless string = String.try_convert(json)
+ raise TypeError, " no implicit conversion of #{json.class} into String"
+ end
+
+ super(string)
+ end
+
+ def to_json(state = nil, *)
+ json
+ end
+ end
+
+ module_function
+
+ # :call-seq:
+ # JSON.parse(source, opts) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # Argument +source+ contains the \String to be parsed.
+ #
+ # Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ #
+ # ---
+ #
+ # When +source+ is a \JSON array, returns a Ruby \Array:
+ # source = '["foo", 1.0, true, false, null]'
+ # ruby = JSON.parse(source)
+ # ruby # => ["foo", 1.0, true, false, nil]
+ # ruby.class # => Array
+ #
+ # When +source+ is a \JSON object, returns a Ruby \Hash:
+ # source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}'
+ # ruby = JSON.parse(source)
+ # ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
+ # ruby.class # => Hash
+ #
+ # For examples of parsing for all \JSON data types, see
+ # {Parsing \JSON}[#module-JSON-label-Parsing+JSON].
+ #
+ # Parses nested JSON objects:
+ # source = <<~JSON
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # JSON
+ # ruby = JSON.parse(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # Raises an exception if +source+ is not valid JSON:
+ # # Raises JSON::ParserError (783: unexpected token at ''):
+ # JSON.parse('')
+ #
+ def parse(source, opts = nil)
+ opts = ParserOptions.prepare(opts) unless opts.nil?
+ Parser.parse(source, opts)
+ end
+
+ PARSE_L_OPTIONS = {
+ max_nesting: false,
+ allow_nan: true,
+ }.freeze
+ private_constant :PARSE_L_OPTIONS
+
+ # :call-seq:
+ # JSON.parse!(source, opts) -> object
+ #
+ # Calls
+ # parse(source, opts)
+ # with +source+ and possibly modified +opts+.
+ #
+ # Differences from JSON.parse:
+ # - Option +max_nesting+, if not provided, defaults to +false+,
+ # which disables checking for nesting depth.
+ # - Option +allow_nan+, if not provided, defaults to +true+.
+ def parse!(source, opts = nil)
+ if opts.nil?
+ parse(source, PARSE_L_OPTIONS)
+ else
+ parse(source, PARSE_L_OPTIONS.merge(opts))
+ end
+ end
+
+ # :call-seq:
+ # JSON.load_file(path, opts={}) -> object
+ #
+ # Calls:
+ # parse(File.read(path), opts)
+ #
+ # See method #parse.
+ def load_file(filespec, opts = nil)
+ parse(File.read(filespec, encoding: Encoding::UTF_8), opts)
+ end
+
+ # :call-seq:
+ # JSON.load_file!(path, opts = {})
+ #
+ # Calls:
+ # JSON.parse!(File.read(path, opts))
+ #
+ # See method #parse!
+ def load_file!(filespec, opts = nil)
+ parse!(File.read(filespec, encoding: Encoding::UTF_8), opts)
+ end
+
+ # :call-seq:
+ # JSON.generate(obj, opts = nil) -> new_string
+ #
+ # Returns a \String containing the generated \JSON data.
+ #
+ # See also JSON.pretty_generate.
+ #
+ # Argument +obj+ is the Ruby object to be converted to \JSON.
+ #
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
+ # See {Generating Options}[#module-JSON-label-Generating+Options].
+ #
+ # ---
+ #
+ # When +obj+ is an \Array, returns a \String containing a \JSON array:
+ # obj = ["foo", 1.0, true, false, nil]
+ # json = JSON.generate(obj)
+ # json # => '["foo",1.0,true,false,null]'
+ #
+ # When +obj+ is a \Hash, returns a \String containing a \JSON object:
+ # obj = {foo: 0, bar: 's', baz: :bat}
+ # json = JSON.generate(obj)
+ # json # => '{"foo":0,"bar":"s","baz":"bat"}'
+ #
+ # For examples of generating from other Ruby objects, see
+ # {Generating \JSON from Other Objects}[#module-JSON-label-Generating+JSON+from+Other+Objects].
+ #
+ # ---
+ #
+ # Raises an exception if any formatting option is not a \String.
+ #
+ # Raises an exception if +obj+ contains circular references:
+ # a = []; b = []; a.push(b); b.push(a)
+ # # Raises JSON::NestingError (nesting of 100 is too deep):
+ # JSON.generate(a)
+ #
+ def generate(obj, opts = nil)
+ if State === opts
+ opts.generate(obj)
+ else
+ State.generate(obj, opts, nil)
+ end
+ end
+
+ # :call-seq:
+ # JSON.fast_generate(obj, opts) -> new_string
+ #
+ # Arguments +obj+ and +opts+ here are the same as
+ # arguments +obj+ and +opts+ in JSON.generate.
+ #
+ # By default, generates \JSON data without checking
+ # for circular references in +obj+ (option +max_nesting+ set to +false+, disabled).
+ #
+ # Raises an exception if +obj+ contains circular references:
+ # a = []; b = []; a.push(b); b.push(a)
+ # # Raises SystemStackError (stack level too deep):
+ # JSON.fast_generate(a)
+ def fast_generate(obj, opts = nil)
+ if RUBY_VERSION >= "3.0"
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
+ else
+ warn "JSON.fast_generate is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
+ end
+ generate(obj, opts)
+ end
+
+ PRETTY_GENERATE_OPTIONS = {
+ indent: ' ',
+ space: ' ',
+ object_nl: "\n",
+ array_nl: "\n",
+ }.freeze
+ private_constant :PRETTY_GENERATE_OPTIONS
+
+ # :call-seq:
+ # JSON.pretty_generate(obj, opts = nil) -> new_string
+ #
+ # Arguments +obj+ and +opts+ here are the same as
+ # arguments +obj+ and +opts+ in JSON.generate.
+ #
+ # Default options are:
+ # {
+ # indent: ' ', # Two spaces
+ # space: ' ', # One space
+ # array_nl: "\n", # Newline
+ # object_nl: "\n" # Newline
+ # }
+ #
+ # Example:
+ # obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
+ # json = JSON.pretty_generate(obj)
+ # puts json
+ # Output:
+ # {
+ # "foo": [
+ # "bar",
+ # "baz"
+ # ],
+ # "bat": {
+ # "bam": 0,
+ # "bad": 1
+ # }
+ # }
+ #
+ def pretty_generate(obj, opts = nil)
+ return opts.generate(obj) if State === opts
+
+ options = PRETTY_GENERATE_OPTIONS
+
+ if opts
+ unless opts.is_a?(Hash)
+ if opts.respond_to? :to_hash
+ opts = opts.to_hash
+ elsif opts.respond_to? :to_h
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ end
+ options = options.merge(opts)
+ end
+
+ State.generate(obj, options, nil)
+ end
+
+ # Sets or returns default options for the JSON.unsafe_load method.
+ # Initially:
+ # opts = JSON.load_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
+ deprecated_singleton_attr_accessor :unsafe_load_default_options
+
+ @unsafe_load_default_options = {
+ :max_nesting => false,
+ :allow_nan => true,
+ :allow_blank => true,
+ :create_additions => true,
+ }
+
+ # Sets or returns default options for the JSON.load method.
+ # Initially:
+ # opts = JSON.load_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true, :allow_blank=>true, :create_additions=>true}
+ deprecated_singleton_attr_accessor :load_default_options
+
+ @load_default_options = {
+ :allow_nan => true,
+ :allow_blank => true,
+ :create_additions => nil,
+ }
+ # :call-seq:
+ # JSON.unsafe_load(source, options = {}) -> object
+ # JSON.unsafe_load(source, proc = nil, options = {}) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # BEWARE: This method is meant to serialise data from trusted user input,
+ # like from your own database server or clients under your control, it could
+ # be dangerous to allow untrusted users to pass JSON sources into it.
+ #
+ # - Argument +source+ must be, or be convertible to, a \String:
+ # - If +source+ responds to instance method +to_str+,
+ # <tt>source.to_str</tt> becomes the source.
+ # - If +source+ responds to instance method +to_io+,
+ # <tt>source.to_io.read</tt> becomes the source.
+ # - If +source+ responds to instance method +read+,
+ # <tt>source.read</tt> becomes the source.
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
+ # - Option +allow_blank+ specifies a truthy value.
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
+ # - Otherwise, +source+ remains the source.
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
+ # It will be called recursively with each result (depth-first order).
+ # See details below.
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ # The default options can be changed via method JSON.unsafe_load_default_options=.
+ #
+ # ---
+ #
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
+ # <tt>parse(source, opts)</tt>; see #parse.
+ #
+ # Source for following examples:
+ # source = <<~JSON
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # JSON
+ #
+ # Load a \String:
+ # ruby = JSON.unsafe_load(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load an \IO object:
+ # require 'stringio'
+ # object = JSON.unsafe_load(StringIO.new(source))
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load a \File object:
+ # path = 't.json'
+ # File.write(path, source)
+ # File.open(path) do |file|
+ # JSON.unsafe_load(file)
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # When +proc+ is given:
+ # - Modifies +source+ as above.
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
+ # - Recursively calls <tt>proc(result)</tt>.
+ # - Returns the final result.
+ #
+ # Example:
+ # require 'json'
+ #
+ # # Some classes for the example.
+ # class Base
+ # def initialize(attributes)
+ # @attributes = attributes
+ # end
+ # end
+ # class User < Base; end
+ # class Account < Base; end
+ # class Admin < Base; end
+ # # The JSON source.
+ # json = <<-EOF
+ # {
+ # "users": [
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
+ # {"type": "User", "username": "john", "email": "john@example.com"}
+ # ],
+ # "accounts": [
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
+ # ],
+ # "admins": {"type": "Admin", "password": "0wn3d"}
+ # }
+ # EOF
+ # # Deserializer method.
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
+ # type = obj.is_a?(Hash) && obj["type"]
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
+ # end
+ # # Call to JSON.unsafe_load
+ # ruby = JSON.unsafe_load(json, proc {|obj|
+ # case obj
+ # when Hash
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
+ # when Array
+ # obj.map! {|v| deserialize_obj v }
+ # end
+ # obj
+ # })
+ # pp ruby
+ # Output:
+ # {"users"=>
+ # [#<User:0x00000000064c4c98
+ # @attributes=
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
+ # #<User:0x00000000064c4bd0
+ # @attributes=
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
+ # "accounts"=>
+ # [{"account"=>
+ # #<Account:0x00000000064c4928
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
+ # {"account"=>
+ # #<Account:0x00000000064c4680
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
+ # "admins"=>
+ # #<Admin:0x00000000064c41f8
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
+ #
+ def unsafe_load(source, proc = nil, options = nil)
+ opts = if options.nil?
+ if proc && proc.is_a?(Hash)
+ options, proc = proc, nil
+ options
+ else
+ _unsafe_load_default_options
+ end
+ else
+ _unsafe_load_default_options.merge(options)
+ end
+
+ unless source.is_a?(String)
+ if source.respond_to? :to_str
+ source = source.to_str
+ elsif source.respond_to? :to_io
+ source = source.to_io.read
+ elsif source.respond_to?(:read)
+ source = source.read
+ end
+ end
+
+ if opts[:allow_blank] && (source.nil? || source.empty?)
+ source = 'null'
+ end
+
+ if proc
+ opts = opts.dup
+ opts[:on_load] = proc.to_proc
+ end
+
+ parse(source, opts)
+ end
+
+ # :call-seq:
+ # JSON.load(source, options = {}) -> object
+ # JSON.load(source, proc = nil, options = {}) -> object
+ #
+ # Returns the Ruby objects created by parsing the given +source+.
+ #
+ # BEWARE: This method is meant to serialise data from trusted user input,
+ # like from your own database server or clients under your control, it could
+ # be dangerous to allow untrusted users to pass JSON sources into it.
+ # If you must use it, use JSON.unsafe_load instead to make it clear.
+ #
+ # Since JSON version 2.8.0, `load` emits a deprecation warning when a
+ # non native type is deserialized, without `create_additions` being explicitly
+ # enabled, and in JSON version 3.0, `load` will have `create_additions` disabled
+ # by default.
+ #
+ # - Argument +source+ must be, or be convertible to, a \String:
+ # - If +source+ responds to instance method +to_str+,
+ # <tt>source.to_str</tt> becomes the source.
+ # - If +source+ responds to instance method +to_io+,
+ # <tt>source.to_io.read</tt> becomes the source.
+ # - If +source+ responds to instance method +read+,
+ # <tt>source.read</tt> becomes the source.
+ # - If both of the following are true, source becomes the \String <tt>'null'</tt>:
+ # - Option +allow_blank+ specifies a truthy value.
+ # - The source, as defined above, is +nil+ or the empty \String <tt>''</tt>.
+ # - Otherwise, +source+ remains the source.
+ # - Argument +proc+, if given, must be a \Proc that accepts one argument.
+ # It will be called recursively with each result (depth-first order).
+ # See details below.
+ # - Argument +opts+, if given, contains a \Hash of options for the parsing.
+ # See {Parsing Options}[#module-JSON-label-Parsing+Options].
+ # The default options can be changed via method JSON.load_default_options=.
+ #
+ # ---
+ #
+ # When no +proc+ is given, modifies +source+ as above and returns the result of
+ # <tt>parse(source, opts)</tt>; see #parse.
+ #
+ # Source for following examples:
+ # source = <<~JSON
+ # {
+ # "name": "Dave",
+ # "age" :40,
+ # "hats": [
+ # "Cattleman's",
+ # "Panama",
+ # "Tophat"
+ # ]
+ # }
+ # JSON
+ #
+ # Load a \String:
+ # ruby = JSON.load(source)
+ # ruby # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load an \IO object:
+ # require 'stringio'
+ # object = JSON.load(StringIO.new(source))
+ # object # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # Load a \File object:
+ # path = 't.json'
+ # File.write(path, source)
+ # File.open(path) do |file|
+ # JSON.load(file)
+ # end # => {"name"=>"Dave", "age"=>40, "hats"=>["Cattleman's", "Panama", "Tophat"]}
+ #
+ # ---
+ #
+ # When +proc+ is given:
+ # - Modifies +source+ as above.
+ # - Gets the +result+ from calling <tt>parse(source, opts)</tt>.
+ # - Recursively calls <tt>proc(result)</tt>.
+ # - Returns the final result.
+ #
+ # Example:
+ # require 'json'
+ #
+ # # Some classes for the example.
+ # class Base
+ # def initialize(attributes)
+ # @attributes = attributes
+ # end
+ # end
+ # class User < Base; end
+ # class Account < Base; end
+ # class Admin < Base; end
+ # # The JSON source.
+ # json = <<-EOF
+ # {
+ # "users": [
+ # {"type": "User", "username": "jane", "email": "jane@example.com"},
+ # {"type": "User", "username": "john", "email": "john@example.com"}
+ # ],
+ # "accounts": [
+ # {"account": {"type": "Account", "paid": true, "account_id": "1234"}},
+ # {"account": {"type": "Account", "paid": false, "account_id": "1235"}}
+ # ],
+ # "admins": {"type": "Admin", "password": "0wn3d"}
+ # }
+ # EOF
+ # # Deserializer method.
+ # def deserialize_obj(obj, safe_types = %w(User Account Admin))
+ # type = obj.is_a?(Hash) && obj["type"]
+ # safe_types.include?(type) ? Object.const_get(type).new(obj) : obj
+ # end
+ # # Call to JSON.load
+ # ruby = JSON.load(json, proc {|obj|
+ # case obj
+ # when Hash
+ # obj.each {|k, v| obj[k] = deserialize_obj v }
+ # when Array
+ # obj.map! {|v| deserialize_obj v }
+ # end
+ # obj
+ # })
+ # pp ruby
+ # Output:
+ # {"users"=>
+ # [#<User:0x00000000064c4c98
+ # @attributes=
+ # {"type"=>"User", "username"=>"jane", "email"=>"jane@example.com"}>,
+ # #<User:0x00000000064c4bd0
+ # @attributes=
+ # {"type"=>"User", "username"=>"john", "email"=>"john@example.com"}>],
+ # "accounts"=>
+ # [{"account"=>
+ # #<Account:0x00000000064c4928
+ # @attributes={"type"=>"Account", "paid"=>true, "account_id"=>"1234"}>},
+ # {"account"=>
+ # #<Account:0x00000000064c4680
+ # @attributes={"type"=>"Account", "paid"=>false, "account_id"=>"1235"}>}],
+ # "admins"=>
+ # #<Admin:0x00000000064c41f8
+ # @attributes={"type"=>"Admin", "password"=>"0wn3d"}>}
+ #
+ def load(source, proc = nil, options = nil)
+ if proc && options.nil? && proc.is_a?(Hash)
+ options = proc
+ proc = nil
+ end
+
+ opts = if options.nil?
+ if proc && proc.is_a?(Hash)
+ options, proc = proc, nil
+ options
+ else
+ _load_default_options
+ end
+ else
+ _load_default_options.merge(options)
+ end
+
+ unless source.is_a?(String)
+ if source.respond_to? :to_str
+ source = source.to_str
+ elsif source.respond_to? :to_io
+ source = source.to_io.read
+ elsif source.respond_to?(:read)
+ source = source.read
+ end
+ end
+
+ if opts[:allow_blank] && (source.nil? || (String === source && source.empty?))
+ source = 'null'
+ end
+
+ if proc
+ opts = opts.dup
+ opts[:on_load] = proc.to_proc
+ end
+
+ parse(source, opts)
+ end
+
+ # Sets or returns the default options for the JSON.dump method.
+ # Initially:
+ # opts = JSON.dump_default_options
+ # opts # => {:max_nesting=>false, :allow_nan=>true}
+ deprecated_singleton_attr_accessor :dump_default_options
+ @dump_default_options = {
+ :max_nesting => false,
+ :allow_nan => true,
+ }
+
+ # :call-seq:
+ # JSON.dump(obj, io = nil, limit = nil)
+ #
+ # Dumps +obj+ as a \JSON string, i.e. calls generate on the object and returns the result.
+ #
+ # The default options can be changed via method JSON.dump_default_options.
+ #
+ # - Argument +io+, if given, should respond to method +write+;
+ # the \JSON \String is written to +io+, and +io+ is returned.
+ # If +io+ is not given, the \JSON \String is returned.
+ # - Argument +limit+, if given, is passed to JSON.generate as option +max_nesting+.
+ #
+ # ---
+ #
+ # When argument +io+ is not given, returns the \JSON \String generated from +obj+:
+ # obj = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad}
+ # json = JSON.dump(obj)
+ # json # => "{\"foo\":[0,1],\"bar\":{\"baz\":2,\"bat\":3},\"bam\":\"bad\"}"
+ #
+ # When argument +io+ is given, writes the \JSON \String to +io+ and returns +io+:
+ # path = 't.json'
+ # File.open(path, 'w') do |file|
+ # JSON.dump(obj, file)
+ # end # => #<File:t.json (closed)>
+ # puts File.read(path)
+ # Output:
+ # {"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}
+ def dump(obj, anIO = nil, limit = nil, kwargs = nil)
+ if kwargs.nil?
+ if limit.nil?
+ if anIO.is_a?(Hash)
+ kwargs = anIO
+ anIO = nil
+ end
+ elsif limit.is_a?(Hash)
+ kwargs = limit
+ limit = nil
+ end
+ end
+
+ unless anIO.nil?
+ if anIO.respond_to?(:to_io)
+ anIO = anIO.to_io
+ elsif limit.nil? && !anIO.respond_to?(:write)
+ anIO, limit = nil, anIO
+ end
+ end
+
+ opts = JSON._dump_default_options
+ opts = opts.merge(:max_nesting => limit) if limit
+ opts = opts.merge(kwargs) if kwargs
+
+ begin
+ State.generate(obj, opts, anIO)
+ rescue JSON::NestingError
+ raise ArgumentError, "exceed depth limit"
+ end
+ end
+
+ # :stopdoc:
+ # All these were meant to be deprecated circa 2009, but were just set as undocumented
+ # so usage still exist in the wild.
+ def unparse(...)
+ if RUBY_VERSION >= "3.0"
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
+ else
+ warn "JSON.unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
+ end
+ generate(...)
+ end
+ module_function :unparse
+
+ def fast_unparse(...)
+ if RUBY_VERSION >= "3.0"
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1, category: :deprecated
+ else
+ warn "JSON.fast_unparse is deprecated and will be removed in json 3.0.0, just use JSON.generate", uplevel: 1
+ end
+ generate(...)
+ end
+ module_function :fast_unparse
+
+ def pretty_unparse(...)
+ if RUBY_VERSION >= "3.0"
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
+ else
+ warn "JSON.pretty_unparse is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
+ end
+ pretty_generate(...)
+ end
+ module_function :fast_unparse
+
+ def restore(...)
+ if RUBY_VERSION >= "3.0"
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1, category: :deprecated
+ else
+ warn "JSON.restore is deprecated and will be removed in json 3.0.0, just use JSON.load", uplevel: 1
+ end
+ load(...)
+ end
+ module_function :restore
+
+ class << self
+ private
+
+ def const_missing(const_name)
+ case const_name
+ when :PRETTY_STATE_PROTOTYPE
+ if RUBY_VERSION >= "3.0"
+ warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1, category: :deprecated
+ else
+ warn "JSON::PRETTY_STATE_PROTOTYPE is deprecated and will be removed in json 3.0.0, just use JSON.pretty_generate", uplevel: 1
+ end
+ state.new(PRETTY_GENERATE_OPTIONS)
+ else
+ super
+ end
+ end
+ end
+ # :startdoc:
+
+ # JSON::Coder holds a parser and generator configuration.
+ #
+ # module MyApp
+ # JSONC_CODER = JSON::Coder.new(
+ # allow_trailing_comma: true
+ # )
+ # end
+ #
+ # MyApp::JSONC_CODER.load(document)
+ #
+ class Coder
+ # :call-seq:
+ # JSON.new(options = nil, &block)
+ #
+ # Argument +options+, if given, contains a \Hash of options for both parsing and generating.
+ # See {Parsing Options}[rdoc-ref:JSON@Parsing+Options],
+ # and {Generating Options}[rdoc-ref:JSON@Generating+Options].
+ #
+ # For generation, the <tt>strict: true</tt> option is always set. When a Ruby object with no native \JSON counterpart is
+ # encountered, the block provided to the initialize method is invoked, and must return a Ruby object that has a native
+ # \JSON counterpart:
+ #
+ # module MyApp
+ # API_JSON_CODER = JSON::Coder.new do |object|
+ # case object
+ # when Time
+ # object.iso8601(3)
+ # else
+ # object # Unknown type, will raise
+ # end
+ # end
+ # end
+ #
+ # puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
+ #
+ def initialize(options = nil, &as_json)
+ if options.nil?
+ options = { strict: true }
+ else
+ options = options.dup
+ options[:strict] = true
+ end
+ options[:as_json] = as_json if as_json
+
+ @state = State.new(options).freeze
+ @parser_config = Ext::Parser::Config.new(ParserOptions.prepare(options)).freeze
+ end
+
+ # call-seq:
+ # dump(object) -> String
+ # dump(object, io) -> io
+ #
+ # Serialize the given object into a \JSON document.
+ def dump(object, io = nil)
+ @state.generate(object, io)
+ end
+ alias_method :generate, :dump
+
+ # call-seq:
+ # load(string) -> Object
+ #
+ # Parse the given \JSON document and return an equivalent Ruby object.
+ def load(source)
+ @parser_config.parse(source)
+ end
+ alias_method :parse, :load
+
+ # call-seq:
+ # load(path) -> Object
+ #
+ # Parse the given \JSON document and return an equivalent Ruby object.
+ def load_file(path)
+ load(File.read(path, encoding: Encoding::UTF_8))
+ end
+ end
+
+ module GeneratorMethods
+ # call-seq: to_json(*)
+ #
+ # Converts this object into a JSON string.
+ # If this object doesn't directly maps to a JSON native type,
+ # first convert it to a string (calling #to_s), then converts
+ # it to a JSON string, and returns the result.
+ # This is a fallback, if no special method #to_json was defined for some object.
+ def to_json(state = nil, *)
+ obj = case self
+ when nil, false, true, Integer, Float, Array, Hash
+ self
+ else
+ "#{self}"
+ end
+
+ if state.nil?
+ JSON::State._generate_no_fallback(obj, nil, nil)
+ else
+ JSON::State.from_state(state)._generate_no_fallback(obj)
+ end
+ end
+ end
+end
+
+module ::Kernel
+ private
+
+ # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
+ # one line.
+ def j(*objs)
+ if RUBY_VERSION >= "3.0"
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
+ else
+ warn "Kernel#j is deprecated and will be removed in json 3.0.0", uplevel: 1
+ end
+
+ objs.each do |obj|
+ puts JSON.generate(obj, :allow_nan => true, :max_nesting => false)
+ end
+ nil
+ end
+
+ # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
+ # indentation and over many lines.
+ def jj(*objs)
+ if RUBY_VERSION >= "3.0"
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1, category: :deprecated
+ else
+ warn "Kernel#jj is deprecated and will be removed in json 3.0.0", uplevel: 1
+ end
+
+ objs.each do |obj|
+ puts JSON.pretty_generate(obj, :allow_nan => true, :max_nesting => false)
+ end
+ nil
+ end
+
+ # If _object_ is string-like, parse the string and return the parsed result as
+ # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
+ # structure object and return it.
+ #
+ # The _opts_ argument is passed through to generate/parse respectively. See
+ # generate and parse for their documentation.
+ def JSON(object, opts = nil)
+ JSON[object, opts]
+ end
+end
+
+class Object
+ include JSON::GeneratorMethods
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext.rb
new file mode 100644
index 0000000..28500f1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'json/common'
+
+module JSON
+ # This module holds all the modules/classes that implement JSON's
+ # functionality as C extensions.
+ module Ext
+ class Parser
+ class << self
+ def parse(...)
+ new(...).parse
+ end
+ alias_method :parse, :parse # Allow redefinition by extensions
+ end
+
+ def initialize(source, opts = nil)
+ @source = source
+ @config = Config.new(opts)
+ end
+
+ def source
+ @source.dup
+ end
+
+ def parse
+ @config.parse(@source)
+ end
+ end
+
+ require 'json/ext/parser'
+ Ext::Parser::Config = Ext::ParserConfig
+ JSON.parser = Ext::Parser
+
+ if RUBY_ENGINE == 'truffleruby'
+ require 'json/truffle_ruby/generator'
+ JSON.generator = JSON::TruffleRuby::Generator
+ else
+ require 'json/ext/generator'
+ JSON.generator = Generator
+ end
+ end
+
+ if defined?(ResumableParser) # Not yet available on JRuby
+ class ResumableParser
+ # Returns whether the parser is entirely done: no unconsumed bytes in
+ # the buffer, no document under construction and no parsed value
+ # awaiting retrieval.
+ #
+ # The main use case is detecting a truncated stream once the input is
+ # exhausted:
+ #
+ # loop do
+ # begin
+ # parser << socket.readpartial(4096)
+ # rescue EOFError
+ # break
+ # end
+ # while parser.parse
+ # process(parser.value)
+ # end
+ # end
+ # warn "stream was truncated" unless parser.empty?
+ def empty?
+ eos? && !partial_value? && !value?
+ end
+ end
+ end
+
+ JSON_LOADED = true unless defined?(JSON::JSON_LOADED)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator.bundle b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator.bundle
new file mode 100755
index 0000000..96b7040
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator.bundle
Binary files differ
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator/state.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator/state.rb
new file mode 100644
index 0000000..3c1d2fb
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/generator/state.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+module JSON
+ module Ext
+ module Generator
+ class State
+ # call-seq: new(opts = {})
+ #
+ # Instantiates a new State object, configured by _opts_.
+ #
+ # Argument +opts+, if given, contains a \Hash of options for the generation.
+ # See {Generating Options}[rdoc-ref:JSON@Generating+Options].
+ def initialize(opts = nil)
+ if opts && !opts.empty?
+ configure(opts)
+ end
+ end
+
+ # call-seq: configure(opts)
+ #
+ # Configure this State instance with the Hash _opts_, and return
+ # itself.
+ def configure(opts)
+ unless opts.is_a?(Hash)
+ if opts.respond_to?(:to_hash)
+ opts = opts.to_hash
+ elsif opts.respond_to?(:to_h)
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+ end
+ _configure(opts)
+ end
+
+ alias_method :merge, :configure
+
+ # call-seq: to_h
+ #
+ # Returns the configuration instance variables as a hash, that can be
+ # passed to the configure method.
+ def to_h
+ result = {
+ indent: indent,
+ space: space,
+ space_before: space_before,
+ object_nl: object_nl,
+ array_nl: array_nl,
+ as_json: as_json,
+ allow_nan: allow_nan?,
+ ascii_only: ascii_only?,
+ max_nesting: max_nesting,
+ script_safe: script_safe?,
+ strict: strict?,
+ depth: depth,
+ buffer_initial_length: buffer_initial_length,
+ sort_keys: sort_keys
+ }
+
+ allow_duplicate_key = allow_duplicate_key?
+ unless allow_duplicate_key.nil?
+ result[:allow_duplicate_key] = allow_duplicate_key
+ end
+
+ instance_variables.each do |iv|
+ iv = iv.to_s[1..-1]
+ result[iv.to_sym] = self[iv]
+ end
+
+ result
+ end
+
+ alias_method :to_hash, :to_h
+
+ # call-seq: [](name)
+ #
+ # Returns the value returned by method +name+.
+ def [](name)
+ ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
+
+ if respond_to?(name)
+ __send__(name)
+ else
+ instance_variable_get("@#{name}") if
+ instance_variables.include?("@#{name}".to_sym) # avoid warning
+ end
+ end
+
+ # call-seq: []=(name, value)
+ #
+ # Sets the attribute name to value.
+ def []=(name, value)
+ ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
+
+ if respond_to?(name_writer = "#{name}=")
+ __send__ name_writer, value
+ else
+ instance_variable_set "@#{name}", value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/parser.bundle b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/parser.bundle
new file mode 100755
index 0000000..432f107
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/ext/parser.bundle
Binary files differ
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/generic_object.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/generic_object.rb
new file mode 100644
index 0000000..5c8ace3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/generic_object.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+begin
+ require 'ostruct'
+rescue LoadError
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
+end
+
+module JSON
+ class GenericObject < OpenStruct
+ class << self
+ alias [] new
+
+ def json_creatable?
+ @json_creatable
+ end
+
+ attr_writer :json_creatable
+
+ def json_create(data)
+ data = data.dup
+ data.delete JSON.create_id
+ self[data]
+ end
+
+ def from_hash(object)
+ case
+ when object.respond_to?(:to_hash)
+ result = new
+ object.to_hash.each do |key, value|
+ result[key] = from_hash(value)
+ end
+ result
+ when object.respond_to?(:to_ary)
+ object.to_ary.map { |a| from_hash(a) }
+ else
+ object
+ end
+ end
+
+ def load(source, proc = nil, opts = {})
+ result = ::JSON.load(source, proc, opts.merge(:object_class => self))
+ result.nil? ? new : result
+ end
+
+ def dump(obj, *args)
+ ::JSON.dump(obj, *args)
+ end
+ end
+ self.json_creatable = false
+
+ def to_hash
+ table
+ end
+
+ def |(other)
+ self.class[other.to_hash.merge(to_hash)]
+ end
+
+ def as_json(*)
+ { JSON.create_id => self.class.name }.merge to_hash
+ end
+
+ def to_json(*a)
+ as_json.to_json(*a)
+ end
+ end if defined?(::OpenStruct)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/truffle_ruby/generator.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/truffle_ruby/generator.rb
new file mode 100644
index 0000000..ee6186f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/truffle_ruby/generator.rb
@@ -0,0 +1,790 @@
+# frozen_string_literal: true
+module JSON
+ module TruffleRuby
+ module Generator
+ MAP = {
+ "\x0" => '\u0000',
+ "\x1" => '\u0001',
+ "\x2" => '\u0002',
+ "\x3" => '\u0003',
+ "\x4" => '\u0004',
+ "\x5" => '\u0005',
+ "\x6" => '\u0006',
+ "\x7" => '\u0007',
+ "\b" => '\b',
+ "\t" => '\t',
+ "\n" => '\n',
+ "\xb" => '\u000b',
+ "\f" => '\f',
+ "\r" => '\r',
+ "\xe" => '\u000e',
+ "\xf" => '\u000f',
+ "\x10" => '\u0010',
+ "\x11" => '\u0011',
+ "\x12" => '\u0012',
+ "\x13" => '\u0013',
+ "\x14" => '\u0014',
+ "\x15" => '\u0015',
+ "\x16" => '\u0016',
+ "\x17" => '\u0017',
+ "\x18" => '\u0018',
+ "\x19" => '\u0019',
+ "\x1a" => '\u001a',
+ "\x1b" => '\u001b',
+ "\x1c" => '\u001c',
+ "\x1d" => '\u001d',
+ "\x1e" => '\u001e',
+ "\x1f" => '\u001f',
+ '"' => '\"',
+ '\\' => '\\\\',
+ }.freeze # :nodoc:
+
+ SCRIPT_SAFE_MAP = MAP.merge(
+ '/' => '\\/',
+ "\u2028" => '\u2028',
+ "\u2029" => '\u2029',
+ ).freeze
+
+ SCRIPT_SAFE_ESCAPE_PATTERN = /[\/"\\\x0-\x1f\u2028-\u2029]/
+
+ def self.native_type?(value) # :nodoc:
+ (false == value || true == value || nil == value || String === value || Symbol === value || Array === value || Hash === value || Integer === value || Float === value || Fragment === value)
+ end
+
+ def self.native_key?(key) # :nodoc:
+ (Symbol === key || String === key)
+ end
+
+ def self.valid_encoding?(string) # :nodoc:
+ return false unless string.encoding == ::Encoding::UTF_8 || string.encoding == ::Encoding::US_ASCII
+ string.is_a?(Symbol) || string.valid_encoding?
+ end
+
+ # Convert a UTF8 encoded Ruby string _string_ to a JSON string, encoded with
+ # UTF16 big endian characters as \u????, and return it.
+ def self.utf8_to_json(string, script_safe = false) # :nodoc:
+ if script_safe
+ if SCRIPT_SAFE_ESCAPE_PATTERN.match?(string)
+ string.gsub(SCRIPT_SAFE_ESCAPE_PATTERN, SCRIPT_SAFE_MAP)
+ else
+ string
+ end
+ else
+ if /["\\\x0-\x1f]/.match?(string)
+ string.gsub(/["\\\x0-\x1f]/, MAP)
+ else
+ string
+ end
+ end
+ end
+
+ def self.utf8_to_json_ascii(original_string, script_safe = false) # :nodoc:
+ string = original_string.b
+ map = script_safe ? SCRIPT_SAFE_MAP : MAP
+ string.gsub!(/[\/"\\\x0-\x1f]/n) { map[$&] || $& }
+ string.gsub!(/(
+ (?:
+ [\xc2-\xdf][\x80-\xbf] |
+ [\xe0-\xef][\x80-\xbf]{2} |
+ [\xf0-\xf4][\x80-\xbf]{3}
+ )+ |
+ [\x80-\xc1\xf5-\xff] # invalid
+ )/nx) { |c|
+ c.size == 1 and raise GeneratorError.new("invalid utf8 byte: '#{c}'", original_string)
+ s = c.encode(::Encoding::UTF_16BE, ::Encoding::UTF_8).unpack('H*')[0]
+ s.force_encoding(::Encoding::BINARY)
+ s.gsub!(/.{4}/n, '\\\\u\&')
+ s.force_encoding(::Encoding::UTF_8)
+ }
+ string.force_encoding(::Encoding::UTF_8)
+ string
+ rescue => e
+ raise GeneratorError.new(e.message, original_string)
+ end
+
+ def self.valid_utf8?(string)
+ encoding = string.encoding
+ (encoding == Encoding::UTF_8 || encoding == Encoding::ASCII) &&
+ string.valid_encoding?
+ end
+
+ # This class is used to create State instances, that are use to hold data
+ # while generating a JSON text from a Ruby data structure.
+ class State
+ singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
+
+ def self.generate(obj, opts = nil, io = nil)
+ new(opts).generate(obj, io)
+ end
+
+ # Creates a State object from _opts_, which ought to be Hash to create
+ # a new State instance configured by _opts_, something else to create
+ # an unconfigured instance. If _opts_ is a State object, it is just
+ # returned.
+ def self.from_state(opts)
+ if opts
+ case
+ when self === opts
+ return opts
+ when opts.respond_to?(:to_hash)
+ return new(opts.to_hash)
+ when opts.respond_to?(:to_h)
+ return new(opts.to_h)
+ end
+ end
+ new
+ end
+
+ # Instantiates a new State object, configured by _opts_.
+ #
+ # _opts_ can have the following keys:
+ #
+ # * *indent*: a string used to indent levels (default: ''),
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
+ # * *script_safe*: true if U+2028, U+2029 and forward slash (/) should be escaped
+ # as to make the JSON object safe to interpolate in a script tag (default: false).
+ # * *check_circular*: is deprecated now, use the :max_nesting option instead,
+ # * *max_nesting*: sets the maximum level of data structure nesting in
+ # the generated JSON, max_nesting = 0 if no maximum should be checked.
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
+ # generated, otherwise an exception is thrown, if these values are
+ # encountered. This options defaults to false.
+ def initialize(opts = nil)
+ @indent = ''
+ @space = ''
+ @space_before = ''
+ @object_nl = ''
+ @array_nl = ''
+ @allow_nan = false
+ @ascii_only = false
+ @as_json = false
+ @depth = 0
+ @buffer_initial_length = 1024
+ @script_safe = false
+ @strict = false
+ @max_nesting = 100
+ @sort_keys = false
+ configure(opts) if opts
+ end
+
+ # This string is used to indent levels in the JSON text.
+ attr_accessor :indent
+
+ # This string is used to insert a space between the tokens in a JSON
+ # string.
+ attr_accessor :space
+
+ # This string is used to insert a space before the ':' in JSON objects.
+ attr_accessor :space_before
+
+ # This string is put at the end of a line that holds a JSON object (or
+ # Hash).
+ attr_accessor :object_nl
+
+ # This string is put at the end of a line that holds a JSON array.
+ attr_accessor :array_nl
+
+ # This proc converts unsupported types into native JSON types.
+ attr_accessor :as_json
+
+ # This integer returns the maximum level of data structure nesting in
+ # the generated JSON, max_nesting = 0 if no maximum is checked.
+ attr_accessor :max_nesting
+
+ # If this attribute is set to true, forward slashes will be escaped in
+ # all json strings.
+ attr_accessor :script_safe
+
+ # If this attribute is set to true, attempting to serialize types not
+ # supported by the JSON spec will raise a JSON::GeneratorError
+ attr_accessor :strict
+
+ # Controls key sorting in the generated JSON. If set to +true+, object
+ # keys are sorted by key lexicographically. If set to a Proc, it
+ # receives the entire Hash and must return a Hash with its pairs in the
+ # desired order.
+ attr_reader :sort_keys
+
+ def sort_keys=(value) # :nodoc:
+ type_error = false
+ @sort_keys = case value
+ when Proc
+ value
+ when true
+ State.default_sort_keys_proc
+ when nil, false
+ false
+ else
+ type_error = true
+ false
+ end
+
+ if type_error
+ raise TypeError, "The `sort_keys` argument must be a boolean or a Proc"
+ end
+
+ @sort_keys
+ end
+
+ # :stopdoc:
+ attr_reader :buffer_initial_length
+
+ def buffer_initial_length=(length)
+ if length > 0
+ @buffer_initial_length = length
+ end
+ end
+ # :startdoc:
+
+ # This integer returns the current depth data structure nesting in the
+ # generated JSON.
+ attr_reader :depth
+
+ def depth=(depth)
+ if depth.negative?
+ raise ArgumentError, "depth must be >= 0 (got #{depth})"
+ end
+ @depth = depth
+ end
+
+ def check_max_nesting # :nodoc:
+ return if @max_nesting.zero?
+ current_nesting = depth + 1
+ current_nesting > @max_nesting and
+ raise NestingError, "nesting of #{current_nesting} is too deep. Did you try to serialize objects with circular references?"
+ end
+
+ # Returns true, if circular data structures are checked,
+ # otherwise returns false.
+ def check_circular?
+ !@max_nesting.zero?
+ end
+
+ # Returns true if NaN, Infinity, and -Infinity should be considered as
+ # valid JSON and output.
+ def allow_nan?
+ @allow_nan
+ end
+
+ # Returns true, if only ASCII characters should be generated. Otherwise
+ # returns false.
+ def ascii_only?
+ @ascii_only
+ end
+
+ # Returns true, if forward slashes are escaped. Otherwise returns false.
+ def script_safe?
+ @script_safe
+ end
+
+ # Returns true, if strict mode is enabled. Otherwise returns false.
+ # Strict mode only allow serializing JSON native types: Hash, Array,
+ # String, Integer, Float, true, false and nil.
+ def strict?
+ @strict
+ end
+
+ # Configure this State instance with the Hash _opts_, and return
+ # itself.
+ def configure(opts)
+ if opts.respond_to?(:to_hash)
+ opts = opts.to_hash
+ elsif opts.respond_to?(:to_h)
+ opts = opts.to_h
+ else
+ raise TypeError, "can't convert #{opts.class} into Hash"
+ end
+
+ if opts[:depth]&.negative?
+ raise ArgumentError, "depth must be >= 0 (got #{opts[:depth]})"
+ end
+
+ opts.each do |key, value|
+ instance_variable_set "@#{key}", value
+ end
+
+ # NOTE: If adding new instance variables here, check whether #generate should check them for #generate_json
+ @indent = opts[:indent] || '' if opts.key?(:indent)
+ @space = opts[:space] || '' if opts.key?(:space)
+ @space_before = opts[:space_before] || '' if opts.key?(:space_before)
+ @object_nl = opts[:object_nl] || '' if opts.key?(:object_nl)
+ @array_nl = opts[:array_nl] || '' if opts.key?(:array_nl)
+ @allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
+ @as_json = opts[:as_json].to_proc if opts[:as_json]
+ @ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
+ self.sort_keys = opts[:sort_keys] if opts.key?(:sort_keys)
+ @depth = opts[:depth] || 0
+ @buffer_initial_length ||= opts[:buffer_initial_length]
+
+ @script_safe = if opts.key?(:script_safe)
+ !!opts[:script_safe]
+ elsif opts.key?(:escape_slash)
+ !!opts[:escape_slash]
+ else
+ false
+ end
+
+ if opts.key?(:allow_duplicate_key)
+ @allow_duplicate_key = !!opts[:allow_duplicate_key]
+ else
+ @allow_duplicate_key = nil # nil is deprecation
+ end
+
+ @strict = !!opts[:strict] if opts.key?(:strict)
+
+ if !opts.key?(:max_nesting) # defaults to 100
+ @max_nesting = 100
+ elsif opts[:max_nesting]
+ unless opts[:max_nesting].is_a?(Integer)
+ raise TypeError, ":max_nesting must be an Integer, got: #{opts[:max_nesting].class}"
+ end
+ @max_nesting = opts[:max_nesting]
+ else
+ @max_nesting = 0
+ end
+ self
+ end
+ alias merge configure
+
+ def allow_duplicate_key? # :nodoc:
+ @allow_duplicate_key
+ end
+
+ # Returns the configuration instance variables as a hash, that can be
+ # passed to the configure method.
+ def to_h
+ result = {}
+ instance_variables.each do |iv|
+ key = iv.to_s[1..-1]
+ result[key.to_sym] = instance_variable_get(iv)
+ end
+
+ if result[:allow_duplicate_key].nil?
+ result.delete(:allow_duplicate_key)
+ end
+
+ result
+ end
+
+ alias to_hash to_h
+
+ # Generates a valid JSON document from object +obj+ and
+ # returns the result. If no valid JSON document can be
+ # created this method raises a
+ # GeneratorError exception.
+ def generate(obj, anIO = nil)
+ return dup.generate(obj, anIO) if frozen?
+
+ depth = @depth
+ if @indent.empty? and @space.empty? and @space_before.empty? and @object_nl.empty? and @array_nl.empty? and
+ !@ascii_only and !@script_safe and @max_nesting == 0 and (!@strict || Symbol === obj) and !@sort_keys
+ result = generate_json(obj, ''.dup)
+ else
+ if @sort_keys
+ obj = @sort_keys.call(obj)
+ end
+
+ result = obj.to_json(self)
+ end
+ JSON::TruffleRuby::Generator.valid_utf8?(result) or raise GeneratorError.new(
+ "source sequence #{result.inspect} is illegal/malformed utf-8",
+ obj
+ )
+ if anIO
+ anIO.write(result)
+ anIO
+ else
+ result
+ end
+ ensure
+ @depth = depth unless frozen?
+ end
+
+ # Handles @allow_nan, @buffer_initial_length, other ivars must be the default value (see above)
+ private def generate_json(obj, buf)
+ case obj
+ when Hash
+ buf << '{'
+ first = true
+ key_type = nil
+ obj.each_pair do |k,v|
+ if first
+ key_type = k.class
+ else
+ if key_type && !@allow_duplicate_key && key_type != k.class
+ key_type = nil # stop checking
+ JSON.send(:on_mixed_keys_hash, obj, !@allow_duplicate_key.nil?)
+ end
+ buf << ','
+ end
+
+ key_str = k.to_s
+ if key_str.class == String
+ fast_serialize_string(key_str, buf)
+ elsif key_str.is_a?(String)
+ generate_json(key_str, buf)
+ else
+ raise TypeError, "#{k.class}#to_s returns an instance of #{key_str.class}, expected a String"
+ end
+
+ buf << ':'
+ generate_json(v, buf)
+ first = false
+ end
+ buf << '}'
+ when Array
+ buf << '['
+ first = true
+ obj.each do |e|
+ buf << ',' unless first
+ generate_json(e, buf)
+ first = false
+ end
+ buf << ']'
+ when String
+ if obj.class == String
+ fast_serialize_string(obj, buf)
+ else
+ buf << obj.to_json(self)
+ end
+ when Integer
+ buf << obj.to_s
+ when Symbol
+ if @strict
+ fast_serialize_string(obj.name, buf)
+ else
+ buf << obj.to_json(self)
+ end
+ else
+ # Note: Float is handled this way since Float#to_s is slow anyway
+ buf << obj.to_json(self)
+ end
+ end
+
+ # Assumes !@ascii_only, !@script_safe
+ private def fast_serialize_string(string, buf) # :nodoc:
+ buf << '"'
+ unless string.encoding == ::Encoding::UTF_8
+ begin
+ string = string.encode(::Encoding::UTF_8)
+ rescue Encoding::UndefinedConversionError => error
+ raise GeneratorError.new(error.message, string)
+ end
+ end
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", string) unless string.valid_encoding?
+
+ if /["\\\x0-\x1f]/.match?(string)
+ buf << string.gsub(/["\\\x0-\x1f]/, MAP)
+ else
+ buf << string
+ end
+ buf << '"'
+ end
+
+ # Return the value returned by method +name+.
+ def [](name)
+ ::JSON.deprecation_warning("JSON::State#[] is deprecated and will be removed in json 3.0.0")
+
+ if respond_to?(name)
+ __send__(name)
+ else
+ instance_variable_get("@#{name}") if
+ instance_variables.include?("@#{name}".to_sym) # avoid warning
+ end
+ end
+
+ def []=(name, value)
+ ::JSON.deprecation_warning("JSON::State#[]= is deprecated and will be removed in json 3.0.0")
+
+ if respond_to?(name_writer = "#{name}=")
+ __send__ name_writer, value
+ else
+ instance_variable_set "@#{name}", value
+ end
+ end
+ end
+
+ module GeneratorMethods
+ module Object
+ # Converts this object to a string (calling #to_s), converts
+ # it to a JSON string, and returns the result. This is a fallback, if no
+ # special method #to_json was defined for some object.
+ def to_json(state = nil, *)
+ state = State.from_state(state) if state
+ if state&.strict?
+ value = self
+ if state.strict? && !Generator.native_type?(value)
+ if state.as_json
+ value = state.as_json.call(value, false)
+ unless Generator.native_type?(value)
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
+ end
+ value.to_json(state)
+ else
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
+ end
+ end
+ else
+ to_s.to_json
+ end
+ end
+ end
+
+ module Hash
+ # Returns a JSON string containing a JSON object, that is unparsed from
+ # this Hash instance.
+ # _state_ is a JSON::State object, that can also be used to configure the
+ # produced JSON string output further.
+ # _depth_ is used to find out nesting depth, to indent accordingly.
+ def to_json(state = nil, *)
+ state = State.from_state(state)
+ depth = state.depth
+ state.check_max_nesting
+ json_transform(state)
+ ensure
+ state.depth = depth
+ end
+
+ private
+
+ def json_transform(state)
+ depth = state.depth += 1
+
+ if empty?
+ state.depth -= 1
+ return +'{}'
+ end
+
+ delim = ",#{state.object_nl}"
+ result = "{#{state.object_nl}"
+ first = true
+ key_type = nil
+ indent = !state.object_nl.empty?
+ each { |key, value|
+ if first
+ key_type = key.class
+ else
+ if key_type && !state.allow_duplicate_key? && key_type != key.class
+ key_type = nil # stop checking
+ JSON.send(:on_mixed_keys_hash, self, state.allow_duplicate_key? == false)
+ end
+ result << delim
+ end
+ result << state.indent * depth if indent
+
+ if state.strict?
+ if state.as_json && (!Generator.native_key?(key) || !Generator.valid_encoding?(key))
+ key = state.as_json.call(key, true)
+ end
+
+ unless Generator.native_key?(key)
+ raise GeneratorError.new("#{key.class} not allowed as object key in JSON", key)
+ end
+
+ unless Generator.valid_encoding?(key)
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", key)
+ end
+ end
+
+ key_str = key.to_s
+ if key_str.is_a?(String)
+ key_json = key_str.to_json(state)
+ else
+ raise TypeError, "#{key.class}#to_s returns an instance of #{key_str.class}, expected a String"
+ end
+
+ result = "#{result}#{key_json}#{state.space_before}:#{state.space}"
+ if state.strict? && !Generator.native_type?(value)
+ if state.as_json
+ value = state.as_json.call(value, false)
+ unless Generator.native_type?(value)
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
+ end
+ result << value.to_json(state)
+ state.depth = depth
+ else
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
+ end
+ elsif value.respond_to?(:to_json)
+ result << value.to_json(state)
+ state.depth = depth
+ else
+ result << %{"#{String(value)}"}
+ end
+ first = false
+ }
+ depth -= 1
+ unless first
+ result << state.object_nl
+ result << state.indent * depth if indent
+ end
+ result << '}'
+ result
+ end
+ end
+
+ module Array
+ # Returns a JSON string containing a JSON array, that is unparsed from
+ # this Array instance.
+ # _state_ is a JSON::State object, that can also be used to configure the
+ # produced JSON string output further.
+ def to_json(state = nil, *)
+ state = State.from_state(state)
+ depth = state.depth
+ state.check_max_nesting
+ json_transform(state)
+ ensure
+ state.depth = depth
+ end
+
+ private
+
+ def json_transform(state)
+ depth = state.depth += 1
+
+ if empty?
+ state.depth -= 1
+ return +'[]'
+ end
+
+ result = '['.dup
+ if state.array_nl.empty?
+ delim = ","
+ else
+ result << state.array_nl
+ delim = ",#{state.array_nl}"
+ end
+
+ first = true
+ indent = !state.array_nl.empty?
+ each { |value|
+ result << delim unless first
+ result << state.indent * depth if indent
+ if state.strict? && !Generator.native_type?(value)
+ if state.as_json
+ value = state.as_json.call(value, false)
+ unless Generator.native_type?(value)
+ raise GeneratorError.new("#{value.class} returned by #{state.as_json} not allowed in JSON", value)
+ end
+ result << value.to_json(state)
+ else
+ raise GeneratorError.new("#{value.class} not allowed in JSON", value)
+ end
+ elsif value.respond_to?(:to_json)
+ result << value.to_json(state)
+ state.depth = depth
+ else
+ result << %{"#{String(value)}"}
+ end
+ first = false
+ }
+ depth -= 1
+ result << state.array_nl
+ result << state.indent * depth if indent
+ result << ']'
+ end
+ end
+
+ module Integer
+ # Returns a JSON string representation for this Integer number.
+ def to_json(*) to_s end
+ end
+
+ module Float
+ # Returns a JSON string representation for this Float number.
+ def to_json(state = nil, *args)
+ state = State.from_state(state)
+ if infinite? || nan?
+ if state.allow_nan?
+ to_s
+ elsif state.strict? && state.as_json
+ casted_value = state.as_json.call(self, false)
+
+ if casted_value.equal?(self)
+ raise GeneratorError.new("#{self} not allowed in JSON", self)
+ end
+ unless Generator.native_type?(casted_value)
+ raise GeneratorError.new("#{casted_value.class} returned by #{state.as_json} not allowed in JSON", casted_value)
+ end
+
+ state.check_max_nesting
+ state.depth += 1
+ result = casted_value.to_json(state, *args)
+ state.depth -= 1
+ result
+ else
+ raise GeneratorError.new("#{self} not allowed in JSON", self)
+ end
+ else
+ to_s
+ end
+ end
+ end
+
+ module Symbol
+ def to_json(state = nil, *args)
+ state = State.from_state(state)
+ if state.strict?
+ name.to_json(state, *args)
+ else
+ super
+ end
+ end
+ end
+
+ module String
+ # This string should be encoded with UTF-8 A call to this method
+ # returns a JSON string encoded with UTF16 big endian characters as
+ # \u????.
+ def to_json(state = nil, *args)
+ state = State.from_state(state)
+ string = self
+
+ if state.strict? && state.as_json
+ unless Generator.valid_encoding?(string)
+ string = state.as_json.call(string, false)
+ unless string.is_a?(::String)
+ return string.to_json(state, *args)
+ end
+ end
+ end
+
+ if string.encoding == ::Encoding::UTF_8
+ unless string.valid_encoding?
+ raise GeneratorError.new("source sequence is illegal/malformed utf-8", self)
+ end
+ else
+ string = string.encode(::Encoding::UTF_8)
+ end
+
+ if state.ascii_only?
+ %("#{JSON::TruffleRuby::Generator.utf8_to_json_ascii(string, state.script_safe)}")
+ else
+ %("#{JSON::TruffleRuby::Generator.utf8_to_json(string, state.script_safe)}")
+ end
+ rescue Encoding::UndefinedConversionError => error
+ raise ::JSON::GeneratorError.new(error.message, self)
+ end
+ end
+
+ module TrueClass
+ # Returns a JSON string for true: 'true'.
+ def to_json(*) +'true' end
+ end
+
+ module FalseClass
+ # Returns a JSON string for false: 'false'.
+ def to_json(*) +'false' end
+ end
+
+ module NilClass
+ # Returns a JSON string for nil: 'null'.
+ def to_json(*) +'null' end
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/version.rb b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/version.rb
new file mode 100644
index 0000000..e8483db
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/json-2.21.1/lib/json/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module JSON
+ VERSION = '2.21.1'
+end