nosj 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/Cargo.lock +375 -0
- data/Cargo.toml +27 -0
- data/LICENSE.txt +21 -0
- data/NOTICE +13 -0
- data/README.md +214 -0
- data/ext/nosj/Cargo.toml +25 -0
- data/ext/nosj/extconf.rb +6 -0
- data/ext/nosj/src/gen/errors.rs +61 -0
- data/ext/nosj/src/gen/hash_iter.rs +53 -0
- data/ext/nosj/src/gen/keys.rs +58 -0
- data/ext/nosj/src/gen/mod.rs +171 -0
- data/ext/nosj/src/gen/opts.rs +162 -0
- data/ext/nosj/src/gen/ruby.rs +100 -0
- data/ext/nosj/src/gen/walker.rs +638 -0
- data/ext/nosj/src/lib.rs +56 -0
- data/ext/nosj/src/parse.rs +211 -0
- data/ext/nosj/src/pointer.rs +190 -0
- data/ext/nosj/src/sink.rs +370 -0
- data/ext/nosj/src/state.rs +89 -0
- data/lib/nosj/json.rb +124 -0
- data/lib/nosj/multi_json.rb +51 -0
- data/lib/nosj/native.rb +11 -0
- data/lib/nosj/version.rb +6 -0
- data/lib/nosj.rb +193 -0
- data/sig/nosj.rbs +61 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 87c27bdaeada8a26a98f378d168befec0d1b50b497663bf09ea58b133358b98f
|
|
4
|
+
data.tar.gz: 0756f4a05107fabcffe58c889c7a40487c7f060d944d9b5bce161430d4e0c70f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ff14cc073152752000f9ad0102222c69e3a63ec561b8411886164c5e1c85fb30044d31e6286074169e1cc531a533b8d59d5b103473b4ae413c32c4e0d565b7f9
|
|
7
|
+
data.tar.gz: 673019e943af6446a6bb63dacaaaee002f11c07e450154abd5b0842deffc1f798e924ec3531366d2717a71391bf630d98b83b1e2320e90aa3790c83cb2cd93dd
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## [0.1.0] - 2026-07-15
|
|
2
|
+
|
|
3
|
+
Initial release.
|
|
4
|
+
|
|
5
|
+
- `NOSJ.parse`, `NOSJ.generate`, and `NOSJ.pretty_generate`: `json`-gem-compatible parsing and generation—same output bytes, same option names, same error classes and messages—built on the first-party SIMD [nosj](https://crates.io/crates/nosj) crate (NEON on Apple Silicon; SSE2/AVX2 on x86-64, selected at runtime). Faster than the `json` gem and the third-party parsers (Oj, RapidJSON, FastJsonparser, Yajl) across the benchmark corpus, in both directions.
|
|
6
|
+
- Partial parsing: `NOSJ.dig` and `NOSJ.at_pointer` resolve a JSON Pointer and materialize only the matched subtree; `NOSJ.dig_many` and `NOSJ.at_pointers` resolve whole batches of paths in a single pass over the document.
|
|
7
|
+
- `NOSJ.valid?`: full-strictness validation that allocates no Ruby objects.
|
|
8
|
+
- Drop-in acceleration: `require "nosj/json"` reroutes `JSON.parse`, `JSON.generate`, `JSON.pretty_generate`, and `JSON.dump` through nosj, falling back to the original implementation for unsupported options; `require "nosj/multi_json"` adds a MultiJson adapter.
|
|
9
|
+
- Precompiled platform gems, each built natively with profile-guided optimization: Linux x86-64 and arm64 (glibc and musl), macOS (Apple Silicon), Windows (x64), for Ruby 3.3 through 4.0. Other platforms compile the source gem.
|
|
10
|
+
- RBS signatures and full YARD documentation.
|
data/Cargo.lock
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "ahash"
|
|
7
|
+
version = "0.8.12"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"cfg-if",
|
|
12
|
+
"getrandom",
|
|
13
|
+
"once_cell",
|
|
14
|
+
"version_check",
|
|
15
|
+
"zerocopy",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[[package]]
|
|
19
|
+
name = "aho-corasick"
|
|
20
|
+
version = "1.1.4"
|
|
21
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
22
|
+
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"memchr",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[[package]]
|
|
28
|
+
name = "bindgen"
|
|
29
|
+
version = "0.72.1"
|
|
30
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
31
|
+
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
|
32
|
+
dependencies = [
|
|
33
|
+
"bitflags",
|
|
34
|
+
"cexpr",
|
|
35
|
+
"clang-sys",
|
|
36
|
+
"itertools",
|
|
37
|
+
"proc-macro2",
|
|
38
|
+
"quote",
|
|
39
|
+
"regex",
|
|
40
|
+
"rustc-hash",
|
|
41
|
+
"shlex",
|
|
42
|
+
"syn",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[package]]
|
|
46
|
+
name = "bitflags"
|
|
47
|
+
version = "2.13.0"
|
|
48
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
49
|
+
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "cexpr"
|
|
53
|
+
version = "0.6.0"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
|
56
|
+
dependencies = [
|
|
57
|
+
"nom",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "cfg-if"
|
|
62
|
+
version = "1.0.4"
|
|
63
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
64
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
65
|
+
|
|
66
|
+
[[package]]
|
|
67
|
+
name = "clang-sys"
|
|
68
|
+
version = "1.8.1"
|
|
69
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
70
|
+
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
|
71
|
+
dependencies = [
|
|
72
|
+
"glob",
|
|
73
|
+
"libc",
|
|
74
|
+
"libloading",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[[package]]
|
|
78
|
+
name = "either"
|
|
79
|
+
version = "1.16.0"
|
|
80
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
81
|
+
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
|
82
|
+
|
|
83
|
+
[[package]]
|
|
84
|
+
name = "fast-float2"
|
|
85
|
+
version = "0.2.3"
|
|
86
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
+
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
|
88
|
+
|
|
89
|
+
[[package]]
|
|
90
|
+
name = "getrandom"
|
|
91
|
+
version = "0.3.4"
|
|
92
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
93
|
+
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
94
|
+
dependencies = [
|
|
95
|
+
"cfg-if",
|
|
96
|
+
"libc",
|
|
97
|
+
"r-efi",
|
|
98
|
+
"wasip2",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
[[package]]
|
|
102
|
+
name = "glob"
|
|
103
|
+
version = "0.3.3"
|
|
104
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
105
|
+
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
|
106
|
+
|
|
107
|
+
[[package]]
|
|
108
|
+
name = "itertools"
|
|
109
|
+
version = "0.13.0"
|
|
110
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
111
|
+
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
112
|
+
dependencies = [
|
|
113
|
+
"either",
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
[[package]]
|
|
117
|
+
name = "lazy_static"
|
|
118
|
+
version = "1.5.0"
|
|
119
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
120
|
+
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "libc"
|
|
124
|
+
version = "0.2.186"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "libloading"
|
|
130
|
+
version = "0.8.9"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
|
133
|
+
dependencies = [
|
|
134
|
+
"cfg-if",
|
|
135
|
+
"windows-link",
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
[[package]]
|
|
139
|
+
name = "magnus"
|
|
140
|
+
version = "0.9.0"
|
|
141
|
+
source = "git+https://github.com/matsadler/magnus#4e46772050e47cd6cd988fa935263cc5c583e388"
|
|
142
|
+
dependencies = [
|
|
143
|
+
"magnus-macros",
|
|
144
|
+
"rb-sys",
|
|
145
|
+
"rb-sys-env",
|
|
146
|
+
"seq-macro",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
[[package]]
|
|
150
|
+
name = "magnus-macros"
|
|
151
|
+
version = "0.9.0"
|
|
152
|
+
source = "git+https://github.com/matsadler/magnus#4e46772050e47cd6cd988fa935263cc5c583e388"
|
|
153
|
+
dependencies = [
|
|
154
|
+
"proc-macro2",
|
|
155
|
+
"quote",
|
|
156
|
+
"syn",
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
[[package]]
|
|
160
|
+
name = "memchr"
|
|
161
|
+
version = "2.8.3"
|
|
162
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
163
|
+
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
164
|
+
|
|
165
|
+
[[package]]
|
|
166
|
+
name = "minimal-lexical"
|
|
167
|
+
version = "0.2.1"
|
|
168
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
169
|
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|
170
|
+
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "nom"
|
|
173
|
+
version = "7.1.3"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"memchr",
|
|
178
|
+
"minimal-lexical",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "nosj"
|
|
183
|
+
version = "0.1.1"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "c6ecf0db23b7e9944ecee15ab3dd01b0bde0ab2a195442f363847739f667ac8d"
|
|
186
|
+
dependencies = [
|
|
187
|
+
"fast-float2",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
[[package]]
|
|
191
|
+
name = "nosj_native"
|
|
192
|
+
version = "0.1.0"
|
|
193
|
+
dependencies = [
|
|
194
|
+
"ahash",
|
|
195
|
+
"magnus",
|
|
196
|
+
"nosj",
|
|
197
|
+
"rb-sys",
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
[[package]]
|
|
201
|
+
name = "once_cell"
|
|
202
|
+
version = "1.21.4"
|
|
203
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
204
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
205
|
+
|
|
206
|
+
[[package]]
|
|
207
|
+
name = "proc-macro2"
|
|
208
|
+
version = "1.0.106"
|
|
209
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
210
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
211
|
+
dependencies = [
|
|
212
|
+
"unicode-ident",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
[[package]]
|
|
216
|
+
name = "quote"
|
|
217
|
+
version = "1.0.46"
|
|
218
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
219
|
+
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
|
220
|
+
dependencies = [
|
|
221
|
+
"proc-macro2",
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
[[package]]
|
|
225
|
+
name = "r-efi"
|
|
226
|
+
version = "5.3.0"
|
|
227
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
228
|
+
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
229
|
+
|
|
230
|
+
[[package]]
|
|
231
|
+
name = "rb-sys"
|
|
232
|
+
version = "0.9.128"
|
|
233
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
234
|
+
checksum = "45ca28513560e56cfb79a62b1fce363c73af170a182024ce880c77ee9429920a"
|
|
235
|
+
dependencies = [
|
|
236
|
+
"rb-sys-build",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
[[package]]
|
|
240
|
+
name = "rb-sys-build"
|
|
241
|
+
version = "0.9.128"
|
|
242
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
243
|
+
checksum = "ce04b2c55eff3a21aaa623fcc655d94373238e72cac6b3e1a3641ff31649f99a"
|
|
244
|
+
dependencies = [
|
|
245
|
+
"bindgen",
|
|
246
|
+
"lazy_static",
|
|
247
|
+
"proc-macro2",
|
|
248
|
+
"quote",
|
|
249
|
+
"regex",
|
|
250
|
+
"shell-words",
|
|
251
|
+
"syn",
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
[[package]]
|
|
255
|
+
name = "rb-sys-env"
|
|
256
|
+
version = "0.2.3"
|
|
257
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
258
|
+
checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
|
|
259
|
+
|
|
260
|
+
[[package]]
|
|
261
|
+
name = "regex"
|
|
262
|
+
version = "1.13.0"
|
|
263
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
264
|
+
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
|
|
265
|
+
dependencies = [
|
|
266
|
+
"aho-corasick",
|
|
267
|
+
"memchr",
|
|
268
|
+
"regex-automata",
|
|
269
|
+
"regex-syntax",
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
[[package]]
|
|
273
|
+
name = "regex-automata"
|
|
274
|
+
version = "0.4.15"
|
|
275
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
276
|
+
checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
|
|
277
|
+
dependencies = [
|
|
278
|
+
"aho-corasick",
|
|
279
|
+
"memchr",
|
|
280
|
+
"regex-syntax",
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
[[package]]
|
|
284
|
+
name = "regex-syntax"
|
|
285
|
+
version = "0.8.11"
|
|
286
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
287
|
+
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
288
|
+
|
|
289
|
+
[[package]]
|
|
290
|
+
name = "rustc-hash"
|
|
291
|
+
version = "2.1.3"
|
|
292
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
293
|
+
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
294
|
+
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "seq-macro"
|
|
297
|
+
version = "0.3.6"
|
|
298
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
+
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
300
|
+
|
|
301
|
+
[[package]]
|
|
302
|
+
name = "shell-words"
|
|
303
|
+
version = "1.1.1"
|
|
304
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
305
|
+
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
|
306
|
+
|
|
307
|
+
[[package]]
|
|
308
|
+
name = "shlex"
|
|
309
|
+
version = "1.3.0"
|
|
310
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
311
|
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
312
|
+
|
|
313
|
+
[[package]]
|
|
314
|
+
name = "syn"
|
|
315
|
+
version = "2.0.119"
|
|
316
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
317
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
318
|
+
dependencies = [
|
|
319
|
+
"proc-macro2",
|
|
320
|
+
"quote",
|
|
321
|
+
"unicode-ident",
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
[[package]]
|
|
325
|
+
name = "unicode-ident"
|
|
326
|
+
version = "1.0.24"
|
|
327
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
328
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
329
|
+
|
|
330
|
+
[[package]]
|
|
331
|
+
name = "version_check"
|
|
332
|
+
version = "0.9.5"
|
|
333
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
334
|
+
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
335
|
+
|
|
336
|
+
[[package]]
|
|
337
|
+
name = "wasip2"
|
|
338
|
+
version = "1.0.4+wasi-0.2.12"
|
|
339
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
340
|
+
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
|
341
|
+
dependencies = [
|
|
342
|
+
"wit-bindgen",
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
[[package]]
|
|
346
|
+
name = "windows-link"
|
|
347
|
+
version = "0.2.1"
|
|
348
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
349
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
350
|
+
|
|
351
|
+
[[package]]
|
|
352
|
+
name = "wit-bindgen"
|
|
353
|
+
version = "0.57.1"
|
|
354
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
355
|
+
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
|
356
|
+
|
|
357
|
+
[[package]]
|
|
358
|
+
name = "zerocopy"
|
|
359
|
+
version = "0.8.54"
|
|
360
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
361
|
+
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
|
|
362
|
+
dependencies = [
|
|
363
|
+
"zerocopy-derive",
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
[[package]]
|
|
367
|
+
name = "zerocopy-derive"
|
|
368
|
+
version = "0.8.54"
|
|
369
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
370
|
+
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
|
|
371
|
+
dependencies = [
|
|
372
|
+
"proc-macro2",
|
|
373
|
+
"quote",
|
|
374
|
+
"syn",
|
|
375
|
+
]
|
data/Cargo.toml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
|
|
2
|
+
# a Rust project. Your extensions dependencies should be added to the Cargo.toml
|
|
3
|
+
# in the ext/ directory.
|
|
4
|
+
|
|
5
|
+
[workspace]
|
|
6
|
+
members = ["./ext/nosj"]
|
|
7
|
+
resolver = "2"
|
|
8
|
+
|
|
9
|
+
[profile.release]
|
|
10
|
+
opt-level = 3
|
|
11
|
+
lto = "fat"
|
|
12
|
+
codegen-units = 1
|
|
13
|
+
# Ship lean artifacts: leftover debug sections (e.g. from the
|
|
14
|
+
# precompiled std) are stripped. The symbol table stays, so crash
|
|
15
|
+
# backtraces keep function names.
|
|
16
|
+
strip = "debuginfo"
|
|
17
|
+
# No landing pads: a Ruby extension must never unwind across the C
|
|
18
|
+
# boundary anyway, and magnus's per-pair hash-iteration panic guard
|
|
19
|
+
# (std::panic::catch_unwind) compiles to a plain call under abort.
|
|
20
|
+
# Measured ~4ns per object pair on Zen 4 with unwinding enabled.
|
|
21
|
+
panic = "abort"
|
|
22
|
+
|
|
23
|
+
[profile.profiling]
|
|
24
|
+
inherits = "release"
|
|
25
|
+
debug = true
|
|
26
|
+
opt-level = 3
|
|
27
|
+
lto = "fat"
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yaroslav Markin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/NOTICE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
nosj (Ruby gem)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yaroslav Markin. Released under the MIT License
|
|
4
|
+
(see LICENSE.txt).
|
|
5
|
+
|
|
6
|
+
Every distribution of this gem embeds the nosj Rust crate
|
|
7
|
+
(https://github.com/yaroslav/nosj), compiled into the native
|
|
8
|
+
extension: precompiled platform gems ship the compiled binary, and the
|
|
9
|
+
source gem compiles the crate at install time. The crate is licensed
|
|
10
|
+
MIT AND BSL-1.0 AND Apache-2.0; its NOTICE file itemizes the derived
|
|
11
|
+
components (the fpconv/Grisu2 float formatter under BSL-1.0; the
|
|
12
|
+
fast_float decimal conversion and simdjson structural-indexing
|
|
13
|
+
techniques under Apache-2.0) and reproduces each component license.
|
data/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# gem nosj
|
|
2
|
+
|
|
3
|
+
**gem nosj** is an **very fast JSON parser and generator for Ruby**, written in Rust on the first-party [nosj](https://github.com/yaroslav/nosj) crate and **SIMD-accelerated** on every platform (NEON on Apple Silicon, SSE2/AVX2 on x86-64).
|
|
4
|
+
|
|
5
|
+
> gem nosj is the powerful evil twin of the json gem.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/yaroslav/nosj-ruby/releases)
|
|
8
|
+
[](https://rubydoc.info/gems/nosj)
|
|
9
|
+
|
|
10
|
+
- It is **faster** than gem json and every
|
|
11
|
+
third-party parser, including Oj, RapidJSON, FastJsonparser, Yajl. 1.0–1.8× faster than the bundled json gem, 1.3–11× faster than Oj, and up to 17×
|
|
12
|
+
faster than Yajl—[see Benchmarks](#benchmarks).
|
|
13
|
+
- It comes **precompiled** (platform gems built with per-platform optimizations,
|
|
14
|
+
nothing to compile on install).
|
|
15
|
+
- It has a **partial parsing mode**: JSON Pointer lookups that pull single values out of big documents in microseconds, skipping everything else.
|
|
16
|
+
- Same API and option names as gem json.
|
|
17
|
+
|
|
18
|
+
**And there's more**: validate documents without building a single Ruby object, resolve whole batches of paths in one pass, and accelerate an entire application with a one-line drop-in.
|
|
19
|
+
|
|
20
|
+
- [Requirements](#requirements)
|
|
21
|
+
- [Getting started](#getting-started)
|
|
22
|
+
- [What's in the box](#whats-in-the-box)
|
|
23
|
+
- [Benchmarks](#benchmarks)
|
|
24
|
+
- [Switching from the json gem](#switching-from-the-json-gem)
|
|
25
|
+
- [How it works](#how-it-works)
|
|
26
|
+
- [Development](#development)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- **Ruby 3.3 or newer** (CRuby; tested on 3.3, 3.4, and 4.0).
|
|
32
|
+
- **Linux** (x86-64 and arm64, glibc and musl), **macOS** (Apple
|
|
33
|
+
Silicon), or **Windows** (x64): these platforms install precompiled,
|
|
34
|
+
per-platform-optimized gems with nothing to build.
|
|
35
|
+
|
|
36
|
+
## Getting started
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bundle add nosj
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require "nosj"
|
|
44
|
+
|
|
45
|
+
NOSJ.parse('{"a":[1,true]}') #=> {"a" => [1, true]}
|
|
46
|
+
NOSJ.generate({"a" => [1, true]}) #=> '{"a":[1,true]}'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That's it—if you know the `json` gem, you already know `nosj`.
|
|
50
|
+
|
|
51
|
+
Want the speedup without touching your code? One line reroutes
|
|
52
|
+
`JSON.parse`, `JSON.generate`, `JSON.pretty_generate`, and `JSON.dump`
|
|
53
|
+
through nosj:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
require "nosj/json"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
In a Bundler app (Rails included) that can live entirely in the
|
|
60
|
+
Gemfile you can do this:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
gem "nosj", require: "nosj/json"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Options nosj supports take the fast path; anything exotic
|
|
67
|
+
(`create_additions`, `object_class`, `JSON::State`, procs, IO
|
|
68
|
+
arguments) falls back to the original implementation, so `JSON.load`,
|
|
69
|
+
`JSON.parse!`, and `JSON.load_file` keep their exact behavior.
|
|
70
|
+
Exceptions re-raise as the JSON classes, so your rescue clauses keep
|
|
71
|
+
working. Measured through the patch: parse 1.11×, generate 1.05× over
|
|
72
|
+
the original gem. (A MultiJson adapter ships too:
|
|
73
|
+
`require "nosj/multi_json"`, then `MultiJson.use NOSJ::MultiJsonAdapter`.)
|
|
74
|
+
|
|
75
|
+
## What's in the box
|
|
76
|
+
|
|
77
|
+
**The `json` gem API**, on the `NOSJ` module:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
NOSJ.parse(src, symbolize_names: true) # also: freeze, max_nesting,
|
|
81
|
+
# allow_nan, allow_trailing_comma
|
|
82
|
+
NOSJ.generate(obj) # indent, space, object_nl, ...,
|
|
83
|
+
NOSJ.pretty_generate(obj) # ascii_only, script_safe, strict
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Validation without parsing.** `NOSJ.valid?` runs the full
|
|
87
|
+
parser—tokenizers, string decode, number validation—into a null sink
|
|
88
|
+
and allocates no Ruby objects at all. It is 2-4× faster than
|
|
89
|
+
`NOSJ.parse`, which already leads every parser above:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
NOSJ.valid?('{"a":1}') #=> true
|
|
93
|
+
NOSJ.valid?('{"a":}') #=> false
|
|
94
|
+
NOSJ.valid?(src, max_nesting: false) # same options as parse
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Partial parsing.** Pull values out of a document without
|
|
98
|
+
materializing the rest—skipped content is stepped over at SIMD block
|
|
99
|
+
speed, so a lookup costs what it skips, not what the document weighs:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
NOSJ.dig(json, "users", 3, "name") # Hash#dig-shaped
|
|
103
|
+
NOSJ.at_pointer(json, "/users/3/name") # JSON Pointer
|
|
104
|
+
|
|
105
|
+
# Many lookups in one pass. A batch costs about as much as its
|
|
106
|
+
# single deepest member:
|
|
107
|
+
NOSJ.at_pointers(json, ["/users/3/name", "/meta/count"])
|
|
108
|
+
NOSJ.dig_many(json, [["users", 3, "name"], ["meta", "count"]])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Example: an early field resolves in ~0.35µs where `JSON.parse(json).dig(...)`
|
|
112
|
+
costs ~980µs on the same document—three orders of magnitude. A field
|
|
113
|
+
at the far end of a 570 KB document costs ~71µs, still 13× faster
|
|
114
|
+
than parse-then-dig. Misses return nil; matched subtrees materialize
|
|
115
|
+
with the same options as `parse` (`symbolize_names:`, `freeze:`).
|
|
116
|
+
|
|
117
|
+
## Benchmarks
|
|
118
|
+
|
|
119
|
+
Every installed JSON gem, benchmark-ips: AWS EC2 c7a.2xlarge (AMD EPYC 9R14, Zen 4), Ruby 4.0.6 + YJIT, json 2.21.1, Oj 3.17.4, RapidJSON 0.4.0, FastJsonparser 0.6.0, Yajl 1.4.3, PGO build, 2026-07-16. `×N` = times slower than nosj.
|
|
120
|
+
|
|
121
|
+
Parse:
|
|
122
|
+
|
|
123
|
+
| file | nosj (i/s) | json | Oj | FastJsonparser | RapidJSON | Yajl |
|
|
124
|
+
|---|---:|---:|---:|---:|---:|---:|
|
|
125
|
+
| activitypub | **12.4k** | ×1.18 | ×1.53 | ×1.89 | ×1.99 | ×4.57 |
|
|
126
|
+
| canada | **248** | ×1.10 | ×8.28 | ×1.45 | ×1.51 | ×4.96 |
|
|
127
|
+
| citm_catalog | **504** | ×1.03 | ×1.97 | ×2.07 | ×1.83 | ×4.93 |
|
|
128
|
+
| gsoc-2018 | **397** | ×1.30 | ×1.47 | ×1.81 | ×1.80 | ×4.78 |
|
|
129
|
+
| homebrew-formula | **15.2** | ×1.13 | ×1.54 | ×2.42 | ×2.02 | ×4.30 |
|
|
130
|
+
| homebrew-llvm | **49.3k** | ×1.60 | ×1.48 | ×1.50 | ×1.85 | ×5.11 |
|
|
131
|
+
| mesh | **1.1k** | ×1.30 | ×3.93 | ×1.82 | ×1.95 | ×6.96 |
|
|
132
|
+
| numbers | **5.9k** | ×1.19 | ×4.64 | ×1.43 | ×1.75 | ×7.04 |
|
|
133
|
+
| ohai | **14.4k** | ×1.47 | ×1.61 | ×2.11 | ×1.97 | ×4.71 |
|
|
134
|
+
| simple | **977k** | ×1.31 | ×1.77 | ×2.08 | ×1.63 | ×5.15 |
|
|
135
|
+
| small_mixed | **3.2M** | ×1.48 | ×2.85 | ×2.44 | ×1.82 | ×6.75 |
|
|
136
|
+
| tolstoy | **8.9k** | ×1.79 | ×1.96 | ×2.29 | ×2.10 | ×17.31 |
|
|
137
|
+
| twitter | **1.1k** | ×1.09 | ×1.83 | ×2.25 | ×2.61 | ×5.40 |
|
|
138
|
+
|
|
139
|
+
Generate:
|
|
140
|
+
|
|
141
|
+
| file | nosj (i/s) | json | Oj | RapidJSON | Yajl |
|
|
142
|
+
|---|---:|---:|---:|---:|---:|
|
|
143
|
+
| activitypub | **34.8k** | ×1.20 | ×1.78 | ×2.41 | ×5.84 |
|
|
144
|
+
| canada | **150** | ×0.98\* | ×10.98 | ×11.20 | ×10.87 |
|
|
145
|
+
| citm_catalog | **1.3k** | ×1.04 | ×1.44 | ×1.55 | ×2.85 |
|
|
146
|
+
| gsoc-2018 | **1.1k** | ×1.28 | ×2.64 | ×3.64 | ×11.00 |
|
|
147
|
+
| homebrew-formula | **20.9** | ×1.07 | ×1.25 | ×1.62 | ×3.03 |
|
|
148
|
+
| homebrew-llvm | **71.5k** | ×1.23 | ×2.22 | ×3.04 | ×5.80 |
|
|
149
|
+
| mesh | **613** | ×1.08 | ×9.24 | ×9.40 | ×9.26 |
|
|
150
|
+
| numbers | **2.1k** | ×1.03 | ×10.63 | ×11.00 | ×10.69 |
|
|
151
|
+
| ohai | **39.4k** | ×1.07 | ×1.29 | ×1.50 | ×3.37 |
|
|
152
|
+
| simple | **2.2M** | ×1.03 | ×1.58 | ×1.62 | ×4.36 |
|
|
153
|
+
| small_mixed | **5.6M** | ×1.09 | ×2.26 | ×1.83 | ×7.16 |
|
|
154
|
+
| tolstoy | **8.3k** | ×1.30 | ×4.15 | ×6.50 | ×14.54 |
|
|
155
|
+
| twitter | **2.9k** | ×1.09 | ×1.46 | ×2.01 | ×4.05 |
|
|
156
|
+
|
|
157
|
+
\* canada-generate is a statistical tie with the json gem (within
|
|
158
|
+
measurement error).
|
|
159
|
+
|
|
160
|
+
Reproduce with `rake bench` (the parity-gated comparison, after a PGO retrain—the shipping configuration) or `rake bench:ips` (the multi-gem shoot-out).
|
|
161
|
+
|
|
162
|
+
## Switching from the json gem
|
|
163
|
+
|
|
164
|
+
You mostly don't have to do anything. Some differences:
|
|
165
|
+
|
|
166
|
+
- The legacy object-deserialization options (`create_additions`,
|
|
167
|
+
`object_class`, `array_class`, `decimal_class`) raise ArgumentError;
|
|
168
|
+
the `nosj/json` drop-in falls back to the original gem for them.
|
|
169
|
+
- Behaviors the `json` gem itself deprecates (JS comments, raw invalid
|
|
170
|
+
UTF-8) follow the strict semantics instead.
|
|
171
|
+
- Unlike `Array#dig`, negative indices in `NOSJ.dig` return nil (JSON
|
|
172
|
+
Pointer has no equivalent).
|
|
173
|
+
- Parse error *messages* use byte offsets rather than the gem's
|
|
174
|
+
phrasing (classes match).
|
|
175
|
+
|
|
176
|
+
Everything else—including the gem's exact float formatting, which is
|
|
177
|
+
not the shortest-round-trip form most libraries emit—matches
|
|
178
|
+
byte-for-byte and is verified continuously against the full corpus.
|
|
179
|
+
|
|
180
|
+
## How it works
|
|
181
|
+
|
|
182
|
+
Most fast parsers build their own tree first and convert it into Ruby
|
|
183
|
+
objects second, paying for every string and container twice. nosj is
|
|
184
|
+
built on the [nosj](https://github.com/yaroslav/nosj) Rust crate, an
|
|
185
|
+
event parser with no tree of its own:
|
|
186
|
+
|
|
187
|
+
- **No intermediate tree.** The crate parses with NEON/SSE2/AVX2 SIMD
|
|
188
|
+
kernels and emits *events*; the extension builds interned hash keys,
|
|
189
|
+
strings, and containers directly on Ruby's heap during the parse,
|
|
190
|
+
with GC-safe value stacks and epoch-evicted key caches. Generation
|
|
191
|
+
walks Ruby objects once, streaming through fused scan-and-store
|
|
192
|
+
escape kernels.
|
|
193
|
+
- **Byte-exact floats.** Output reproduces the json gem's fpconv
|
|
194
|
+
(Grisu2) float format digit for digit—round-tripping is verified,
|
|
195
|
+
not assumed.
|
|
196
|
+
- **PGO everywhere.** Local builds, CI, and every precompiled platform
|
|
197
|
+
gem train on the benchmark corpus before the shipping compile; the
|
|
198
|
+
precompiled binaries use portable codegen with SIMD tiers detected at
|
|
199
|
+
runtime.
|
|
200
|
+
|
|
201
|
+
## Development
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
bundle exec rake compile # build the extension (applies a PGO profile if present)
|
|
205
|
+
bundle exec rake spec # the gem-parity suite
|
|
206
|
+
bundle exec rake bench # PGO retrain + the parity-gated sweep vs the json gem
|
|
207
|
+
bundle exec rake bench:fast # the sweep without retraining
|
|
208
|
+
bundle exec rake "bench:ips[twitter]" # multi-gem shoot-out (benchmark-ips); no args = full corpus
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT. The underlying Rust crate is `MIT AND BSL-1.0 AND Apache-2.0`; its
|
|
214
|
+
NOTICE file itemizes the derived components.
|