Skip to content

Commit 8c07e1f

Browse files
authored
Merge pull request #27 from lpgauth/feat/0.2-vendored-sonic
0.2.0: fused decoder + bounded parser (vendored sonic-rs)
2 parents 38fb5e0 + 4996b48 commit 8c07e1f

67 files changed

Lines changed: 20638 additions & 540 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ The script reads the version from `mix.exs`, creates a git tag, waits for the re
3131

3232
## Architecture
3333

34-
Torque is a high-performance JSON library for Elixir using Rustler NIFs backed by sonic-rs (SIMD-accelerated JSON).
34+
Torque is a high-performance JSON library for Elixir using Rustler NIFs backed by sonic-rs (SIMD-accelerated JSON). sonic-rs is **vendored** under `native/sonic-rs/` with a minimal patch (see that crate's `Cargo.toml`): its native push-based `JsonVisitor` is made public, and its DOM parser is capped at 128 nesting levels so deeply nested input returns an error instead of overflowing the stack.
3535

3636
### Decoding Strategies
3737

38-
1. **Parse + Get**`parse/1` returns an opaque reference to a parsed document. `get/2,3` extracts fields by JSON Pointer (RFC 6901) path. `get_many/2` extracts multiple fields in a single NIF call. Ideal when only a subset of fields is needed.
38+
1. **Parse + Get**`parse/1` returns an opaque reference to a parsed document (`sonic_rs::Value`). `get/2,3` extracts fields by JSON Pointer (RFC 6901) path via `value_to_term`. `get_many/2` extracts multiple fields in a single NIF call. Ideal when only a subset of fields is needed.
3939

40-
2. **Full decode**`decode/1` converts an entire JSON binary into Elixir terms in one pass.
40+
2. **Full decode**`decode/1` builds Erlang terms directly during the SIMD parse by implementing sonic-rs's native `JsonVisitor` (`native_decode.rs`): single pass, no intermediate `Value`, zero-copy sub-binaries for unescaped strings.
4141

4242
### Encoding
4343

4444
`encode/1` walks Elixir terms directly (no intermediate representation) and writes JSON bytes to a buffer. Supports maps (atom/binary keys), lists, numbers, booleans, nil, and jiffy-style `{proplist}` tuples.
4545

4646
### Scheduler Awareness
4747

48-
Inputs larger than 10 KB are automatically dispatched to dirty CPU schedulers to avoid blocking normal BEAM schedulers. The `get/2` NIF always runs on a normal scheduler (sub-microsecond pointer traversal).
48+
Inputs larger than 20 KB are automatically dispatched to dirty CPU schedulers to avoid blocking normal BEAM schedulers. The `get/2` NIF always runs on a normal scheduler (sub-microsecond pointer traversal).
4949

5050
### Type Conversion
5151

@@ -65,6 +65,8 @@ Inputs larger than 10 KB are automatically dispatched to dirty CPU schedulers to
6565
- `lib/torque/native.ex` — RustlerPrecompiled NIF stubs (set `TORQUE_BUILD=true` to compile from source)
6666
- `native/torque_nif/src/lib.rs` — NIF registration, `ParsedDocument` resource
6767
- `native/torque_nif/src/decoder.rs` — parse, get, get_many, decode NIFs
68+
- `native/torque_nif/src/native_decode.rs` — fused decoder; builds terms during the SIMD parse via sonic-rs's `JsonVisitor`
6869
- `native/torque_nif/src/encoder.rs` — direct term-walking JSON encoder
69-
- `native/torque_nif/src/types.rs` — sonic_rs Value → Erlang term conversion
70+
- `native/torque_nif/src/types.rs` — sonic_rs Value → Erlang term conversion (used by get/get_many)
7071
- `native/torque_nif/src/atoms.rs` — cached atoms (ok, error, nil, no_such_field, nesting_too_deep, unsupported_type, non_finite_float, invalid_key, malformed_proplist, invalid_utf8)
72+
- `native/sonic-rs/` — vendored, Torque-patched sonic-rs (native `JsonVisitor` exposed + DOM recursion-depth limit)

Cargo.lock

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
resolver = "2"
33
members = ["native/torque_nif"]
44

5+
# Use the vendored, Torque-patched sonic-rs (native push-based visitor exposed +
6+
# DOM recursion-depth limit). See native/sonic-rs/Cargo.toml.
7+
[patch.crates-io]
8+
sonic-rs = { path = "native/sonic-rs" }
9+
510
[profile.release]
611
opt-level = 3
712
lto = "fat"

README.md

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
High-performance JSON library for Elixir via [Rustler](https://github.com/rustler-magic/rustler) NIFs, powered by [sonic-rs](https://github.com/cloudwego/sonic-rs) (SIMD-accelerated).
44

5-
Torque provides among the fastest JSON encoding and decoding available in the BEAM ecosystem, with a selective field extraction API for workloads that only need a subset of fields from each document.
5+
Torque provides the fastest JSON encoding and decoding available in the BEAM ecosystem, with a selective field extraction API for workloads that only need a subset of fields from each document.
66

77
## Features
88

@@ -20,7 +20,7 @@ Add to your `mix.exs`:
2020
```elixir
2121
def deps do
2222
[
23-
{:torque, "~> 0.1.10"}
23+
{:torque, "~> 0.2.0"}
2424
]
2525
end
2626
```
@@ -149,7 +149,7 @@ Functions return `{:error, reason}` tuples (or raise `ArgumentError` for bang/io
149149

150150
| Atom | Returned by | Meaning |
151151
|------|-------------|---------|
152-
| `:nesting_too_deep` | `decode/1`, `get/2`, `get_many/2` | Document exceeds 128 nesting levels |
152+
| `:nesting_too_deep` | `decode/1`, `parse/1`, `get/2`, `get_many/2` | Document exceeds 128 nesting levels |
153153

154154
`parse/1` and `decode/1` also return `{:error, binary}` with a message from sonic-rs for malformed JSON.
155155

@@ -172,82 +172,80 @@ Apple M2 Pro, OTP 29, Elixir 1.20:
172172

173173
| Library | ips | mean | median | p99 | memory |
174174
|---|---|---|---|---|---|
175-
| **glazer** | **371.2K** | **2.69 μs** | **2.50 μs** | 6.42 μs | 1.56 KB |
176-
| **torque** | 284.3K | 3.52 μs | 3.42 μs | **5.71 μs** | 1.56 KB |
177-
| **jiffy** | 215.7K | 4.64 μs | 4.21 μs | 8.71 μs | **1.55 KB** |
178-
| **simdjsone** | 194.9K | 5.13 μs | 4.96 μs | 8.96 μs | 1.59 KB |
179-
| **otp json** | 145.6K | 6.87 μs | 6.58 μs | 11.63 μs | 7.73 KB |
180-
| **jason** | 113.4K | 8.82 μs | 8.42 μs | 16.08 μs | 9.54 KB |
175+
| **torque** | **392.8K** | **2.55 μs** | **2.46 μs** | **4.25 μs** | 1.56 KB |
176+
| **glazer** | 298.9K | 3.35 μs | 3.00 μs | 8.92 μs | 1.56 KB |
177+
| **jiffy** | 208.8K | 4.79 μs | 4.33 μs | 10.13 μs | **1.55 KB** |
178+
| **simdjsone** | 181.6K | 5.51 μs | 5.29 μs | 9.88 μs | 1.59 KB |
179+
| **otp json** | 125.2K | 7.99 μs | 7.71 μs | 13.08 μs | 7.73 KB |
180+
| **jason** | 92.6K | 10.80 μs | 10.13 μs | 22.33 μs | 9.54 KB |
181181

182182
### Decode (750 KB Twitter)
183183

184184
| Library | ips | mean | median | p99 | memory |
185185
|---|---|---|---|---|---|
186-
| **glazer** | **592.9** | **1.69 ms** | **1.50 ms** | **2.21 ms** | 1.58 KB |
187-
| **torque** | 543.6 | 1.84 ms | 1.66 ms | 2.36 ms | **1.57 KB** |
188-
| **simdjsone** | 438.4 | 2.28 ms | 1.80 ms | 3.40 ms | 1.59 KB |
189-
| **jiffy** | 311.5 | 3.21 ms | 3.31 ms | 3.71 ms | 2.30 MB |
190-
| **otp json** | 213.1 | 4.69 ms | 4.73 ms | 5.68 ms | 2.48 MB |
191-
| **jason** | 152.0 | 6.58 ms | 6.58 ms | 6.88 ms | 3.52 MB |
186+
| **torque** | **655.1** | **1.53 ms** | **1.32 ms** | **2.16 ms** | **1.57 KB** |
187+
| **glazer** | 590.8 | 1.69 ms | 1.53 ms | 2.56 ms | 1.58 KB |
188+
| **simdjsone** | 392.5 | 2.55 ms | 2.10 ms | 4.53 ms | 1.59 KB |
189+
| **jiffy** | 286.3 | 3.49 ms | 3.59 ms | 3.92 ms | 2.30 MB |
190+
| **otp json** | 165.2 | 6.05 ms | 5.42 ms | 16.57 ms | 2.48 MB |
191+
| **jason** | 142.1 | 7.04 ms | 7.01 ms | 8.15 ms | 3.54 MB |
192192

193193
### Encode (1.2 KB OpenRTB)
194194

195195
| Library | ips | mean | median | p99 | memory |
196196
|---|---|---|---|---|---|
197-
| **torque** [proplist() :: iodata()] | **1260K** | **0.79 μs** | **0.71 μs** | **0.92 μs** | **64 B** |
198-
| **torque** [proplist() :: binary()] | 1250K | 0.80 μs | 0.75 μs | **0.92 μs** | 88 B |
199-
| **otp json** [map() :: iodata()] | 1180K | 0.85 μs | 0.79 μs | 1.25 μs | 3928 B |
200-
| **torque** [map() :: binary()] | 1050K | 0.96 μs | 0.88 μs | 1.04 μs | 88 B |
201-
| **glazer** [map() :: binary()] | 1020K | 0.98 μs | 0.83 μs | 1.63 μs | **64 B** |
202-
| **torque** [map() :: iodata()] | 1010K | 0.99 μs | 0.88 μs | 1.96 μs | **64 B** |
203-
| **jiffy** [proplist() :: iodata()] | 730K | 1.37 μs | 1.17 μs | 1.63 μs | 120 B |
204-
| **jason** [map() :: iodata()] | 610K | 1.63 μs | 1.50 μs | 2.63 μs | 3848 B |
205-
| **jiffy** [map() :: iodata()] | 590K | 1.71 μs | 1.42 μs | 3.71 μs | 824 B |
206-
| **simdjsone** [proplist() :: iodata()] | 450K | 2.23 μs | 2.04 μs | 2.75 μs | 184 B |
207-
| **simdjsone** [map() :: iodata()] | 370K | 2.67 μs | 2.42 μs | 4.67 μs | 888 B |
208-
| **jason** [map() :: binary()] | 290K | 3.48 μs | 2.42 μs | 20.04 μs | 3912 B |
197+
| **torque** [proplist() :: iodata()] | **1280K** | **0.78 μs** | **0.71 μs** | **0.88 μs** | **64 B** |
198+
| **torque** [proplist() :: binary()] | 1190K | 0.84 μs | 0.75 μs | 1.63 μs | 88 B |
199+
| **glazer** [map() :: binary()] | 1090K | 0.92 μs | 0.83 μs | 1.13 μs | **64 B** |
200+
| **otp json** [map() :: iodata()] | 1080K | 0.92 μs | 0.79 μs | 2.04 μs | 3928 B |
201+
| **torque** [map() :: iodata()] | 1050K | 0.95 μs | 0.88 μs | 1.13 μs | **64 B** |
202+
| **torque** [map() :: binary()] | 1040K | 0.96 μs | 0.88 μs | 1.13 μs | 88 B |
203+
| **jiffy** [proplist() :: iodata()] | 730K | 1.37 μs | 1.13 μs | 2.33 μs | 120 B |
204+
| **jiffy** [map() :: iodata()] | 620K | 1.62 μs | 1.42 μs | 2.04 μs | 824 B |
205+
| **jason** [map() :: iodata()] | 610K | 1.63 μs | 1.50 μs | 2.88 μs | 3848 B |
206+
| **simdjsone** [proplist() :: iodata()] | 440K | 2.25 μs | 2.04 μs | 2.88 μs | 184 B |
207+
| **jason** [map() :: binary()] | 380K | 2.66 μs | 2.38 μs | 6.29 μs | 3912 B |
208+
| **simdjsone** [map() :: iodata()] | 370K | 2.69 μs | 2.42 μs | 4.79 μs | 888 B |
209209

210210
### Encode (750 KB Twitter)
211211

212212
| Library | ips | mean | median | p99 | memory |
213213
|---|---|---|---|---|---|
214-
| **torque** [proplist() :: binary()] | **1245.8** | **0.80 ms** | **0.79 ms** | **0.98 ms** | 88 B |
215-
| **torque** [proplist() :: iodata()] | 1241.5 | 0.81 ms | **0.79 ms** | **0.98 ms** | **64 B** |
216-
| **torque** [map() :: binary()] | 1121.0 | 0.89 ms | 0.88 ms | 1.05 ms | 88 B |
217-
| **torque** [map() :: iodata()] | 1096.7 | 0.91 ms | 0.90 ms | 1.08 ms | **64 B** |
218-
| **glazer** [map() :: binary()] | 1001.0 | 1.00 ms | 0.99 ms | 1.10 ms | **64 B** |
219-
| **jiffy** [proplist() :: iodata()] | 544.7 | 1.84 ms | 1.83 ms | 2.04 ms | 37.7 KB |
220-
| **jiffy** [map() :: iodata()] | 402.6 | 2.48 ms | 2.63 ms | 2.97 ms | 1.06 MB |
221-
| **otp json** [map() :: iodata()] | 277.6 | 3.60 ms | 3.40 ms | 4.71 ms | 5.40 MB |
222-
| **simdjsone** [proplist() :: iodata()] | 258.6 | 3.87 ms | 3.81 ms | 5.82 ms | 37.7 KB |
223-
| **jason** [map() :: iodata()] | 240.0 | 4.17 ms | 3.91 ms | 6.22 ms | 4.96 MB |
224-
| **simdjsone** [map() :: iodata()] | 218.6 | 4.57 ms | 4.66 ms | 5.31 ms | 1.06 MB |
225-
| **jason** [map() :: binary()] | 129.6 | 7.72 ms | 7.71 ms | 8.34 ms | 4.96 MB |
214+
| **torque** [proplist() :: iodata()] | **1228.7** | **0.81 ms** | **0.80 ms** | 1.00 ms | **64 B** |
215+
| **torque** [proplist() :: binary()] | 1226.6 | 0.82 ms | **0.80 ms** | **0.99 ms** | 88 B |
216+
| **torque** [map() :: binary()] | 1098.9 | 0.91 ms | 0.89 ms | 1.17 ms | 88 B |
217+
| **torque** [map() :: iodata()] | 1077.4 | 0.93 ms | 0.91 ms | 1.12 ms | **64 B** |
218+
| **glazer** [map() :: binary()] | 981.9 | 1.02 ms | 1.00 ms | 1.28 ms | **64 B** |
219+
| **jiffy** [proplist() :: iodata()] | 536.5 | 1.86 ms | 1.82 ms | 2.98 ms | 37.7 KB |
220+
| **jiffy** [map() :: iodata()] | 439.8 | 2.27 ms | 2.26 ms | 2.48 ms | 1.06 MB |
221+
| **otp json** [map() :: iodata()] | 267.3 | 3.74 ms | 4.08 ms | 6.51 ms | 5.40 MB |
222+
| **jason** [map() :: iodata()] | 260.6 | 3.84 ms | 3.58 ms | 6.07 ms | 4.96 MB |
223+
| **simdjsone** [proplist() :: iodata()] | 255.6 | 3.91 ms | 3.78 ms | 6.60 ms | 37.7 KB |
224+
| **simdjsone** [map() :: iodata()] | 214.7 | 4.66 ms | 4.64 ms | 6.61 ms | 1.06 MB |
225+
| **jason** [map() :: binary()] | 136.6 | 7.32 ms | 7.10 ms | 9.02 ms | 4.96 MB |
226226

227227
### Parse (1.2 KB OpenRTB)
228228

229229
| Library | ips | mean | median | p99 |
230230
|---|---|---|---|---|
231-
| **torque** parse(unique_keys) | **570.0K** | **1.75 μs** | 1.33 μs | 5.79 μs |
232-
| **torque** parse | 545.2K | 1.83 μs | 1.33 μs | 6.08 μs |
233-
| **simdjsone** parse | 360.1K | 2.78 μs | **1.17 μs** | **5.63 μs** |
231+
| **torque** parse(unique_keys) | **572.1K** | **1.75 μs** | 1.33 μs | **5.75 μs** |
232+
| **torque** parse | 549.5K | 1.82 μs | 1.33 μs | 6.08 μs |
233+
| **simdjsone** parse | 320.4K | 3.12 μs | **1.21 μs** | 5.96 μs |
234234

235-
### Get (5 fields) (1.2 KB OpenRTB)
235+
### Extract 5 fields from raw JSON (1.2 KB OpenRTB)
236236

237-
| Library | ips | mean | median | p99 | memory |
238-
|---|---|---|---|---|---|
239-
| **glazer** find (decoded) | **2.83M** | **353 ns** | **333 ns** | **459 ns** | 424 B |
240-
| **torque** get_many_nil (unique_keys) | 2.43M | 411 ns | 375 ns | 500 ns | **240 B** |
241-
| **torque** get_many (unique_keys) | 2.36M | 423 ns | 375 ns | 500 ns | 360 B |
242-
| **torque** get_many_nil | 2.13M | 469 ns | 458 ns | 584 ns | **240 B** |
243-
| **torque** get_many | 2.04M | 491 ns | 458 ns | 625 ns | 360 B |
244-
| **simdjsone** get | 1.76M | 568 ns | 458 ns | 1000 ns | 384 B |
245-
| **torque** get (unique_keys) | 1.56M | 641 ns | 584 ns | 792 ns | 384 B |
246-
| **torque** get | 1.41M | 709 ns | 667 ns | 916 ns | 384 B |
247-
248-
`glazer find` runs over a fully decoded term (decode cost excluded, as parse
249-
cost is excluded for `torque`/`simdjsone`); glazer has no parse-to-handle API,
250-
so it is absent from the parse benchmark.
237+
End-to-end cost of pulling 5 fields out of a JSON blob: `parse` + `get`
238+
(torque, simdjsone) vs `decode` + `find` (glazer has no lazy handle, so it must
239+
fully decode first). This is the apples-to-apples version of "get" — torque's
240+
selective extraction skips materializing the whole document.
241+
242+
| Library | ips | mean | median | p99 |
243+
|---|---|---|---|---|
244+
| **torque** parse(unique_keys) + get_many | **443.3K** | **2.26 μs** | 1.71 μs | 6.67 μs |
245+
| **torque** parse + get_many | 425.3K | 2.35 μs | 1.79 μs | **6.04 μs** |
246+
| **torque** parse + get x5 | 419.9K | 2.38 μs | 2.00 μs | 6.92 μs |
247+
| **simdjsone** parse + get x5 | 371.7K | 2.69 μs | **1.67 μs** | 7.00 μs |
248+
| **glazer** decode + find x5 | 317.0K | 3.15 μs | 2.83 μs | 7.71 μs |
251249

252250
Run benchmarks locally:
253251

@@ -257,7 +255,7 @@ MIX_ENV=bench mix run bench/torque_bench.exs
257255

258256
## Limitations
259257

260-
- **Nesting depth**: JSON documents nested deeper than 128 levels return `{:error, :nesting_too_deep}` from `decode/1`, `get/2`, `get_many/2`, and `encode/1` rather than crashing the VM. Real-world documents are never this deep; the limit exists to prevent stack overflow in the NIF (the dirty CPU scheduler, used for inputs over 20 KB, has a small stack).
258+
- **Nesting depth**: JSON documents nested deeper than 128 levels return `{:error, :nesting_too_deep}` from `decode/1`, `parse/1`, `get/2`, `get_many/2`, and `encode/1` rather than crashing the VM. Real-world documents are never this deep; the limit exists to prevent stack overflow in the NIF (the dirty CPU scheduler, used for inputs over 20 KB, has a small stack).
261259

262260
## License
263261

bench/torque_bench.exs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,8 @@ Benchee.run(
403403
] ++ ci_formatters
404404
)
405405

406-
{:ok, pre_doc} = Torque.parse(sample_json)
407-
{:ok, pre_doc_uk} = Torque.parse(sample_json, unique_keys: true)
408-
pre_ref = :simdjson.parse(sample_json)
409-
410-
# glazer has no parse-to-handle API; glazer:find/2 runs over a decoded term, so
411-
# it appears under "get" (decode once, reuse) rather than "parse".
412-
glazer_decoded = :glazer_json.decode(sample_json)
413-
406+
# Pre-compiled jq paths for glazer (application-time constants, like torque's
407+
# JSON pointer strings) — reused inside the timed function below.
414408
glazer_paths =
415409
Enum.map(
416410
[".id", ".site.domain", ".device.ip", ".device.geo.country", ".user.id"],
@@ -438,25 +432,40 @@ Benchee.run(
438432
] ++ ci_formatters
439433
)
440434

441-
BenchGroup.set("Get (5 fields) — 1.2 KB OpenRTB")
442-
IO.puts("\n=== GET BENCHMARK ===\n")
435+
BenchGroup.set("Extract 5 fields — 1.2 KB OpenRTB")
436+
IO.puts("\n=== EXTRACT 5 FIELDS BENCHMARK ===\n")
443437

438+
# End-to-end from raw JSON: each library does its full setup plus 5 extractions,
439+
# so the comparison is apples-to-apples — torque/simdjsone parse + get, glazer
440+
# decode + find (it has no lazy handle). memory_time is 0 because simdjsone's
441+
# parse resource is freed during Benchee's memory runs and its destructor
442+
# use-after-frees (segfault).
444443
Benchee.run(
445444
%{
446-
"glazer find (decoded)" => fn ->
447-
for p <- glazer_paths, do: :glazer.find(glazer_decoded, p)
445+
"glazer decode + find x5" => fn ->
446+
d = :glazer_json.decode(sample_json)
447+
for p <- glazer_paths, do: :glazer.find(d, p)
448+
end,
449+
"simdjsone parse + get x5" => fn ->
450+
r = :simdjson.parse(sample_json)
451+
for f <- fields, do: :simdjson.get(r, f)
452+
end,
453+
"torque parse + get x5" => fn ->
454+
{:ok, doc} = Torque.parse(sample_json)
455+
for f <- fields, do: Torque.get(doc, f)
448456
end,
449-
"simdjsone get" => fn -> for f <- fields, do: :simdjson.get(pre_ref, f) end,
450-
"torque get" => fn -> for f <- fields, do: Torque.get(pre_doc, f) end,
451-
"torque get_many" => fn -> Torque.get_many(pre_doc, fields) end,
452-
"torque get_many_nil" => fn -> Torque.get_many_nil(pre_doc, fields) end,
453-
"torque get (unique_keys)" => fn -> for f <- fields, do: Torque.get(pre_doc_uk, f) end,
454-
"torque get_many (unique_keys)" => fn -> Torque.get_many(pre_doc_uk, fields) end,
455-
"torque get_many_nil (unique_keys)" => fn -> Torque.get_many_nil(pre_doc_uk, fields) end
457+
"torque parse + get_many" => fn ->
458+
{:ok, doc} = Torque.parse(sample_json)
459+
Torque.get_many(doc, fields)
460+
end,
461+
"torque parse(unique_keys) + get_many" => fn ->
462+
{:ok, doc} = Torque.parse(sample_json, unique_keys: true)
463+
Torque.get_many(doc, fields)
464+
end
456465
},
457466
warmup: 2,
458467
time: 5,
459-
memory_time: 2,
468+
memory_time: 0,
460469
percentiles: [50, 95, 99],
461470
formatters:
462471
[

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Torque.MixProject do
22
use Mix.Project
33

4-
@version "0.1.10"
4+
@version "0.2.0"
55
@source_url "https://github.com/lpgauth/torque"
66

77
def project do

native/sonic-rs/Cargo.toml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Vendored copy of sonic-rs 0.3.17 with a minimal Torque patch:
2+
# * `JsonVisitor` and `RawStr` made public + re-exported
3+
# * `parse_into_visitor` entry added, so Torque can build Erlang terms
4+
# directly during the native SIMD parse (one pass, zero-copy strings)
5+
# instead of going through the slower serde path
6+
# * a recursion-depth limit added to the DOM parser so deeply nested input
7+
# returns an error instead of overflowing the stack and crashing the VM
8+
# Upstream: https://github.com/cloudwego/sonic-rs (Apache-2.0)
9+
10+
# Standalone manifest so this is not pulled into the torque workspace.
11+
[workspace]
12+
13+
[package]
14+
edition = "2021"
15+
name = "sonic-rs"
16+
version = "0.3.17"
17+
authors = ["Volo Team <volo@cloudwego.io>"]
18+
build = false
19+
autobins = false
20+
autoexamples = false
21+
autotests = false
22+
autobenches = false
23+
description = "Sonic-rs is a fast Rust JSON library based on SIMD (vendored, patched for Torque)"
24+
license = "Apache-2.0"
25+
repository = "https://github.com/cloudwego/sonic-rs"
26+
27+
[lib]
28+
name = "sonic_rs"
29+
path = "src/lib.rs"
30+
31+
[dependencies]
32+
bumpalo = "3.13"
33+
bytes = "1.9"
34+
cfg-if = "1.0"
35+
faststr = { version = "0.2", features = ["serde"] }
36+
itoa = "1.0"
37+
ref-cast = "1.0"
38+
ryu = "1.0"
39+
serde = { version = "1.0", features = ["rc", "derive"] }
40+
simdutf8 = "0.1"
41+
sonic-number = "0.1"
42+
sonic-simd = "0.1"
43+
thiserror = "2.0"
44+
45+
[features]
46+
arbitrary_precision = []
47+
default = []
48+
sort_keys = []
49+
use_raw = []
50+
utf8_lossy = []

0 commit comments

Comments
 (0)