Current section

31 Versions

Jump to

Compare versions

20 files changed
+2869 additions
-1638 deletions
  @@ -8,6 +8,13 @@ DEBUG ?= 0
8 8 REBAR ?= rebar3
9 9 APP := $(shell sed -nE 's/^\{application, ([a-zA-Z0-9_]+),.*/\1/p' src/*.app.src | head -n1)
10 10
11 + OPTIMIZE ?= 0
12 + ifneq ($(filter $(OPTIMIZE),1 true),)
13 + override OPTIMIZE := 1
14 + else
15 + override OPTIMIZE := 0
16 + endif
17 +
11 18 all: compile
12 19
13 20 help:
  @@ -23,6 +30,9 @@ help:
23 30 @echo ""
24 31 @echo "Development:"
25 32 @echo " test Run eunit test suite"
33 + @echo " cover Run eunit with coverage analysis and print a summary"
34 + @echo " check Run xref and dialyzer"
35 + @echo " dialyzer Run dialyzer"
26 36 @echo " memcheck Build with ASan (-fsanitize=address) and run eunit (leak=1 adds LSan)"
27 37 @echo " benchmark Run benchmarks via mix bench"
28 38 @echo " deps Fetch mix dependencies"
  @@ -38,6 +48,7 @@ help:
38 48 @echo " ASAN=1 Build with AddressSanitizer (implied by memcheck)"
39 49 @echo " leak=1 Enable LeakSanitizer during memcheck (off by default)"
40 50 @echo " VERBOSE=1 Show full compiler command lines"
51 + @echo " OPTIMIZE=1 Make all/compile run the PGO 'optimize' build (same as 'make optimize')"
41 52
42 53 nif: $(PRIV_DIR)/glazer.so
43 54
  @@ -45,8 +56,12 @@ $(PRIV_DIR)/glazer.so: $(wildcard c_src/*.cpp c_src/*.hpp)
45 56 @$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) DEBUG=$(DEBUG) \
46 57 $(if $(VERBOSE),VERBOSE=1,) --no-print-directory
47 58
59 + ifeq ($(OPTIMIZE),1)
60 + compile: optimize
61 + else
48 62 compile: $(PRIV_DIR)/glazer.so
49 63 $(REBAR) compile
64 + endif
50 65
51 66 clean:
52 67 $(REBAR) clean
  @@ -78,7 +93,8 @@ else
78 93 DETECT_LEAKS := 1
79 94 endif
80 95
81 - check dialyzer:
96 + check:
97 + $(REBAR) xref
82 98 $(REBAR) dialyzer
83 99
84 100 memcheck:
  @@ -110,8 +126,8 @@ optimize:
110 126 @echo "==> PGO step 1/3: build instrumented binary"
111 127 @$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) PGO=generate clean all
112 128 @$(REBAR) compile
113 - @echo "==> PGO step 2/3: collect profile data via test suite"
114 - MIX_ENV=bench mix bench-json 1>/dev/null
129 + @echo "==> PGO step 2/3: collect profile data"
130 + @./bin/pgo-profile.es
115 131 @echo "==> PGO step 3/3: rebuild with profile data"
116 132 @rm -f $(OBJ_DIR)/glazer_nif.o $(PRIV_DIR)/glazer.so
117 133 @$(MAKE) -C c_src PRIV_DIR=$(PRIV_DIR) OBJ_DIR=$(OBJ_DIR) PGO=use all
  @@ -131,6 +147,9 @@ deprecate:
131 147 fi
132 148 $(REBAR) hex retire $(APP) $(vsn) deprecated --message Deprecated
133 149
150 + cover:
151 + $(REBAR) cover --verbose
152 +
134 153 bump-version:
135 154 @FILE=$$(ls -1 src/*.app.src | head -n1); \
136 155 CURRENT=$$(grep -m1 '{vsn,' $$FILE | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/'); \
  @@ -148,5 +167,5 @@ bump-version:
148 167 git commit -am "Bump version to $${NEW}"; \
149 168 fi
150 169
151 - .PHONY: all help doc compile clean distclean test memcheck nif \
170 + .PHONY: all help doc compile clean distclean test cover check dialyzer memcheck nif \
152 171 optimize benchmark bench publish deprecate bump-version
  @@ -18,15 +18,14 @@ formats.
18 18
19 19 ## Performance
20 20
21 - - **[JSON](#performance-1)**: faster encoding than every other library
22 - benchmarked, and roughly on par with `torque` (Rust `sonic-rs` NIF) on
23 - decoding — both well ahead of `simdjsone`, `jiffy`, and the pure-Elixir
24 - libraries `jason`, `thoas`, `euneus`, and OTP's built-in `json`.
25 - - **[YAML](#benchmarking-yaml)**: an order of magnitude faster than
26 - `yaml_rustler` and `fast_yaml`, and ~10-100x faster than the pure-Erlang
27 - `yamerl`/`ymlr`.
28 - - **[CSV](#benchmarking-csv)**: 2-20x faster than `nimble_csv`, and tens to
29 - hundreds of times faster than `csv` and `erl_csv` (which times out on
21 + - **[JSON](#performance-1)**: faster than every other library benchmarked on
22 + both encoding and decoding — consistently ~25–40% ahead of `torque`
23 + (Rust `sonic-rs` NIF), and well ahead of `simdjsone`, `jiffy`, and the
24 + pure-Elixir libraries `jason`, `thoas`, `euneus`, and OTP's built-in `json`.
25 + - **[YAML](#benchmarking-yaml)**: 2–7× faster than `yaml_rustler` and
26 + `fast_yaml`, and ~25–75× faster than the pure-Erlang `yamerl`/`ymlr`.
27 + - **[CSV](#benchmarking-csv)**: 4–12× faster than `nimble_csv`, and tens to
28 + hundreds of times faster than `csv` and `erl_csv` (which time out on
30 29 large inputs).
31 30
32 31 <img src="assets/bench_small.svg" width="100%" alt="Small file benchmarks (JSON/YAML/CSV)"/>
  @@ -49,14 +48,17 @@ Benchmark tables:
49 48 (including bignums), floats, booleans, and `null`
50 49 - Encoding Erlang terms straight to JSON, including big integers
51 50 - Incremental/streaming decoding of partial input (e.g. NDJSON over a
52 - socket) via `json_stream_decoder/0,1`, `json_stream_feed/2`, `json_stream_eof/1`
51 + socket) via `stream_decoder/0,1`, `stream_feed/2`, `stream_eof/1`
53 52 - Configurable representation of JSON `null` and JSON object keys
54 - - `json_minify/1` and `json_prettify/1` helpers
53 + - `minify/1` and `prettify/1` helpers
55 54 - Standalone big-integer encode/decode helpers
56 55 (`encode_integer/1`, `decode_integer/1`, `try_decode_integer/1`)
57 - - `json_query/2,3`: run a [jq](https://jqlang.org/) filter over a JSON
56 + - `query/2,3`: run a [jq](https://jqlang.org/) filter over a JSON
58 57 document, returning decoded Erlang terms (requires `glazer` to be built
59 58 with `libjq` available — see [jq filter support](#jq-filter-support))
59 + - `glazer:find/2` and `glazer:compile_path/1`: look up value(s) in a
60 + decoded term using a small subset of jq path syntax (`.a.b[].c[0]`),
61 + with no `libjq` dependency
60 62
61 63 ### YAML
62 64
  @@ -68,10 +70,10 @@ Benchmark tables:
68 70
69 71 ### CSV
70 72
71 - - RFC 4180 CSV encoding/decoding via `csv_decode/1,2` and `csv_encode/1,2`,
73 + - RFC 4180 CSV encoding/decoding via `decode/1,2` and `encode/1,2`,
72 74 with optional header-row support
73 - - Incremental/streaming CSV decoding via `csv_stream_decoder/0,1`,
74 - `csv_stream_feed/2`, `csv_stream_eof/1`
75 + - Incremental/streaming CSV decoding via `stream_decoder/0,1`,
76 + `stream_feed/2`, `stream_eof/1`
75 77
76 78 ## Scope
77 79
  @@ -136,37 +138,38 @@ Once compiled, call it via the `:glazer` module from Elixir:
136 138
137 139 **Erlang:**
138 140 ```erlang
139 - 1> glazer:json_decode(~"{\"a\":1,\"b\":[true,null,3.5]}")
141 + 1> glazer_json:decode(~"{\"a\":1,\"b\":[true,null,3.5]}")
140 142 #{<<"a">> => 1,<<"b">> => [true,null,3.5]}
141 143 ```
142 144
143 145 **Elixir:**
144 146 ```elixir
145 - iex> :glazer.json_encode(%{"a" => 1, "b" => [true, :null, 3.5]})
147 + iex> :glazer_json.encode(%{"a" => 1, "b" => [true, :null, 3.5]})
146 148 "{\"a\":1,\"b\":[true,null,3.5]}"
147 149 ```
148 150
149 - Use the `use_nil`/`{null_term, nil}` option (see [JSON `null`](#json-null)
150 - below) to get idiomatic Elixir `nil` instead of the atom `:null`.
151 + Use the `use_nil`/`{null_term, nil}` option (see
152 + [Null term configuration](#null-term-configuration) below) to get idiomatic
153 + Elixir `nil` instead of the atom `:null`.
151 154
152 155 ## JSON
153 156
154 157 ### Usage
155 158
156 159 ```erlang
157 - 1> glazer:json_decode(<<"{\"a\":1,\"b\":[true,null,3.5]}">>).
160 + 1> glazer_json:decode(<<"{\"a\":1,\"b\":[true,null,3.5]}">>).
158 161 #{<<"a">> => 1, <<"b">> => [true, null, 3.5]}
159 162
160 - 2> glazer:json_encode(#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}).
163 + 2> glazer_json:encode(#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}).
161 164 <<"{\"a\":1,\"b\":[true,null,3.5]}">>
162 165
163 - 3> glazer:json_encode(#{a => 1}, [pretty]).
166 + 3> glazer_json:encode(#{a => 1}, [pretty]).
164 167 <<"{\n \"a\": 1\n}">>
165 168
166 - 4> glazer:json_minify(<<" { \"a\" : 1 } ">>).
169 + 4> glazer_json:minify(<<" { \"a\" : 1 } ">>).
167 170 {ok, <<"{\"a\":1}">>}
168 171
169 - 5> glazer:json_prettify(<<"{\"a\":1}">>).
172 + 5> glazer_json:prettify(<<"{\"a\":1}">>).
170 173 {ok, <<"{\n \"a\": 1\n}">>}
171 174 ```
172 175
  @@ -174,55 +177,55 @@ below) to get idiomatic Elixir `nil` instead of the atom `:null`.
174 177
175 178 For input that arrives in chunks — e.g. reading a large document
176 179 incrementally, or consuming newline-delimited JSON (NDJSON) from a
177 - socket or file — `json_stream_decoder/0,1` provides a small stateful
180 + socket or file — `stream_decoder/0,1` provides a small stateful
178 181 wrapper that buffers partial input and decodes each JSON value as soon
179 182 as it's complete, without re-parsing bytes you've already seen:
180 183
181 184 ```erlang
182 - 1> D0 = glazer:json_stream_decoder(),
183 - 2> {Vals1, D1} = glazer:json_stream_feed(D0, <<"{\"a\":1} {\"b\":">>),
185 + 1> D0 = glazer_json:stream_decoder(),
186 + 2> {Vals1, D1} = glazer_json:stream_feed(D0, <<"{\"a\":1} {\"b\":">>),
184 187 3> Vals1.
185 188 [#{<<"a">> => 1}]
186 189
187 - 4> {Vals2, D2} = glazer:json_stream_feed(D1, <<"2}">>),
190 + 4> {Vals2, D2} = glazer_json:stream_feed(D1, <<"2}">>),
188 191 5> Vals2.
189 192 [#{<<"b">> => 2}]
190 193
191 - 6> glazer:json_stream_eof(D2).
194 + 6> glazer_json:stream_eof(D2).
192 195 {ok, []}
193 196 ```
194 197
195 - `json_stream_feed/2` returns the list of values completed by the chunk just
198 + `stream_feed/2` returns the list of values completed by the chunk just
196 199 fed (possibly empty, possibly more than one if the chunk completes
197 200 several values) along with the updated decoder state to pass to the
198 - next call. Once the input is exhausted, call `json_stream_eof/1` to flush
201 + next call. Once the input is exhausted, call `stream_eof/1` to flush
199 202 any trailing bare scalar (numbers, strings, etc. have no closing
200 203 delimiter of their own) and surface an error if the buffer holds an
201 204 incomplete value:
202 205
203 206 ```erlang
204 - 1> D0 = glazer:json_stream_decoder(),
205 - 2> {[], D1} = glazer:json_stream_feed(D0, <<" 42">>),
206 - 3> glazer:json_stream_eof(D1).
207 + 1> D0 = glazer_json:stream_decoder(),
208 + 2> {[], D1} = glazer_json:stream_feed(D0, <<" 42">>),
209 + 3> glazer_json:stream_eof(D1).
207 210 {ok, [42]}
208 211 ```
209 212
210 - `json_stream_decoder/1` accepts the same options as `json_decode/2` (e.g.
213 + `stream_decoder/1` accepts the same options as `decode/2` (e.g.
211 214 `{keys, atom}`, `use_nil`) and applies them to every decoded value.
212 215
213 - A typical read loop calls `json_stream_feed/2` for each chunk while more data
214 - may still arrive, and `json_stream_eof/1` once the socket closes to flush any
216 + A typical read loop calls `stream_feed/2` for each chunk while more data
217 + may still arrive, and `stream_eof/1` once the socket closes to flush any
215 218 trailing value:
216 219
217 220 ```erlang
218 221 loop(Socket, D0) ->
219 222 case gen_tcp:recv(Socket, 0) of
220 223 {ok, Chunk} ->
221 - {Vals, D1} = glazer:json_stream_feed(D0, Chunk),
224 + {Vals, D1} = glazer_json:stream_feed(D0, Chunk),
222 225 handle_values(Vals),
223 226 loop(Socket, D1);
224 227 {error, closed} ->
225 - case glazer:json_stream_eof(D0) of
228 + case glazer_json:stream_eof(D0) of
226 229 {ok, Trailing} -> handle_values(Trailing);
227 230 {error, Reason} -> handle_truncated_stream(Reason)
228 231 end
  @@ -231,49 +234,51 @@ loop(Socket, D0) ->
231 234
232 235 #### Efficiency
233 236
234 - `json_stream_feed/2` only scans for value *boundaries* incrementally —
237 + `stream_feed/2` only scans for value *boundaries* incrementally —
235 238 the scanner carries a small resumable cursor (`scan_state()`) that
236 239 remembers how far it has already looked (nesting depth, whether it's
237 - inside a string, escape state, …), so each call to `json_scan/2` resumes
240 + inside a string, escape state, …), so each call to `scan/2` resumes
238 241 from where the previous one left off rather than re-walking the whole
239 242 buffer from byte zero. Once a complete value's end offset is known,
240 243 that slice is decoded exactly once via the same NIF-backed decoder
241 - used by `json_decode/2` — there's no intermediate tokenization or tree
244 + used by `decode/2` — there's no intermediate tokenization or tree
242 245 representation, and no byte is ever scanned or decoded twice. The only
243 246 buffering cost is concatenating newly-arrived chunks onto the
244 247 not-yet-complete tail of the input.
245 248
246 - This makes `json_stream_feed/2` well suited to byte-at-a-time or
249 + This makes `stream_feed/2` well suited to byte-at-a-time or
247 250 small-chunk feeding (e.g. consuming a `gen_tcp`/`gen_statem` socket
248 251 buffer as it fills) without the quadratic-rescan cost a naive
249 252 "concatenate and retry full decode" loop would incur on large or
250 253 slow-arriving documents.
251 254
252 - Under the hood, `json_stream_feed/2` is built on `json_scan/1,2` — a low-level
255 + Under the hood, `stream_feed/2` is built on `scan/1,2` — a low-level
253 256 primitive that scans a buffer for the byte offset where the next JSON
254 257 value ends (or reports that more input is needed) without doing a full
255 258 decode. It's exposed directly for callers that want to implement their
256 259 own framing/buffering strategy:
257 260
258 261 ```erlang
259 - 1> glazer:json_scan(<<"{\"a\":1} {\"b\":2}">>).
262 + 1> glazer_json:scan(<<"{\"a\":1} {\"b\":2}">>).
260 263 {complete, 7}
261 264
262 - 2> glazer:json_scan(<<"{\"a\":">>).
265 + 2> glazer_json:scan(<<"{\"a\":">>).
263 266 {incomplete, ScanState}
264 267
265 - 3> glazer:json_scan(<<"{\"a\":1}">>, ScanState).
268 + 3> glazer_json:scan(<<"{\"a\":1}">>, ScanState).
266 269 {complete, 7}
267 270 ```
268 271
269 - `json_stream_decoder/0,1`, `json_stream_feed/2`, `json_stream_eof/1` and
270 - `json_scan/1,2` are JSON-only — see [YAML streaming](#streaming-1) and
272 + `stream_decoder/0,1`, `stream_feed/2`, `stream_eof/1` and
273 + `scan/1,2` are JSON-only — see [YAML streaming](#streaming-1) and
271 274 [CSV streaming](#streaming-2) below for the other formats.
272 275
273 - ### JSON `null`
276 + ### Null term configuration
274 277
275 - By default, JSON `null` decodes to (and `null` encodes from) the atom
276 - `null`. This can be overridden:
278 + By default, JSON/YAML `null` decodes to (and `null` encodes from) the atom
279 + `null`, and this same atom is used as the default null term throughout the
280 + library (e.g. for the CSV `on_failure => null` field option). This can be
281 + overridden:
277 282
278 283 - Application-wide, via the `null` environment key — set this once in
279 284 the application's config and every call uses it as the default:
  @@ -289,11 +294,11 @@ By default, JSON `null` decodes to (and `null` encodes from) the atom
289 294 ```
290 295
291 296 - Per call, with the `use_nil` shorthand or the `{null_term, Atom}`
292 - option (see [Decode options](#decode-options-json_decode2) below).
297 + option (see [Decode options](#decode-options-glazer_jsondecode2) below).
293 298 Per-call options always take precedence over the application-wide
294 299 default.
295 300
296 - ### Decode options (`json_decode/2`)
301 + ### Decode options (`glazer_json:decode/2`)
297 302
298 303 | Option | Description |
299 304 |---|---|
  @@ -306,25 +311,25 @@ By default, JSON `null` decodes to (and `null` encodes from) the atom
306 311 | `dedupe_keys` | With `object_as_tuple`, eliminate duplicate object keys, keeping the last occurrence's value (and position) |
307 312
308 313 ```erlang
309 - 1> glazer:json_decode(<<"{\"a\":1}">>, [object_as_tuple]).
314 + 1> glazer_json:decode(<<"{\"a\":1}">>, [object_as_tuple]).
310 315 {[{<<"a">>, 1}]}
311 316
312 - 2> glazer:json_decode(<<"{\"a\":1}">>, [{keys, atom}]).
317 + 2> glazer_json:decode(<<"{\"a\":1}">>, [{keys, atom}]).
313 318 #{a => 1}
314 319
315 - 3> glazer:json_decode(<<"null">>, [use_nil]).
320 + 3> glazer_json:decode(<<"null">>, [use_nil]).
316 321 nil
317 322
318 - 4> glazer:json_decode(<<"null">>, [{null_term, undefined}]).
323 + 4> glazer_json:decode(<<"null">>, [{null_term, undefined}]).
319 324 undefined
320 325
321 - 5> glazer:json_decode(<<"{\"a\":1,\"a\":2}">>).
326 + 5> glazer_json:decode(<<"{\"a\":1,\"a\":2}">>).
322 327 #{<<"a">> => 2}
323 328
324 - 6> glazer:json_decode(<<"{\"a\":1,\"a\":2}">>, [object_as_tuple]).
329 + 6> glazer_json:decode(<<"{\"a\":1,\"a\":2}">>, [object_as_tuple]).
325 330 {[{<<"a">>, 1}, {<<"a">>, 2}]}
326 331
327 - 7> glazer:json_decode(<<"{\"a\":1,\"a\":2}">>, [object_as_tuple, dedupe_keys]).
332 + 7> glazer_json:decode(<<"{\"a\":1,\"a\":2}">>, [object_as_tuple, dedupe_keys]).
328 333 {[{<<"a">>, 2}]}
329 334 ```
330 335
  @@ -335,7 +340,7 @@ undefined
335 340 > `object_as_tuple`, duplicate keys are preserved as-is unless `dedupe_keys`
336 341 > is given.
337 342
338 - ### Encode options (`json_encode/2`)
343 + ### Encode options (`glazer_json:encode/2`)
339 344
340 345 | Option | Description |
341 346 |---|---|
  @@ -346,60 +351,64 @@ undefined
346 351 | `{null_term, Atom}` | Encode `Atom` as JSON `null` |
347 352
348 353 ```erlang
349 - 1> glazer:json_encode(#{a => 1}, [pretty]).
354 + 1> glazer_json:encode(#{a => 1}, [pretty]).
350 355 <<"{\n \"a\": 1\n}">>
351 356
352 - 2> glazer:json_encode(<<"héllo"/utf8>>, [uescape]).
357 + 2> glazer_json:encode(<<"héllo"/utf8>>, [uescape]).
353 358 <<"\"h\\u00e9llo\"">>
354 359
355 - 3> glazer:json_encode(nil, [use_nil]).
360 + 3> glazer_json:encode(nil, [use_nil]).
356 361 <<"null">>
357 362 ```
358 363
359 364 ### jq filter support
360 365
361 366 If [`libjq`](https://jqlang.org/) and its headers (`jq.h`/`jv.h`) are
362 - available when `glazer` is built, `json_query/2,3` runs a jq filter
367 + available when `glazer` is built, `query/2,3` runs a jq filter
363 368 program against a JSON document and returns one Erlang term per value
364 369 produced by the filter (decoded using the same options as
365 - `json_decode/2`):
370 + `decode/2`):
366 371
367 372 ```erlang
368 - 1> glazer:json_query(<<"{\"a\":[1,2,3]}">>, <<".a[]">>).
373 + 1> glazer_json:query(<<"{\"a\":[1,2,3]}">>, <<".a[]">>).
369 374 {ok, [1, 2, 3]}
370 375
371 - 2> glazer:json_query(<<"{\"a\":1}">>, <<".b">>).
376 + 2> glazer_json:query(<<"{\"a\":1}">>, <<".b">>).
372 377 {ok, [null]}
373 378
374 - 3> glazer:json_query(<<"{\"a\":{\"b\":2}}">>, <<".">>, [{keys, atom}]).
379 + 3> glazer_json:query(<<"{\"a\":{\"b\":2}}">>, <<".">>, [{keys, atom}]).
375 380 {ok, [#{a => #{b => 2}}]}
376 381
377 - 4> glazer:json_query(<<"not json">>, <<".">>).
382 + 4> glazer_json:query(<<"not json">>, <<".">>).
378 383 {error, invalid_input}
379 384
380 - 5> glazer:json_query(<<"{\"a\":1}">>, <<"bad syntax (((">>).
385 + 5> glazer_json:query(<<"{\"a\":1}">>, <<"bad syntax (((">>).
381 386 {error, jq_decode_error}
382 387 ```
383 388
384 - If `libjq` was not available at build time, `json_query/2,3` returns
389 + If `libjq` was not available at build time, `query/2,3` returns
385 390 `{error, jq_not_available}`. Build detection is automatic — `make` probes
386 391 for `jq.h`/`libjq` and only enables this feature if found, so `glazer`
387 392 still builds and works without `libjq` installed.
388 393
389 394 ### API
390 395
396 + All functions below are in `glazer_json`.
397 +
391 398 | Function | Description |
392 399 |---|---|
393 - | `json_decode/1`, `json_decode/2` | Decode a JSON binary or iolist to an Erlang term |
394 - | `json_try_decode/1`, `json_try_decode/2` | Decode a JSON binary or iolist, returning `{ok, Term}` or `{error, {parse_error, Msg}}` instead of raising |
395 - | `json_encode/1`, `json_encode/2` | Encode an Erlang term to a JSON binary |
396 - | `json_minify/1` | Remove unnecessary whitespace from a JSON document |
397 - | `json_prettify/1` | Pretty-print a JSON document with two-space indentation |
398 - | `json_scan/1`, `json_scan/2` | Scan a buffer for the end offset of the next complete JSON value |
399 - | `json_stream_decoder/0`, `json_stream_decoder/1` | Create an incremental-decode state for chunked input |
400 - | `json_stream_feed/2` | Feed a chunk to a stream decoder, returning completed values |
401 - | `json_stream_eof/1` | Flush a stream decoder at end-of-input |
402 - | `json_query/2`, `json_query/3` | Run a [jq](https://jqlang.org/) filter over a JSON document, returning `{ok, [Term]}` (requires `libjq`) |
400 + | `decode/1`, `decode/2` | Decode a JSON binary or iolist to an Erlang term |
401 + | `try_decode/1`, `try_decode/2` | Decode a JSON binary or iolist, returning `{ok, Term}` or `{error, {parse_error, Msg}}` instead of raising |
402 + | `encode/1`, `encode/2` | Encode an Erlang term to a JSON binary |
403 + | `minify/1` | Remove unnecessary whitespace from a JSON document |
404 + | `prettify/1` | Pretty-print a JSON document with two-space indentation |
405 + | `read_file/1`, `read_file/2` | Read a file and decode its contents as JSON |
406 + | `write_file/2`, `write_file/3` | Encode a term to JSON and write it to a file |
407 + | `scan/1`, `scan/2` | Scan a buffer for the end offset of the next complete JSON value |
408 + | `stream_decoder/0`, `stream_decoder/1` | Create an incremental-decode state for chunked input |
409 + | `stream_feed/2` | Feed a chunk to a stream decoder, returning completed values |
410 + | `stream_eof/1` | Flush a stream decoder at end-of-input |
411 + | `query/2`, `query/3` | Run a [jq](https://jqlang.org/) filter over a JSON document, returning `{ok, [Term]}` (requires `libjq`) |
403 412
404 413 ### Benchmarking JSON
405 414
  @@ -412,30 +421,28 @@ $ PARALLEL=2 make bench
412 421 ==> Running benchmarks with parallelism: 2
413 422
414 423 (numbers in µs)
415 - JSON twitter (616.7K) twitter2 (758.0K) openrtb (1.2K) esad (1.3K) small (0.1K)
416 - decode encode decode encode decode encode decode encode decode encode
424 + JSON twitter (616.7K) twitter2 (758.0K) openrtb (1.2K) esad (1.3K) small (0.1K)
425 + decode encode decode encode decode encode decode encode decode encode
417 426 -------------------------------------------------------------------------------------------------------------
418 - glazer 3876.2 1205.8 4716.9 1971.3 9.1 3.7 5.8 2.9 0.9 0.8
419 - torque 5140.3 1333.7 4468.8 4411.4 9.3 5.5 4.9 3.5 1.8 1.4
420 - simdjsone 4652.9 3564.3 7426.9 6236.6 10.2 13.5 7.7 8.4 1.2 2.1
421 - jiffy 5921.4 2547.3 7974.6 4654.6 11.1 11.4 7.9 6.2 1.8 1.9
422 - jason 10040.8 8237.9 18594.6 17591.8 23.4 21.9 16.3 20.6 2.8 2.2
423 - thoas 10245.4 9117.9 19040.7 18598.2 23.7 23.5 19.6 21.2 2.6 2.3
424 - euneus 10446.2 6845.7 14069.1 12934.7 20.8 15.5 11.9 9.5 3.1 2.1
425 - json 10061.2 6563.0 13467.7 12629.6 19.6 16.0 11.1 8.3 2.5 1.8
427 + glazer 4379.2 1143.4 5132.9 2586.7 7.5 8.7 6.7 4.0 1.2 1.0
428 + torque 6089.2 1643.8 8087.6 3091.0 10.7 9.8 9.3 6.2 1.7 1.3
429 + simdjsone 5847.3 5019.7 8719.8 8620.6 14.4 17.7 12.1 12.6 1.9 3.6
430 + jiffy 7868.6 3615.6 9779.9 6532.6 16.8 15.2 12.4 9.1 2.5 3.8
431 + jason 13509.0 11248.6 25267.6 20837.6 33.5 30.0 19.7 25.0 4.4 2.9
432 + thoas 13679.7 12466.1 25638.7 22607.2 31.2 33.0 25.1 29.9 3.2 3.9
433 + euneus 14699.8 10247.2 18646.5 16886.6 29.1 25.2 16.7 14.6 4.0 4.6
434 + json 14315.5 9718.9 17844.3 16473.5 28.3 25.3 19.2 12.3 4.0 4.5
426 435 ```
427 436
428 437 (requires the `bench`/`dev` Mix dependencies — see `mix.exs`).
429 438
430 439 ### Performance
431 440
432 - `glazer` has a faster JSON encoder than all competitors. `glazer` is roughly on
433 - par with `torque` (a Rust `sonic-rs` NIF) across the benchmarked workloads on
434 - decoding — neither library is consistently faster, and the gap on any given
435 - file/operation is typically modest (within ~30%), varying in direction from
436 - file to file. Both sit well ahead of the other contenders (`simdjsone`,
437 - `jiffy`, and the pure-Elixir libraries `jason`, `thoas`, `euneus`, and OTP's
438 - built-in `json`).
441 + `glazer` is faster than all competitors on both encoding and decoding.
442 + On decoding it leads `torque` (Rust `sonic-rs` NIF) by ~25–40% across every
443 + benchmarked workload, and on encoding by ~10–30%. Both sit well ahead of
444 + the remaining contenders (`simdjsone`, `jiffy`, and the pure-Elixir libraries
445 + `jason`, `thoas`, `euneus`, and OTP's built-in `json`).
439 446
440 447 Where `glazer` has an edge over `torque`:
441 448
  @@ -453,7 +460,7 @@ Where `glazer` has an edge over `torque`:
453 460 - **`uescape`/`force_utf8` encode options** for `\uXXXX`-escaping non-ASCII
454 461 output and sanitizing invalid UTF-8 — useful when targeting strict JSON
455 462 consumers or transports that aren't UTF-8 clean.
456 - - **Standalone `json_minify/1`/`json_prettify/1` and big-integer helpers**
463 + - **Standalone `minify/1`/`prettify/1` and big-integer helpers**
457 464 (`encode_integer/1`/`decode_integer/1`/`try_decode_integer/1`) that don't
458 465 require a full decode/encode round-trip.
459 466 - **No external C++ dependencies.** The NIF is fully self-contained —
  @@ -501,19 +508,30 @@ of the gap over the slower contenders:
501 508 coincidentally look live). This makes cache construction effectively
502 509 free, regardless of table size.
503 510
511 + - **SIMD string scanning.** The JSON string decoder and encoder use an
512 + AVX2 → SSE2 → SWAR cascade to skip over clean byte spans 32, 16, or 8
513 + bytes at a time. The decoder scans for `"` and `\` (the only stop bytes
514 + in clean strings); the encoder additionally detects control characters
515 + (`c < 0x20`) via a bias trick that maps unsigned `< 0x20` to a signed
516 + comparison, avoiding a branch-per-byte table lookup for the common
517 + all-ASCII case. The same cascade is used by the CSV unquoted-field
518 + scanner (`delimiter | LF | CR`) and the YAML double-quoted scalar scanner
519 + (`"`, `\`, `LF`, `CR`), as well as single-character finders consolidated
520 + in `glazer_common.hpp` (`find_byte`). On AVX2 hardware (Haswell+) this
521 + processes up to 32 bytes per iteration instead of 1.
522 +
504 523 - **SWAR whitespace skipping.** `skip_ws` checks the next byte before
505 524 paying for any wider load, then — for runs of whitespace — scans 8 bytes
506 525 at a time using branch-free bit-twiddling ("SIMD within a register") to
507 - find the first non-whitespace byte, rather than testing one byte at a
508 - time. Minified JSON (the overwhelmingly common case) has little or no
509 - structural whitespace, so the single-byte fast path dominates in
510 - practice.
526 + find the first non-whitespace byte. Minified JSON (the overwhelmingly
527 + common case) has little or no structural whitespace, so the single-byte
528 + fast path dominates; the 8-byte path handles pretty-printed inputs.
511 529
512 530 - **Table-driven string escaping with bulk copies.** JSON string escaping
513 - scans for runs of bytes that need no escaping (a precomputed 256-entry
514 - lookup table answers "does this byte need escaping?" in O(1)) and copies
515 - each run in one `memcpy`, falling into a per-byte switch only for the
516 - rare characters that actually need an escape sequence.
531 + locates the next byte needing escaping in bulk (via the SIMD scanner
532 + above), copies the clean prefix in one `memcpy`, then falls into a
533 + per-byte switch only for the rare characters that actually need an escape
534 + sequence.
517 535
518 536 - **Fast integer formatting.** Integers are written to JSON using a
519 537 lookup-table-based digit-pair algorithm (avoiding division for small
  @@ -524,19 +542,19 @@ of the gap over the slower contenders:
524 542
525 543 ### Usage
526 544
527 - `yaml_decode/1,2` decodes a YAML document to an Erlang term — mappings
545 + `decode/1,2` decodes a YAML document to an Erlang term — mappings
528 546 become maps, sequences become lists, and scalars become the matching
529 547 Erlang type (binaries, numbers, booleans, or `null`):
530 548
531 549 ```erlang
532 - 1> glazer:yaml_decode(<<"a: 1\nb:\n - true\n - null\n - 3.5\n">>).
550 + 1> glazer_yaml:decode(<<"a: 1\nb:\n - true\n - null\n - 3.5\n">>).
533 551 #{<<"a">> => 1, <<"b">> => [true, null, 3.5]}
534 552
535 - 2> glazer:yaml_encode(#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}).
553 + 2> glazer_yaml:encode(#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}).
536 554 <<"a: 1\nb:\n - true\n - null\n - 3.5\n">>
537 555 ```
538 556
539 - `yaml_encode/1,2` encodes an Erlang term to YAML in block style
557 + `encode/1,2` encodes an Erlang term to YAML in block style
540 558 (2-space indentation, sequences at the same indentation as the mapping
541 559 key that owns them).
542 560
  @@ -545,11 +563,11 @@ key that owns them).
545 563 There is no incremental YAML decoder. YAML's block styles have no
546 564 closing delimiter — a mapping or sequence simply ends at a dedent or
547 565 end-of-input — so there is no way to scan a partial buffer for "is this
548 - value complete yet?" the way [`json_scan/1,2`](#efficiency) does for
566 + value complete yet?" the way [`scan/1,2`](#efficiency) does for
549 567 JSON's bracket-balanced syntax. Decode full YAML documents with
550 - `yaml_decode/1,2` once they are fully buffered.
568 + `decode/1,2` once they are fully buffered.
551 569
552 - ### Decode options (`yaml_decode/2`)
570 + ### Decode options (`glazer_yaml:decode/2`)
553 571
554 572 | Option | Description |
555 573 |---|---|
  @@ -561,17 +579,17 @@ JSON's bracket-balanced syntax. Decode full YAML documents with
561 579 | `yaml_1_1_bools` | Additionally treat `yes`/`no`/`on`/`off` (and case variants) as booleans, per the YAML 1.1 core schema. By default (YAML 1.2 core schema) only `true`/`false` are recognized as booleans |
562 580
563 581 ```erlang
564 - 1> glazer:yaml_decode(<<"a: ~\n">>, [use_nil]).
582 + 1> glazer_yaml:decode(<<"a: ~\n">>, [use_nil]).
565 583 #{<<"a">> => nil}
566 584
567 - 2> glazer:yaml_decode(<<"a: 1\n">>, [{keys, atom}]).
585 + 2> glazer_yaml:decode(<<"a: 1\n">>, [{keys, atom}]).
568 586 #{a => 1}
569 587
570 - 3> glazer:yaml_decode(<<"a: yes\n">>, [yaml_1_1_bools]).
588 + 3> glazer_yaml:decode(<<"a: yes\n">>, [yaml_1_1_bools]).
571 589 #{<<"a">> => true}
572 590 ```
573 591
574 - ### Encode options (`yaml_encode/2`)
592 + ### Encode options (`glazer_yaml:encode/2`)
575 593
576 594 | Option | Description |
577 595 |---|---|
  @@ -579,17 +597,21 @@ JSON's bracket-balanced syntax. Decode full YAML documents with
579 597 | `{null_term, Atom}` | Treat `Atom` as YAML `null` |
580 598
581 599 ```erlang
582 - 1> glazer:yaml_encode(#{<<"a">> => nil}, [use_nil]).
600 + 1> glazer_yaml:encode(#{<<"a">> => nil}, [use_nil]).
583 601 <<"a: null\n">>
584 602 ```
585 603
586 604 ### API
587 605
606 + All functions below are in `glazer_yaml`.
607 +
588 608 | Function | Description |
589 609 |---|---|
590 - | `yaml_decode/1`, `yaml_decode/2` | Decode a YAML binary or iolist to an Erlang term |
591 - | `yaml_try_decode/1`, `yaml_try_decode/2` | Decode YAML, returning `{ok, Term}` or `{error, Msg}` instead of raising |
592 - | `yaml_encode/1`, `yaml_encode/2` | Encode an Erlang term to a YAML binary in block style |
610 + | `decode/1`, `decode/2` | Decode a YAML binary or iolist to an Erlang term |
611 + | `try_decode/1`, `try_decode/2` | Decode YAML, returning `{ok, Term}` or `{error, Msg}` instead of raising |
612 + | `encode/1`, `encode/2` | Encode an Erlang term to a YAML binary in block style |
613 + | `read_file/1`, `read_file/2` | Read a file and decode its contents as YAML |
614 + | `write_file/2`, `write_file/3` | Encode a term to YAML and write it to a file |
593 615
594 616 ### Benchmarking YAML
595 617
  @@ -598,40 +620,40 @@ $ PARALLEL=2 make bench-yaml
598 620 ==> Running benchmarks with parallelism: 2
599 621
600 622 (numbers in µs)
601 - YAML openrtb (1.3K) esad (1.3K) small (0.1K)
602 - decode encode decode encode decode encode
623 + YAML openrtb (1.3K) esad (1.3K) small (0.1K)
624 + decode encode decode encode decode encode
603 625 -------------------------------------------------------------------------
604 - glazer 33.2 7.7 27.2 5.3 7.9 1.2
605 - yaml_rustler 126.5 n/a 78.0 n/a 11.8 n/a
606 - fast_yaml 180.0 45.1 97.2 40.6 18.8 6.1
607 - yamerl 1366.2 n/a 991.5 n/a 517.3 n/a
608 - ymlr n/a 47.4 n/a 35.3 n/a 4.8
626 + glazer 81.0 14.7 19.9 7.9 11.5 2.2
627 + yaml_rustler 195.3 n/a 103.9 n/a 16.9 n/a
628 + fast_yaml 254.9 69.5 141.4 54.4 26.7 7.6
629 + yamerl 2014.4 n/a 1486.2 n/a 676.1 n/a
630 + ymlr n/a 62.6 n/a 46.1 n/a 5.9
609 631 ```
610 632
611 633 ## CSV
612 634
613 635 ### Usage
614 636
615 - `csv_decode/1,2` decodes an RFC 4180 CSV document to a list of rows, each
637 + `decode/1,2` decodes an RFC 4180 CSV document to a list of rows, each
616 638 row a list of binary fields:
617 639
618 640 ```erlang
619 - 1> glazer:csv_decode(<<"name,age\nAlice,30\nBob,25\n">>).
641 + 1> glazer_csv:decode(<<"name,age\nAlice,30\nBob,25\n">>).
620 642 [[<<"name">>, <<"age">>], [<<"Alice">>, <<"30">>], [<<"Bob">>, <<"25">>]]
621 643
622 - 2> glazer:csv_encode([[<<"name">>, <<"age">>], [<<"Alice">>, 30]]).
644 + 2> glazer_csv:encode([[<<"name">>, <<"age">>], [<<"Alice">>, 30]]).
623 645 <<"name,age\r\nAlice,30\r\n">>
624 646 ```
625 647
626 648 With the `headers` option, the first row is used as column names and each
627 - subsequent row decodes to a map; `csv_encode/2` with `headers` does the
649 + subsequent row decodes to a map; `encode/2` with `headers` does the
628 650 reverse, deriving the header row from the first map's keys:
629 651
630 652 ```erlang
631 - 1> glazer:csv_decode(<<"name,age\nAlice,30\n">>, [headers]).
653 + 1> glazer_csv:decode(<<"name,age\nAlice,30\n">>, [headers]).
632 654 [#{<<"name">> => <<"Alice">>, <<"age">> => <<"30">>}]
633 655
634 - 2> glazer:csv_encode([#{<<"name">> => <<"Alice">>, <<"age">> => 30}], [headers]).
656 + 2> glazer_csv:encode([#{<<"name">> => <<"Alice">>, <<"age">> => 30}], [headers]).
635 657 <<"name,age\r\nAlice,30\r\n">>
636 658 ```
637 659
  @@ -643,49 +665,49 @@ RFC 4180 and can be changed to `\n` via `{line_ending, lf}`.
643 665
644 666 ### Streaming
645 667
646 - For input that arrives in chunks, `csv_stream_decoder/0,1` provides the
668 + For input that arrives in chunks, `stream_decoder/0,1` provides the
647 669 same kind of stateful wrapper as [JSON streaming](#streaming): it buffers
648 670 partial input and decodes each row as soon as its terminating line break
649 - is seen, via `csv_decode/2` on that single row. A small scanner tracks
671 + is seen, via `decode/2` on that single row. A small scanner tracks
650 672 whether the cursor is inside a quoted field across chunks, so a `\n`/`\r\n`
651 673 inside a quoted field doesn't end the row:
652 674
653 675 ```erlang
654 - 1> D0 = glazer:csv_stream_decoder(),
655 - 2> {Rows1, D1} = glazer:csv_stream_feed(D0, <<"a,b\n1,2\n3,">>),
676 + 1> D0 = glazer_csv:stream_decoder(),
677 + 2> {Rows1, D1} = glazer_csv:stream_feed(D0, <<"a,b\n1,2\n3,">>),
656 678 3> Rows1.
657 679 [[<<"a">>,<<"b">>],[<<"1">>,<<"2">>]]
658 680
659 - 4> {Rows2, D2} = glazer:csv_stream_feed(D1, <<"4\n">>),
681 + 4> {Rows2, D2} = glazer_csv:stream_feed(D1, <<"4\n">>),
660 682 5> Rows2.
661 683 [[<<"3">>,<<"4">>]]
662 684
663 - 6> glazer:csv_stream_eof(D2).
685 + 6> glazer_csv:stream_eof(D2).
664 686 {ok, []}
665 687 ```
666 688
667 - `csv_stream_feed/2` returns the rows completed by the chunk just fed
689 + `stream_feed/2` returns the rows completed by the chunk just fed
668 690 (possibly empty, possibly more than one) along with the updated decoder
669 - state. Once the input is exhausted, call `csv_stream_eof/1` to flush a
691 + state. Once the input is exhausted, call `stream_eof/1` to flush a
670 692 trailing row that has no terminating line break, or surface an error if
671 693 the buffered bytes don't form a valid row:
672 694
673 695 ```erlang
674 - 1> D0 = glazer:csv_stream_decoder(),
675 - 2> {Rows1, D1} = glazer:csv_stream_feed(D0, <<"a,b\n1,2">>),
696 + 1> D0 = glazer_csv:stream_decoder(),
697 + 2> {Rows1, D1} = glazer_csv:stream_feed(D0, <<"a,b\n1,2">>),
676 698 3> Rows1.
677 699 [[<<"a">>,<<"b">>]]
678 700
679 - 4> glazer:csv_stream_eof(D1).
701 + 4> glazer_csv:stream_eof(D1).
680 702 {ok, [[<<"1">>,<<"2">>]]}
681 703 ```
682 704
683 - `csv_stream_decoder/1` accepts the same options as `csv_decode/2`. With
705 + `stream_decoder/1` accepts the same options as `decode/2`. With
684 706 the `headers` option, the first complete row is captured as the header and
685 707 used to decode every subsequent row as a map; no row is emitted for the
686 - header itself. Blank lines are skipped, matching `csv_decode/2`.
708 + header itself. Blank lines are skipped, matching `decode/2`.
687 709
688 - ### Decode options (`csv_decode/2`)
710 + ### Decode options (`glazer_csv:decode/2`)
689 711
690 712 | Option | Description |
691 713 |---|---|
  @@ -694,8 +716,83 @@ header itself. Blank lines are skipped, matching `csv_decode/2`.
694 716 | `{keys, atom}` | With `headers`, decode column names as atoms |
695 717 | `{keys, existing_atom}` | With `headers`, decode column names as existing atoms, falling back to binaries for unknown atoms |
696 718 | `{keys, binary}` | With `headers`, decode column names as binaries (default) |
719 + | `{fields, Specs}` | Convert each column's field from a binary, positionally — see [Field type conversion](#field-type-conversion) |
720 + | `{null_term, Atom}` | Use `Atom` as the value produced by `on_failure => null` (default `null`) |
697 721
698 - ### Encode options (`csv_encode/2`)
722 + ### Field type conversion
723 +
724 + The `{fields, Specs}` decode option converts each column's field from a
725 + binary to the given Erlang type. `Specs` is a list applied positionally —
726 + the Nth spec applies to the Nth column, regardless of whether `headers` is
727 + set. Columns beyond the end of `Specs` are left as binaries.
728 +
729 + ```erlang
730 + 1> glazer_csv:decode(<<"name,age,active,joined\nAlice,30,true,2024-01-15T10:30:00Z\n">>,
731 + .. [headers, {fields, [binary, integer, boolean,
732 + .. {datetime, <<"%Y-%m-%dT%H:%M:%SZ">>}]}]).
733 + [#{<<"name">> => <<"Alice">>, <<"age">> => 30, <<"active">> => true,
734 + <<"joined">> => 1705314600}]
735 + ```
736 +
737 + Each element of `Specs` is either a `Type` directly, or a map
738 + `#{type => Type, default => Term, on_failure => OnFailure}` for more
739 + control (see below). `Type` is one of:
740 +
741 + | Type | Description |
742 + |---|---|
743 + | `integer` | Parse the field as an integer |
744 + | `{float, Precision}` | Parse the field as a float, rounded to `Precision` decimal digits |
745 + | `boolean` | Parse `"true"`/`"false"` (any case) as `true`/`false` |
746 + | `{datetime, InputFormat}` | Parse with a `strptime`-like format string and convert to Unix epoch seconds (UTC) |
747 + | `binary` | Leave the field as a binary (default) |
748 + | `charlist` | Convert the field to a list of Unicode code points |
749 + | `existing_atom` | Convert to an existing atom, falling back to a binary if no such atom exists |
750 + | `{atom, ExistingAtoms}` | Convert to an atom only if the field's text matches (and exists as) one of `ExistingAtoms`, falling back to a binary otherwise |
751 +
752 + `InputFormat` supports the directives `%Y %y %m %d %H %M %S %f %z` (and
753 + `%%` for a literal `%`); any other character must match the input
754 + literally, and a space matches a run of one-or-more whitespace characters.
755 + `%z` accepts `Z`, `+HHMM`, or `+HH:MM`-style offsets; fractional seconds
756 + (`%f`) are parsed but discarded. The result is always in UTC.
757 +
758 + #### `default` and `on_failure`
759 +
760 + Using the map form `#{type => Type, default => Term, on_failure => OnFailure}`:
761 +
762 + - `default` (when given) is used in place of the converted value whenever
763 + the raw CSV field is empty.
764 + - `on_failure` controls what happens when a *non-empty* field fails to
765 + convert to `Type` (default `binary`):
766 +
767 + | `on_failure` | Behavior |
768 + |---|---|
769 + | `binary` | Leave the field as the original binary (default) |
770 + | `raise` | Raise `{invalid_field_value, Row, Column}` (1-based), or return `{error, Reason}` from `try_decode/2` |
771 + | `default` | Use the spec's `default` value (falls back to `binary` if no `default` is given) |
772 + | `null` | Use the configured null term: `{null_term, Atom}` if given, otherwise the library-wide null term (see [Null term configuration](#null-term-configuration) and `{null_term, Atom}` below) |
773 +
774 + ```erlang
775 + 1> glazer_csv:decode(<<"1\nbad\n">>,
776 + .. [{fields, [#{type => integer, on_failure => raise}]}]).
777 + ** exception error: {invalid_field_value,2,1}
778 +
779 + 2> glazer_csv:decode(<<"1\nbad\n">>,
780 + .. [{fields, [#{type => integer, default => 0, on_failure => default}]}]).
781 + [[1],[0]]
782 +
783 + 3> glazer_csv:decode(<<"1\nbad\n">>,
784 + .. [{null_term, nil},
785 + .. {fields, [#{type => integer, on_failure => null}]}]).
786 + [[1],[nil]]
787 + ```
788 +
789 + `{null_term, Atom}` only affects `on_failure => null` for that call. Without
790 + it, `on_failure => null` falls back to the library-wide null term — `null`
791 + by default, or whatever atom is configured via the
792 + [Null term configuration](#null-term-configuration)
793 + application env var (`{glazer, [{null, Atom}]}`).
794 +
795 + ### Encode options (`glazer_csv:encode/2`)
699 796
700 797 | Option | Description |
701 798 |---|---|
  @@ -705,14 +802,18 @@ header itself. Blank lines are skipped, matching `csv_decode/2`.
705 802
706 803 ### API
707 804
805 + All functions below are in `glazer_csv`.
806 +
708 807 | Function | Description |
709 808 |---|---|
710 - | `csv_decode/1`, `csv_decode/2` | Decode a CSV binary or iolist to a list of rows (or maps with `headers`) |
711 - | `csv_try_decode/1`, `csv_try_decode/2` | Decode CSV, returning `{ok, Rows}` or `{error, Reason}` instead of raising |
712 - | `csv_encode/1`, `csv_encode/2` | Encode a list of rows (or maps with `headers`) to a CSV binary |
713 - | `csv_stream_decoder/0`, `csv_stream_decoder/1` | Create an incremental CSV decode state for chunked input |
714 - | `csv_stream_feed/2` | Feed a chunk to a CSV stream decoder, returning completed rows |
715 - | `csv_stream_eof/1` | Flush a CSV stream decoder at end-of-input |
809 + | `decode/1`, `decode/2` | Decode a CSV binary or iolist to a list of rows (or maps with `headers`) |
810 + | `try_decode/1`, `try_decode/2` | Decode CSV, returning `{ok, Rows}` or `{error, Reason}` instead of raising |
811 + | `encode/1`, `encode/2` | Encode a list of rows (or maps with `headers`) to a CSV binary |
812 + | `read_file/1`, `read_file/2` | Read a file and decode its contents as CSV |
813 + | `write_file/2`, `write_file/3` | Encode rows to CSV and write them to a file |
814 + | `stream_decoder/0`, `stream_decoder/1` | Create an incremental CSV decode state for chunked input |
815 + | `stream_feed/2` | Feed a chunk to a CSV stream decoder, returning completed rows |
816 + | `stream_eof/1` | Flush a CSV stream decoder at end-of-input |
716 817
717 818 ### Benchmarking CSV
718 819
  @@ -721,13 +822,13 @@ $ PARALLEL=2 make bench-csv
721 822 ==> Running benchmarks with parallelism: 2
722 823
723 824 (numbers in µs)
724 - CSV small (1.3K) medium (130.9K) large (3433.1K)
725 - decode encode decode encode decode encode
825 + CSV small (1.3K) medium (130.9K) large (3433.1K)
826 + decode encode decode encode decode encode
726 827 -----------------------------------------------------------------------------------
727 - glazer 8.3 4.9 1066.0 380.7 38298.9 12887.9
728 - nimble_csv 33.9 23.4 4202.3 2535.2 173790.7 94933.2
729 - csv 75.1 177.9 6250.8 15820.6 TIMEOUT TIMEOUT
730 - erl_csv 370.6 266.1 39832.4 26145.4 TIMEOUT TIMEOUT
828 + glazer 10.7 3.3 1289.6 469.5 42617.2 16240.1
829 + nimble_csv 44.8 38.8 4582.9 3204.4 238366.4 120585.9
830 + csv 99.3 257.3 8335.2 24393.9 TIMEOUT TIMEOUT
831 + erl_csv 705.5 427.4 54950.5 34607.9 TIMEOUT TIMEOUT
731 832 ```
732 833
733 834 ## Big integers
  @@ -761,6 +862,25 @@ same conversion routines directly, independent of JSON/YAML/CSV parsing/encoding
761 862 See the module's documentation (`src/glazer.erl`) for full type
762 863 specs and details.
763 864
865 + ## Limitations
866 +
867 + ### Nesting depth
868 +
869 + The JSON and YAML decoders both cap recursion at **256 levels** of nesting
870 + (arrays/objects for JSON; mappings/sequences for YAML). Inputs that exceed
871 + this limit are rejected with a decode error rather than crashing the VM by
872 + overflowing the C stack.
873 +
874 + | Format | Limit | Error returned |
875 + |--------|-------|---------------|
876 + | JSON | 256 | `{error, <<"exceeded maximum nesting depth at offset N">>}` |
877 + | YAML | 256 | `{error, <<"exceeded maximum nesting depth at offset N">>}` |
878 +
879 + 256 levels is sufficient for any reasonable real-world document; it is
880 + deliberately not configurable, because the limit exists to protect the
881 + Erlang VM process (the NIF runs on the scheduler thread) from runaway
882 + recursive descent on adversarial input.
883 +
764 884 ## Testing
765 885
766 886 ```sh
  @@ -50,61 +50,63 @@
50 50 <text x="44.0" y="97.7" text-anchor="end" font-size="9" class="muted">10000</text>
51 51 <line x1="50.0" y1="68.0" x2="370.0" y2="68.0" class="grid" stroke-width="1"/>
52 52 <text x="44.0" y="71.0" text-anchor="end" font-size="9" class="muted">15000</text>
53 - <rect x="56.0" y="127.3" width="33.2" height="20.7" class="bar-glazer"><title>glazer: 3876.2 (decode)</title></rect>
54 - <text x="72.6" y="124.3" text-anchor="middle" font-size="8" class="muted">3876.2</text>
53 + <rect x="56.0" y="124.6" width="33.2" height="23.4" class="bar-glazer"><title>glazer: 4379.2 (decode)</title></rect>
54 + <text x="72.6" y="121.6" text-anchor="middle" font-size="8" class="muted">4379.2</text>
55 55 <text x="72.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 72.6,158.0)">glazer</text>
56 - <rect x="95.2" y="120.6" width="33.2" height="27.4" class="bar-1"><title>torque: 5140.3 (decode)</title></rect>
57 - <text x="111.9" y="117.6" text-anchor="middle" font-size="8" class="muted">5140.3</text>
56 + <rect x="95.2" y="115.5" width="33.2" height="32.5" class="bar-1"><title>torque: 6089.2 (decode)</title></rect>
57 + <text x="111.9" y="112.5" text-anchor="middle" font-size="8" class="muted">6089.2</text>
58 58 <text x="111.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 111.9,158.0)">torque</text>
59 - <rect x="134.5" y="123.2" width="33.2" height="24.8" class="bar-2"><title>simdjsone: 4652.9 (decode)</title></rect>
60 - <text x="151.1" y="120.2" text-anchor="middle" font-size="8" class="muted">4652.9</text>
59 + <rect x="134.5" y="116.8" width="33.2" height="31.2" class="bar-2"><title>simdjsone: 5847.3 (decode)</title></rect>
60 + <text x="151.1" y="113.8" text-anchor="middle" font-size="8" class="muted">5847.3</text>
61 61 <text x="151.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 151.1,158.0)">simdjsone</text>
62 - <rect x="173.8" y="116.4" width="33.2" height="31.6" class="bar-3"><title>jiffy: 5921.4 (decode)</title></rect>
63 - <text x="190.4" y="113.4" text-anchor="middle" font-size="8" class="muted">5921.4</text>
62 + <rect x="173.8" y="106.0" width="33.2" height="42.0" class="bar-3"><title>jiffy: 7868.6 (decode)</title></rect>
63 + <text x="190.4" y="103.0" text-anchor="middle" font-size="8" class="muted">7868.6</text>
64 64 <text x="190.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 190.4,158.0)">jiffy</text>
65 - <rect x="213.0" y="94.4" width="33.2" height="53.6" class="bar-4"><title>jason: 10040.8 (decode)</title></rect>
66 - <text x="229.6" y="91.4" text-anchor="middle" font-size="8" class="muted">10040.8</text>
65 + <rect x="213.0" y="76.0" width="33.2" height="72.0" class="bar-4"><title>jason: 13509 (decode)</title></rect>
66 + <text x="229.6" y="73.0" text-anchor="middle" font-size="8" class="muted">13509</text>
67 67 <text x="229.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 229.6,158.0)">jason</text>
68 - <rect x="252.2" y="93.4" width="33.2" height="54.6" class="bar-5"><title>thoas: 10245.4 (decode)</title></rect>
69 - <text x="268.9" y="90.4" text-anchor="middle" font-size="8" class="muted">10245.4</text>
68 + <rect x="252.2" y="75.0" width="33.2" height="73.0" class="bar-5"><title>thoas: 13679.7 (decode)</title></rect>
69 + <text x="268.9" y="72.0" text-anchor="middle" font-size="8" class="muted">13679.7</text>
70 70 <text x="268.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 268.9,158.0)">thoas</text>
71 - <rect x="291.5" y="92.3" width="33.2" height="55.7" class="bar-6"><title>euneus: 10446.2 (decode)</title></rect>
72 - <text x="308.1" y="89.3" text-anchor="middle" font-size="8" class="muted">10446.2</text>
71 + <rect x="291.5" y="69.6" width="33.2" height="78.4" class="bar-6"><title>euneus: 14699.8 (decode)</title></rect>
72 + <text x="308.1" y="66.6" text-anchor="middle" font-size="8" class="muted">14699.8</text>
73 73 <text x="308.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 308.1,158.0)">euneus</text>
74 - <rect x="330.8" y="94.3" width="33.2" height="53.7" class="bar-7"><title>json: 10061.2 (decode)</title></rect>
75 - <text x="347.4" y="91.3" text-anchor="middle" font-size="8" class="muted">10061.2</text>
74 + <rect x="330.8" y="71.7" width="33.2" height="76.3" class="bar-7"><title>json: 14315.5 (decode)</title></rect>
75 + <text x="347.4" y="68.7" text-anchor="middle" font-size="8" class="muted">14315.5</text>
76 76 <text x="347.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 347.4,158.0)">json</text>
77 77 <line x1="50.0" y1="148.0" x2="370.0" y2="148.0" class="axis" stroke-width="1"/>
78 78 <text x="570.0" y="66.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">JSON encode — 616.7K</text>
79 79 <line x1="430.0" y1="148.0" x2="750.0" y2="148.0" class="grid" stroke-width="1"/>
80 80 <text x="424.0" y="151.0" text-anchor="end" font-size="9" class="muted">0</text>
81 - <line x1="430.0" y1="108.0" x2="750.0" y2="108.0" class="grid" stroke-width="1"/>
82 - <text x="424.0" y="111.0" text-anchor="end" font-size="9" class="muted">5000</text>
81 + <line x1="430.0" y1="121.3" x2="750.0" y2="121.3" class="grid" stroke-width="1"/>
82 + <text x="424.0" y="124.3" text-anchor="end" font-size="9" class="muted">5000</text>
83 + <line x1="430.0" y1="94.7" x2="750.0" y2="94.7" class="grid" stroke-width="1"/>
84 + <text x="424.0" y="97.7" text-anchor="end" font-size="9" class="muted">10000</text>
83 85 <line x1="430.0" y1="68.0" x2="750.0" y2="68.0" class="grid" stroke-width="1"/>
84 - <text x="424.0" y="71.0" text-anchor="end" font-size="9" class="muted">10000</text>
85 - <rect x="436.0" y="138.4" width="33.2" height="9.6" class="bar-glazer"><title>glazer: 1205.8 (encode)</title></rect>
86 - <text x="452.6" y="135.4" text-anchor="middle" font-size="8" class="muted">1205.8</text>
86 + <text x="424.0" y="71.0" text-anchor="end" font-size="9" class="muted">15000</text>
87 + <rect x="436.0" y="141.9" width="33.2" height="6.1" class="bar-glazer"><title>glazer: 1143.4 (encode)</title></rect>
88 + <text x="452.6" y="138.9" text-anchor="middle" font-size="8" class="muted">1143.4</text>
87 89 <text x="452.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 452.6,158.0)">glazer</text>
88 - <rect x="475.2" y="137.3" width="33.2" height="10.7" class="bar-1"><title>torque: 1333.7 (encode)</title></rect>
89 - <text x="491.9" y="134.3" text-anchor="middle" font-size="8" class="muted">1333.7</text>
90 + <rect x="475.2" y="139.2" width="33.2" height="8.8" class="bar-1"><title>torque: 1643.8 (encode)</title></rect>
91 + <text x="491.9" y="136.2" text-anchor="middle" font-size="8" class="muted">1643.8</text>
90 92 <text x="491.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 491.9,158.0)">torque</text>
91 - <rect x="514.5" y="119.5" width="33.2" height="28.5" class="bar-2"><title>simdjsone: 3564.3 (encode)</title></rect>
92 - <text x="531.1" y="116.5" text-anchor="middle" font-size="8" class="muted">3564.3</text>
93 + <rect x="514.5" y="121.2" width="33.2" height="26.8" class="bar-2"><title>simdjsone: 5019.7 (encode)</title></rect>
94 + <text x="531.1" y="118.2" text-anchor="middle" font-size="8" class="muted">5019.7</text>
93 95 <text x="531.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 531.1,158.0)">simdjsone</text>
94 - <rect x="553.8" y="127.6" width="33.2" height="20.4" class="bar-3"><title>jiffy: 2547.3 (encode)</title></rect>
95 - <text x="570.4" y="124.6" text-anchor="middle" font-size="8" class="muted">2547.3</text>
96 + <rect x="553.8" y="128.7" width="33.2" height="19.3" class="bar-3"><title>jiffy: 3615.6 (encode)</title></rect>
97 + <text x="570.4" y="125.7" text-anchor="middle" font-size="8" class="muted">3615.6</text>
96 98 <text x="570.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 570.4,158.0)">jiffy</text>
97 - <rect x="593.0" y="82.1" width="33.2" height="65.9" class="bar-4"><title>jason: 8237.9 (encode)</title></rect>
98 - <text x="609.6" y="79.1" text-anchor="middle" font-size="8" class="muted">8237.9</text>
99 + <rect x="593.0" y="88.0" width="33.2" height="60.0" class="bar-4"><title>jason: 11248.6 (encode)</title></rect>
100 + <text x="609.6" y="85.0" text-anchor="middle" font-size="8" class="muted">11248.6</text>
99 101 <text x="609.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 609.6,158.0)">jason</text>
100 - <rect x="632.2" y="75.1" width="33.2" height="72.9" class="bar-5"><title>thoas: 9117.9 (encode)</title></rect>
101 - <text x="648.9" y="72.1" text-anchor="middle" font-size="8" class="muted">9117.9</text>
102 + <rect x="632.2" y="81.5" width="33.2" height="66.5" class="bar-5"><title>thoas: 12466.1 (encode)</title></rect>
103 + <text x="648.9" y="78.5" text-anchor="middle" font-size="8" class="muted">12466.1</text>
102 104 <text x="648.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 648.9,158.0)">thoas</text>
103 - <rect x="671.5" y="93.2" width="33.2" height="54.8" class="bar-6"><title>euneus: 6845.7 (encode)</title></rect>
104 - <text x="688.1" y="90.2" text-anchor="middle" font-size="8" class="muted">6845.7</text>
105 + <rect x="671.5" y="93.3" width="33.2" height="54.7" class="bar-6"><title>euneus: 10247.2 (encode)</title></rect>
106 + <text x="688.1" y="90.3" text-anchor="middle" font-size="8" class="muted">10247.2</text>
105 107 <text x="688.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 688.1,158.0)">euneus</text>
106 - <rect x="710.8" y="95.5" width="33.2" height="52.5" class="bar-7"><title>json: 6563 (encode)</title></rect>
107 - <text x="727.4" y="92.5" text-anchor="middle" font-size="8" class="muted">6563</text>
108 + <rect x="710.8" y="96.2" width="33.2" height="51.8" class="bar-7"><title>json: 9718.9 (encode)</title></rect>
109 + <text x="727.4" y="93.2" text-anchor="middle" font-size="8" class="muted">9718.9</text>
108 110 <text x="727.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 727.4,158.0)">json</text>
109 111 <line x1="430.0" y1="148.0" x2="750.0" y2="148.0" class="axis" stroke-width="1"/>
110 112 <text x="190.0" y="204.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">YAML decode — 1.3K</text>
  @@ -116,17 +118,17 @@
116 118 <text x="44.0" y="235.7" text-anchor="end" font-size="9" class="muted">2000</text>
117 119 <line x1="50.0" y1="206.0" x2="370.0" y2="206.0" class="grid" stroke-width="1"/>
118 120 <text x="44.0" y="209.0" text-anchor="end" font-size="9" class="muted">3000</text>
119 - <rect x="56.0" y="281.9" width="56.8" height="4.1" class="bar-glazer"><title>glazer: 154.3 (decode)</title></rect>
120 - <text x="84.4" y="278.9" text-anchor="middle" font-size="8" class="muted">154.3</text>
121 + <rect x="56.0" y="283.8" width="56.8" height="2.2" class="bar-glazer"><title>glazer: 81 (decode)</title></rect>
122 + <text x="84.4" y="280.8" text-anchor="middle" font-size="8" class="muted">81</text>
121 123 <text x="84.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 84.4,296.0)">glazer</text>
122 - <rect x="118.8" y="279.4" width="56.8" height="6.6" class="bar-1"><title>yaml_rustler: 248 (decode)</title></rect>
123 - <text x="147.2" y="276.4" text-anchor="middle" font-size="8" class="muted">248</text>
124 + <rect x="118.8" y="280.8" width="56.8" height="5.2" class="bar-1"><title>yaml_rustler: 195.3 (decode)</title></rect>
125 + <text x="147.2" y="277.8" text-anchor="middle" font-size="8" class="muted">195.3</text>
124 126 <text x="147.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 147.2,296.0)">yaml_rustler</text>
125 - <rect x="181.6" y="279.3" width="56.8" height="6.7" class="bar-2"><title>fast_yaml: 250.4 (decode)</title></rect>
126 - <text x="210.0" y="276.3" text-anchor="middle" font-size="8" class="muted">250.4</text>
127 + <rect x="181.6" y="279.2" width="56.8" height="6.8" class="bar-2"><title>fast_yaml: 254.9 (decode)</title></rect>
128 + <text x="210.0" y="276.2" text-anchor="middle" font-size="8" class="muted">254.9</text>
127 129 <text x="210.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 210.0,296.0)">fast_yaml</text>
128 - <rect x="244.4" y="232.5" width="56.8" height="53.5" class="bar-3"><title>yamerl: 2006.9 (decode)</title></rect>
129 - <text x="272.8" y="229.5" text-anchor="middle" font-size="8" class="muted">2006.9</text>
130 + <rect x="244.4" y="232.3" width="56.8" height="53.7" class="bar-3"><title>yamerl: 2014.4 (decode)</title></rect>
131 + <text x="272.8" y="229.3" text-anchor="middle" font-size="8" class="muted">2014.4</text>
130 132 <text x="272.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 272.8,296.0)">yamerl</text>
131 133 <text x="335.6" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
132 134 <text x="335.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 335.6,296.0)">ymlr</text>
  @@ -142,39 +144,36 @@
142 144 <text x="424.0" y="229.0" text-anchor="end" font-size="9" class="muted">60</text>
143 145 <line x1="430.0" y1="206.0" x2="750.0" y2="206.0" class="grid" stroke-width="1"/>
144 146 <text x="424.0" y="209.0" text-anchor="end" font-size="9" class="muted">80</text>
145 - <rect x="436.0" y="271.8" width="56.8" height="14.2" class="bar-glazer"><title>glazer: 14.2 (encode)</title></rect>
146 - <text x="464.4" y="268.8" text-anchor="middle" font-size="8" class="muted">14.2</text>
147 + <rect x="436.0" y="271.3" width="56.8" height="14.7" class="bar-glazer"><title>glazer: 14.7 (encode)</title></rect>
148 + <text x="464.4" y="268.3" text-anchor="middle" font-size="8" class="muted">14.7</text>
147 149 <text x="464.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 464.4,296.0)">glazer</text>
148 150 <text x="527.2" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
149 151 <text x="527.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 527.2,296.0)">yaml_rustler</text>
150 - <rect x="561.6" y="220.9" width="56.8" height="65.1" class="bar-2"><title>fast_yaml: 65.1 (encode)</title></rect>
151 - <text x="590.0" y="217.9" text-anchor="middle" font-size="8" class="muted">65.1</text>
152 + <rect x="561.6" y="216.5" width="56.8" height="69.5" class="bar-2"><title>fast_yaml: 69.5 (encode)</title></rect>
153 + <text x="590.0" y="213.5" text-anchor="middle" font-size="8" class="muted">69.5</text>
152 154 <text x="590.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 590.0,296.0)">fast_yaml</text>
153 155 <text x="652.8" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
154 156 <text x="652.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 652.8,296.0)">yamerl</text>
155 - <rect x="687.2" y="227.8" width="56.8" height="58.2" class="bar-4"><title>ymlr: 58.2 (encode)</title></rect>
156 - <text x="715.6" y="224.8" text-anchor="middle" font-size="8" class="muted">58.2</text>
157 + <rect x="687.2" y="223.4" width="56.8" height="62.6" class="bar-4"><title>ymlr: 62.6 (encode)</title></rect>
158 + <text x="715.6" y="220.4" text-anchor="middle" font-size="8" class="muted">62.6</text>
157 159 <text x="715.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 715.6,296.0)">ymlr</text>
158 160 <line x1="430.0" y1="286.0" x2="750.0" y2="286.0" class="axis" stroke-width="1"/>
159 161 <text x="190.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV decode — 3433.1K</text>
160 162 <line x1="50.0" y1="424.0" x2="370.0" y2="424.0" class="grid" stroke-width="1"/>
161 163 <text x="44.0" y="427.0" text-anchor="end" font-size="9" class="muted">0</text>
162 - <line x1="50.0" y1="404.0" x2="370.0" y2="404.0" class="grid" stroke-width="1"/>
163 - <text x="44.0" y="407.0" text-anchor="end" font-size="9" class="muted">100000</text>
164 - <line x1="50.0" y1="384.0" x2="370.0" y2="384.0" class="grid" stroke-width="1"/>
165 - <text x="44.0" y="387.0" text-anchor="end" font-size="9" class="muted">200000</text>
166 - <line x1="50.0" y1="364.0" x2="370.0" y2="364.0" class="grid" stroke-width="1"/>
167 - <text x="44.0" y="367.0" text-anchor="end" font-size="9" class="muted">300000</text>
164 + <line x1="50.0" y1="397.3" x2="370.0" y2="397.3" class="grid" stroke-width="1"/>
165 + <text x="44.0" y="400.3" text-anchor="end" font-size="9" class="muted">100000</text>
166 + <line x1="50.0" y1="370.7" x2="370.0" y2="370.7" class="grid" stroke-width="1"/>
167 + <text x="44.0" y="373.7" text-anchor="end" font-size="9" class="muted">200000</text>
168 168 <line x1="50.0" y1="344.0" x2="370.0" y2="344.0" class="grid" stroke-width="1"/>
169 - <text x="44.0" y="347.0" text-anchor="end" font-size="9" class="muted">400000</text>
170 - <rect x="56.0" y="417.8" width="72.5" height="6.2" class="bar-glazer"><title>glazer: 30936 (decode)</title></rect>
171 - <text x="92.2" y="414.8" text-anchor="middle" font-size="8" class="muted">30936</text>
169 + <text x="44.0" y="347.0" text-anchor="end" font-size="9" class="muted">300000</text>
170 + <rect x="56.0" y="412.6" width="72.5" height="11.4" class="bar-glazer"><title>glazer: 42617.2 (decode)</title></rect>
171 + <text x="92.2" y="409.6" text-anchor="middle" font-size="8" class="muted">42617.2</text>
172 172 <text x="92.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 92.2,434.0)">glazer</text>
173 - <rect x="134.5" y="390.3" width="72.5" height="33.7" class="bar-1"><title>nimble_csv: 168553 (decode)</title></rect>
174 - <text x="170.8" y="387.3" text-anchor="middle" font-size="8" class="muted">168553</text>
173 + <rect x="134.5" y="360.4" width="72.5" height="63.6" class="bar-1"><title>nimble_csv: 238366 (decode)</title></rect>
174 + <text x="170.8" y="357.4" text-anchor="middle" font-size="8" class="muted">238366</text>
175 175 <text x="170.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 170.8,434.0)">nimble_csv</text>
176 - <rect x="213.0" y="355.0" width="72.5" height="69.0" class="bar-2"><title>csv: 345033 (decode)</title></rect>
177 - <text x="249.2" y="352.0" text-anchor="middle" font-size="8" class="muted">345033</text>
176 + <text x="249.2" y="420.0" text-anchor="middle" font-size="8" class="faint">TIMEOUT</text>
178 177 <text x="249.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 249.2,434.0)">csv</text>
179 178 <text x="327.8" y="420.0" text-anchor="middle" font-size="8" class="faint">TIMEOUT</text>
180 179 <text x="327.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 327.8,434.0)">erl_csv</text>
  @@ -182,22 +181,19 @@
182 181 <text x="570.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV encode — 3433.1K</text>
183 182 <line x1="430.0" y1="424.0" x2="750.0" y2="424.0" class="grid" stroke-width="1"/>
184 183 <text x="424.0" y="427.0" text-anchor="end" font-size="9" class="muted">0</text>
185 - <line x1="430.0" y1="404.0" x2="750.0" y2="404.0" class="grid" stroke-width="1"/>
186 - <text x="424.0" y="407.0" text-anchor="end" font-size="9" class="muted">200000</text>
187 - <line x1="430.0" y1="384.0" x2="750.0" y2="384.0" class="grid" stroke-width="1"/>
188 - <text x="424.0" y="387.0" text-anchor="end" font-size="9" class="muted">400000</text>
189 - <line x1="430.0" y1="364.0" x2="750.0" y2="364.0" class="grid" stroke-width="1"/>
190 - <text x="424.0" y="367.0" text-anchor="end" font-size="9" class="muted">600000</text>
184 + <line x1="430.0" y1="397.3" x2="750.0" y2="397.3" class="grid" stroke-width="1"/>
185 + <text x="424.0" y="400.3" text-anchor="end" font-size="9" class="muted">50000</text>
186 + <line x1="430.0" y1="370.7" x2="750.0" y2="370.7" class="grid" stroke-width="1"/>
187 + <text x="424.0" y="373.7" text-anchor="end" font-size="9" class="muted">100000</text>
191 188 <line x1="430.0" y1="344.0" x2="750.0" y2="344.0" class="grid" stroke-width="1"/>
192 - <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">800000</text>
193 - <rect x="436.0" y="423.0" width="72.5" height="1.0" class="bar-glazer"><title>glazer: 10496.8 (encode)</title></rect>
194 - <text x="472.2" y="420.0" text-anchor="middle" font-size="8" class="muted">10496.8</text>
189 + <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">150000</text>
190 + <rect x="436.0" y="415.3" width="72.5" height="8.7" class="bar-glazer"><title>glazer: 16240.1 (encode)</title></rect>
191 + <text x="472.2" y="412.3" text-anchor="middle" font-size="8" class="muted">16240.1</text>
195 192 <text x="472.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 472.2,434.0)">glazer</text>
196 - <rect x="514.5" y="414.9" width="72.5" height="9.1" class="bar-1"><title>nimble_csv: 91117.8 (encode)</title></rect>
197 - <text x="550.8" y="411.9" text-anchor="middle" font-size="8" class="muted">91117.8</text>
193 + <rect x="514.5" y="359.7" width="72.5" height="64.3" class="bar-1"><title>nimble_csv: 120586 (encode)</title></rect>
194 + <text x="550.8" y="356.7" text-anchor="middle" font-size="8" class="muted">120586</text>
198 195 <text x="550.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 550.8,434.0)">nimble_csv</text>
199 - <rect x="593.0" y="361.8" width="72.5" height="62.2" class="bar-2"><title>csv: 621974 (encode)</title></rect>
200 - <text x="629.2" y="358.8" text-anchor="middle" font-size="8" class="muted">621974</text>
196 + <text x="629.2" y="420.0" text-anchor="middle" font-size="8" class="faint">TIMEOUT</text>
201 197 <text x="629.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 629.2,434.0)">csv</text>
202 198 <text x="707.8" y="420.0" text-anchor="middle" font-size="8" class="faint">TIMEOUT</text>
203 199 <text x="707.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 707.8,434.0)">erl_csv</text>
  @@ -44,37 +44,35 @@
44 44 <text x="190.0" y="66.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">JSON decode — 1.3K</text>
45 45 <line x1="50.0" y1="148.0" x2="370.0" y2="148.0" class="grid" stroke-width="1"/>
46 46 <text x="44.0" y="151.0" text-anchor="end" font-size="9" class="muted">0</text>
47 - <line x1="50.0" y1="128.0" x2="370.0" y2="128.0" class="grid" stroke-width="1"/>
48 - <text x="44.0" y="131.0" text-anchor="end" font-size="9" class="muted">5</text>
49 - <line x1="50.0" y1="108.0" x2="370.0" y2="108.0" class="grid" stroke-width="1"/>
50 - <text x="44.0" y="111.0" text-anchor="end" font-size="9" class="muted">10</text>
51 - <line x1="50.0" y1="88.0" x2="370.0" y2="88.0" class="grid" stroke-width="1"/>
52 - <text x="44.0" y="91.0" text-anchor="end" font-size="9" class="muted">15</text>
47 + <line x1="50.0" y1="121.3" x2="370.0" y2="121.3" class="grid" stroke-width="1"/>
48 + <text x="44.0" y="124.3" text-anchor="end" font-size="9" class="muted">10</text>
49 + <line x1="50.0" y1="94.7" x2="370.0" y2="94.7" class="grid" stroke-width="1"/>
50 + <text x="44.0" y="97.7" text-anchor="end" font-size="9" class="muted">20</text>
53 51 <line x1="50.0" y1="68.0" x2="370.0" y2="68.0" class="grid" stroke-width="1"/>
54 - <text x="44.0" y="71.0" text-anchor="end" font-size="9" class="muted">20</text>
55 - <rect x="56.0" y="124.8" width="33.2" height="23.2" class="bar-glazer"><title>glazer: 5.8 (decode)</title></rect>
56 - <text x="72.6" y="121.8" text-anchor="middle" font-size="8" class="muted">5.8</text>
52 + <text x="44.0" y="71.0" text-anchor="end" font-size="9" class="muted">30</text>
53 + <rect x="56.0" y="130.1" width="33.2" height="17.9" class="bar-glazer"><title>glazer: 6.7 (decode)</title></rect>
54 + <text x="72.6" y="127.1" text-anchor="middle" font-size="8" class="muted">6.7</text>
57 55 <text x="72.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 72.6,158.0)">glazer</text>
58 - <rect x="95.2" y="128.4" width="33.2" height="19.6" class="bar-1"><title>torque: 4.9 (decode)</title></rect>
59 - <text x="111.9" y="125.4" text-anchor="middle" font-size="8" class="muted">4.9</text>
56 + <rect x="95.2" y="123.2" width="33.2" height="24.8" class="bar-1"><title>torque: 9.3 (decode)</title></rect>
57 + <text x="111.9" y="120.2" text-anchor="middle" font-size="8" class="muted">9.3</text>
60 58 <text x="111.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 111.9,158.0)">torque</text>
61 - <rect x="134.5" y="117.2" width="33.2" height="30.8" class="bar-2"><title>simdjsone: 7.7 (decode)</title></rect>
62 - <text x="151.1" y="114.2" text-anchor="middle" font-size="8" class="muted">7.7</text>
59 + <rect x="134.5" y="115.7" width="33.2" height="32.3" class="bar-2"><title>simdjsone: 12.1 (decode)</title></rect>
60 + <text x="151.1" y="112.7" text-anchor="middle" font-size="8" class="muted">12.1</text>
63 61 <text x="151.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 151.1,158.0)">simdjsone</text>
64 - <rect x="173.8" y="116.4" width="33.2" height="31.6" class="bar-3"><title>jiffy: 7.9 (decode)</title></rect>
65 - <text x="190.4" y="113.4" text-anchor="middle" font-size="8" class="muted">7.9</text>
62 + <rect x="173.8" y="114.9" width="33.2" height="33.1" class="bar-3"><title>jiffy: 12.4 (decode)</title></rect>
63 + <text x="190.4" y="111.9" text-anchor="middle" font-size="8" class="muted">12.4</text>
66 64 <text x="190.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 190.4,158.0)">jiffy</text>
67 - <rect x="213.0" y="82.8" width="33.2" height="65.2" class="bar-4"><title>jason: 16.3 (decode)</title></rect>
68 - <text x="229.6" y="79.8" text-anchor="middle" font-size="8" class="muted">16.3</text>
65 + <rect x="213.0" y="95.5" width="33.2" height="52.5" class="bar-4"><title>jason: 19.7 (decode)</title></rect>
66 + <text x="229.6" y="92.5" text-anchor="middle" font-size="8" class="muted">19.7</text>
69 67 <text x="229.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 229.6,158.0)">jason</text>
70 - <rect x="252.2" y="69.6" width="33.2" height="78.4" class="bar-5"><title>thoas: 19.6 (decode)</title></rect>
71 - <text x="268.9" y="66.6" text-anchor="middle" font-size="8" class="muted">19.6</text>
68 + <rect x="252.2" y="81.1" width="33.2" height="66.9" class="bar-5"><title>thoas: 25.1 (decode)</title></rect>
69 + <text x="268.9" y="78.1" text-anchor="middle" font-size="8" class="muted">25.1</text>
72 70 <text x="268.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 268.9,158.0)">thoas</text>
73 - <rect x="291.5" y="100.4" width="33.2" height="47.6" class="bar-6"><title>euneus: 11.9 (decode)</title></rect>
74 - <text x="308.1" y="97.4" text-anchor="middle" font-size="8" class="muted">11.9</text>
71 + <rect x="291.5" y="103.5" width="33.2" height="44.5" class="bar-6"><title>euneus: 16.7 (decode)</title></rect>
72 + <text x="308.1" y="100.5" text-anchor="middle" font-size="8" class="muted">16.7</text>
75 73 <text x="308.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 308.1,158.0)">euneus</text>
76 - <rect x="330.8" y="103.6" width="33.2" height="44.4" class="bar-7"><title>json: 11.1 (decode)</title></rect>
77 - <text x="347.4" y="100.6" text-anchor="middle" font-size="8" class="muted">11.1</text>
74 + <rect x="330.8" y="96.8" width="33.2" height="51.2" class="bar-7"><title>json: 19.2 (decode)</title></rect>
75 + <text x="347.4" y="93.8" text-anchor="middle" font-size="8" class="muted">19.2</text>
78 76 <text x="347.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 347.4,158.0)">json</text>
79 77 <line x1="50.0" y1="148.0" x2="370.0" y2="148.0" class="axis" stroke-width="1"/>
80 78 <text x="570.0" y="66.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">JSON encode — 1.3K</text>
  @@ -86,29 +84,29 @@
86 84 <text x="424.0" y="97.7" text-anchor="end" font-size="9" class="muted">20</text>
87 85 <line x1="430.0" y1="68.0" x2="750.0" y2="68.0" class="grid" stroke-width="1"/>
88 86 <text x="424.0" y="71.0" text-anchor="end" font-size="9" class="muted">30</text>
89 - <rect x="436.0" y="140.3" width="33.2" height="7.7" class="bar-glazer"><title>glazer: 2.9 (encode)</title></rect>
90 - <text x="452.6" y="137.3" text-anchor="middle" font-size="8" class="muted">2.9</text>
87 + <rect x="436.0" y="137.3" width="33.2" height="10.7" class="bar-glazer"><title>glazer: 4 (encode)</title></rect>
88 + <text x="452.6" y="134.3" text-anchor="middle" font-size="8" class="muted">4</text>
91 89 <text x="452.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 452.6,158.0)">glazer</text>
92 - <rect x="475.2" y="138.7" width="33.2" height="9.3" class="bar-1"><title>torque: 3.5 (encode)</title></rect>
93 - <text x="491.9" y="135.7" text-anchor="middle" font-size="8" class="muted">3.5</text>
90 + <rect x="475.2" y="131.5" width="33.2" height="16.5" class="bar-1"><title>torque: 6.2 (encode)</title></rect>
91 + <text x="491.9" y="128.5" text-anchor="middle" font-size="8" class="muted">6.2</text>
94 92 <text x="491.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 491.9,158.0)">torque</text>
95 - <rect x="514.5" y="125.6" width="33.2" height="22.4" class="bar-2"><title>simdjsone: 8.4 (encode)</title></rect>
96 - <text x="531.1" y="122.6" text-anchor="middle" font-size="8" class="muted">8.4</text>
93 + <rect x="514.5" y="114.4" width="33.2" height="33.6" class="bar-2"><title>simdjsone: 12.6 (encode)</title></rect>
94 + <text x="531.1" y="111.4" text-anchor="middle" font-size="8" class="muted">12.6</text>
97 95 <text x="531.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 531.1,158.0)">simdjsone</text>
98 - <rect x="553.8" y="131.5" width="33.2" height="16.5" class="bar-3"><title>jiffy: 6.2 (encode)</title></rect>
99 - <text x="570.4" y="128.5" text-anchor="middle" font-size="8" class="muted">6.2</text>
96 + <rect x="553.8" y="123.7" width="33.2" height="24.3" class="bar-3"><title>jiffy: 9.1 (encode)</title></rect>
97 + <text x="570.4" y="120.7" text-anchor="middle" font-size="8" class="muted">9.1</text>
100 98 <text x="570.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 570.4,158.0)">jiffy</text>
101 - <rect x="593.0" y="93.1" width="33.2" height="54.9" class="bar-4"><title>jason: 20.6 (encode)</title></rect>
102 - <text x="609.6" y="90.1" text-anchor="middle" font-size="8" class="muted">20.6</text>
99 + <rect x="593.0" y="81.3" width="33.2" height="66.7" class="bar-4"><title>jason: 25 (encode)</title></rect>
100 + <text x="609.6" y="78.3" text-anchor="middle" font-size="8" class="muted">25</text>
103 101 <text x="609.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 609.6,158.0)">jason</text>
104 - <rect x="632.2" y="91.5" width="33.2" height="56.5" class="bar-5"><title>thoas: 21.2 (encode)</title></rect>
105 - <text x="648.9" y="88.5" text-anchor="middle" font-size="8" class="muted">21.2</text>
102 + <rect x="632.2" y="68.3" width="33.2" height="79.7" class="bar-5"><title>thoas: 29.9 (encode)</title></rect>
103 + <text x="648.9" y="65.3" text-anchor="middle" font-size="8" class="muted">29.9</text>
106 104 <text x="648.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 648.9,158.0)">thoas</text>
107 - <rect x="671.5" y="122.7" width="33.2" height="25.3" class="bar-6"><title>euneus: 9.5 (encode)</title></rect>
108 - <text x="688.1" y="119.7" text-anchor="middle" font-size="8" class="muted">9.5</text>
105 + <rect x="671.5" y="109.1" width="33.2" height="38.9" class="bar-6"><title>euneus: 14.6 (encode)</title></rect>
106 + <text x="688.1" y="106.1" text-anchor="middle" font-size="8" class="muted">14.6</text>
109 107 <text x="688.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 688.1,158.0)">euneus</text>
110 - <rect x="710.8" y="125.9" width="33.2" height="22.1" class="bar-7"><title>json: 8.3 (encode)</title></rect>
111 - <text x="727.4" y="122.9" text-anchor="middle" font-size="8" class="muted">8.3</text>
108 + <rect x="710.8" y="115.2" width="33.2" height="32.8" class="bar-7"><title>json: 12.3 (encode)</title></rect>
109 + <text x="727.4" y="112.2" text-anchor="middle" font-size="8" class="muted">12.3</text>
112 110 <text x="727.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 727.4,158.0)">json</text>
113 111 <line x1="430.0" y1="148.0" x2="750.0" y2="148.0" class="axis" stroke-width="1"/>
114 112 <text x="190.0" y="204.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">YAML decode — 1.3K</text>
  @@ -120,17 +118,17 @@
120 118 <text x="44.0" y="235.7" text-anchor="end" font-size="9" class="muted">1000</text>
121 119 <line x1="50.0" y1="206.0" x2="370.0" y2="206.0" class="grid" stroke-width="1"/>
122 120 <text x="44.0" y="209.0" text-anchor="end" font-size="9" class="muted">1500</text>
123 - <rect x="56.0" y="283.5" width="56.8" height="2.5" class="bar-glazer"><title>glazer: 46 (decode)</title></rect>
124 - <text x="84.4" y="280.5" text-anchor="middle" font-size="8" class="muted">46</text>
121 + <rect x="56.0" y="284.9" width="56.8" height="1.1" class="bar-glazer"><title>glazer: 19.9 (decode)</title></rect>
122 + <text x="84.4" y="281.9" text-anchor="middle" font-size="8" class="muted">19.9</text>
125 123 <text x="84.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 84.4,296.0)">glazer</text>
126 - <rect x="118.8" y="278.8" width="56.8" height="7.2" class="bar-1"><title>yaml_rustler: 134.8 (decode)</title></rect>
127 - <text x="147.2" y="275.8" text-anchor="middle" font-size="8" class="muted">134.8</text>
124 + <rect x="118.8" y="280.5" width="56.8" height="5.5" class="bar-1"><title>yaml_rustler: 103.9 (decode)</title></rect>
125 + <text x="147.2" y="277.5" text-anchor="middle" font-size="8" class="muted">103.9</text>
128 126 <text x="147.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 147.2,296.0)">yaml_rustler</text>
129 - <rect x="181.6" y="276.2" width="56.8" height="9.8" class="bar-2"><title>fast_yaml: 183.6 (decode)</title></rect>
130 - <text x="210.0" y="273.2" text-anchor="middle" font-size="8" class="muted">183.6</text>
127 + <rect x="181.6" y="278.5" width="56.8" height="7.5" class="bar-2"><title>fast_yaml: 141.4 (decode)</title></rect>
128 + <text x="210.0" y="275.5" text-anchor="middle" font-size="8" class="muted">141.4</text>
131 129 <text x="210.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 210.0,296.0)">fast_yaml</text>
132 - <rect x="244.4" y="210.4" width="56.8" height="75.6" class="bar-3"><title>yamerl: 1418.3 (decode)</title></rect>
133 - <text x="272.8" y="207.4" text-anchor="middle" font-size="8" class="muted">1418.3</text>
130 + <rect x="244.4" y="206.7" width="56.8" height="79.3" class="bar-3"><title>yamerl: 1486.2 (decode)</title></rect>
131 + <text x="272.8" y="203.7" text-anchor="middle" font-size="8" class="muted">1486.2</text>
134 132 <text x="272.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 272.8,296.0)">yamerl</text>
135 133 <text x="335.6" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
136 134 <text x="335.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 335.6,296.0)">ymlr</text>
  @@ -144,18 +142,18 @@
144 142 <text x="424.0" y="235.7" text-anchor="end" font-size="9" class="muted">40</text>
145 143 <line x1="430.0" y1="206.0" x2="750.0" y2="206.0" class="grid" stroke-width="1"/>
146 144 <text x="424.0" y="209.0" text-anchor="end" font-size="9" class="muted">60</text>
147 - <rect x="436.0" y="271.7" width="56.8" height="14.3" class="bar-glazer"><title>glazer: 10.7 (encode)</title></rect>
148 - <text x="464.4" y="268.7" text-anchor="middle" font-size="8" class="muted">10.7</text>
145 + <rect x="436.0" y="275.5" width="56.8" height="10.5" class="bar-glazer"><title>glazer: 7.9 (encode)</title></rect>
146 + <text x="464.4" y="272.5" text-anchor="middle" font-size="8" class="muted">7.9</text>
149 147 <text x="464.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 464.4,296.0)">glazer</text>
150 148 <text x="527.2" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
151 149 <text x="527.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 527.2,296.0)">yaml_rustler</text>
152 - <rect x="561.6" y="223.9" width="56.8" height="62.1" class="bar-2"><title>fast_yaml: 46.6 (encode)</title></rect>
153 - <text x="590.0" y="220.9" text-anchor="middle" font-size="8" class="muted">46.6</text>
150 + <rect x="561.6" y="213.5" width="56.8" height="72.5" class="bar-2"><title>fast_yaml: 54.4 (encode)</title></rect>
151 + <text x="590.0" y="210.5" text-anchor="middle" font-size="8" class="muted">54.4</text>
154 152 <text x="590.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 590.0,296.0)">fast_yaml</text>
155 153 <text x="652.8" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
156 154 <text x="652.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 652.8,296.0)">yamerl</text>
157 - <rect x="687.2" y="236.5" width="56.8" height="49.5" class="bar-4"><title>ymlr: 37.1 (encode)</title></rect>
158 - <text x="715.6" y="233.5" text-anchor="middle" font-size="8" class="muted">37.1</text>
155 + <rect x="687.2" y="224.5" width="56.8" height="61.5" class="bar-4"><title>ymlr: 46.1 (encode)</title></rect>
156 + <text x="715.6" y="221.5" text-anchor="middle" font-size="8" class="muted">46.1</text>
159 157 <text x="715.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 715.6,296.0)">ymlr</text>
160 158 <line x1="430.0" y1="286.0" x2="750.0" y2="286.0" class="axis" stroke-width="1"/>
161 159 <text x="190.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV decode — 130.9K</text>
  @@ -167,39 +165,41 @@
167 165 <text x="44.0" y="373.7" text-anchor="end" font-size="9" class="muted">40000</text>
168 166 <line x1="50.0" y1="344.0" x2="370.0" y2="344.0" class="grid" stroke-width="1"/>
169 167 <text x="44.0" y="347.0" text-anchor="end" font-size="9" class="muted">60000</text>
170 - <rect x="56.0" y="422.9" width="72.5" height="1.1" class="bar-glazer"><title>glazer: 810.1 (decode)</title></rect>
171 - <text x="92.2" y="419.9" text-anchor="middle" font-size="8" class="muted">810.1</text>
168 + <rect x="56.0" y="422.3" width="72.5" height="1.7" class="bar-glazer"><title>glazer: 1289.6 (decode)</title></rect>
169 + <text x="92.2" y="419.3" text-anchor="middle" font-size="8" class="muted">1289.6</text>
172 170 <text x="92.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 92.2,434.0)">glazer</text>
173 - <rect x="134.5" y="419.0" width="72.5" height="5.0" class="bar-1"><title>nimble_csv: 3749.8 (decode)</title></rect>
174 - <text x="170.8" y="416.0" text-anchor="middle" font-size="8" class="muted">3749.8</text>
171 + <rect x="134.5" y="417.9" width="72.5" height="6.1" class="bar-1"><title>nimble_csv: 4582.9 (decode)</title></rect>
172 + <text x="170.8" y="414.9" text-anchor="middle" font-size="8" class="muted">4582.9</text>
175 173 <text x="170.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 170.8,434.0)">nimble_csv</text>
176 - <rect x="213.0" y="415.5" width="72.5" height="8.5" class="bar-2"><title>csv: 6341 (decode)</title></rect>
177 - <text x="249.2" y="412.5" text-anchor="middle" font-size="8" class="muted">6341</text>
174 + <rect x="213.0" y="412.9" width="72.5" height="11.1" class="bar-2"><title>csv: 8335.2 (decode)</title></rect>
175 + <text x="249.2" y="409.9" text-anchor="middle" font-size="8" class="muted">8335.2</text>
178 176 <text x="249.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 249.2,434.0)">csv</text>
179 - <rect x="291.5" y="370.5" width="72.5" height="53.5" class="bar-3"><title>erl_csv: 40115.1 (decode)</title></rect>
180 - <text x="327.8" y="367.5" text-anchor="middle" font-size="8" class="muted">40115.1</text>
177 + <rect x="291.5" y="350.7" width="72.5" height="73.3" class="bar-3"><title>erl_csv: 54950.5 (decode)</title></rect>
178 + <text x="327.8" y="347.7" text-anchor="middle" font-size="8" class="muted">54950.5</text>
181 179 <text x="327.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 327.8,434.0)">erl_csv</text>
182 180 <line x1="50.0" y1="424.0" x2="370.0" y2="424.0" class="axis" stroke-width="1"/>
183 181 <text x="570.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV encode — 130.9K</text>
184 182 <line x1="430.0" y1="424.0" x2="750.0" y2="424.0" class="grid" stroke-width="1"/>
185 183 <text x="424.0" y="427.0" text-anchor="end" font-size="9" class="muted">0</text>
186 - <line x1="430.0" y1="397.3" x2="750.0" y2="397.3" class="grid" stroke-width="1"/>
187 - <text x="424.0" y="400.3" text-anchor="end" font-size="9" class="muted">10000</text>
188 - <line x1="430.0" y1="370.7" x2="750.0" y2="370.7" class="grid" stroke-width="1"/>
189 - <text x="424.0" y="373.7" text-anchor="end" font-size="9" class="muted">20000</text>
184 + <line x1="430.0" y1="404.0" x2="750.0" y2="404.0" class="grid" stroke-width="1"/>
185 + <text x="424.0" y="407.0" text-anchor="end" font-size="9" class="muted">10000</text>
186 + <line x1="430.0" y1="384.0" x2="750.0" y2="384.0" class="grid" stroke-width="1"/>
187 + <text x="424.0" y="387.0" text-anchor="end" font-size="9" class="muted">20000</text>
188 + <line x1="430.0" y1="364.0" x2="750.0" y2="364.0" class="grid" stroke-width="1"/>
189 + <text x="424.0" y="367.0" text-anchor="end" font-size="9" class="muted">30000</text>
190 190 <line x1="430.0" y1="344.0" x2="750.0" y2="344.0" class="grid" stroke-width="1"/>
191 - <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">30000</text>
192 - <rect x="436.0" y="422.7" width="72.5" height="1.3" class="bar-glazer"><title>glazer: 483.4 (encode)</title></rect>
193 - <text x="472.2" y="419.7" text-anchor="middle" font-size="8" class="muted">483.4</text>
191 + <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">40000</text>
192 + <rect x="436.0" y="423.1" width="72.5" height="0.9" class="bar-glazer"><title>glazer: 469.5 (encode)</title></rect>
193 + <text x="472.2" y="420.1" text-anchor="middle" font-size="8" class="muted">469.5</text>
194 194 <text x="472.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 472.2,434.0)">glazer</text>
195 - <rect x="514.5" y="416.8" width="72.5" height="7.2" class="bar-1"><title>nimble_csv: 2709.7 (encode)</title></rect>
196 - <text x="550.8" y="413.8" text-anchor="middle" font-size="8" class="muted">2709.7</text>
195 + <rect x="514.5" y="417.6" width="72.5" height="6.4" class="bar-1"><title>nimble_csv: 3204.4 (encode)</title></rect>
196 + <text x="550.8" y="414.6" text-anchor="middle" font-size="8" class="muted">3204.4</text>
197 197 <text x="550.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 550.8,434.0)">nimble_csv</text>
198 - <rect x="593.0" y="378.8" width="72.5" height="45.2" class="bar-2"><title>csv: 16959.9 (encode)</title></rect>
199 - <text x="629.2" y="375.8" text-anchor="middle" font-size="8" class="muted">16959.9</text>
198 + <rect x="593.0" y="375.2" width="72.5" height="48.8" class="bar-2"><title>csv: 24393.9 (encode)</title></rect>
199 + <text x="629.2" y="372.2" text-anchor="middle" font-size="8" class="muted">24393.9</text>
200 200 <text x="629.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 629.2,434.0)">csv</text>
201 - <rect x="671.5" y="360.6" width="72.5" height="63.4" class="bar-3"><title>erl_csv: 23761.5 (encode)</title></rect>
202 - <text x="707.8" y="357.6" text-anchor="middle" font-size="8" class="muted">23761.5</text>
201 + <rect x="671.5" y="354.8" width="72.5" height="69.2" class="bar-3"><title>erl_csv: 34607.9 (encode)</title></rect>
202 + <text x="707.8" y="351.8" text-anchor="middle" font-size="8" class="muted">34607.9</text>
203 203 <text x="707.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 707.8,434.0)">erl_csv</text>
204 204 <line x1="430.0" y1="424.0" x2="750.0" y2="424.0" class="axis" stroke-width="1"/>
205 205 </svg>
\ No newline at end of file
  @@ -44,71 +44,69 @@
44 44 <text x="190.0" y="66.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">JSON decode — 0.1K</text>
45 45 <line x1="50.0" y1="148.0" x2="370.0" y2="148.0" class="grid" stroke-width="1"/>
46 46 <text x="44.0" y="151.0" text-anchor="end" font-size="9" class="muted">0</text>
47 - <line x1="50.0" y1="128.0" x2="370.0" y2="128.0" class="grid" stroke-width="1"/>
48 - <text x="44.0" y="131.0" text-anchor="end" font-size="9" class="muted">1</text>
49 - <line x1="50.0" y1="108.0" x2="370.0" y2="108.0" class="grid" stroke-width="1"/>
50 - <text x="44.0" y="111.0" text-anchor="end" font-size="9" class="muted">2</text>
51 - <line x1="50.0" y1="88.0" x2="370.0" y2="88.0" class="grid" stroke-width="1"/>
52 - <text x="44.0" y="91.0" text-anchor="end" font-size="9" class="muted">3</text>
47 + <line x1="50.0" y1="121.3" x2="370.0" y2="121.3" class="grid" stroke-width="1"/>
48 + <text x="44.0" y="124.3" text-anchor="end" font-size="9" class="muted">2</text>
49 + <line x1="50.0" y1="94.7" x2="370.0" y2="94.7" class="grid" stroke-width="1"/>
50 + <text x="44.0" y="97.7" text-anchor="end" font-size="9" class="muted">4</text>
53 51 <line x1="50.0" y1="68.0" x2="370.0" y2="68.0" class="grid" stroke-width="1"/>
54 - <text x="44.0" y="71.0" text-anchor="end" font-size="9" class="muted">4</text>
55 - <rect x="56.0" y="130.0" width="33.2" height="18.0" class="bar-glazer"><title>glazer: 0.9 (decode)</title></rect>
56 - <text x="72.6" y="127.0" text-anchor="middle" font-size="8" class="muted">0.9</text>
52 + <text x="44.0" y="71.0" text-anchor="end" font-size="9" class="muted">6</text>
53 + <rect x="56.0" y="132.0" width="33.2" height="16.0" class="bar-glazer"><title>glazer: 1.2 (decode)</title></rect>
54 + <text x="72.6" y="129.0" text-anchor="middle" font-size="8" class="muted">1.2</text>
57 55 <text x="72.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 72.6,158.0)">glazer</text>
58 - <rect x="95.2" y="112.0" width="33.2" height="36.0" class="bar-1"><title>torque: 1.8 (decode)</title></rect>
59 - <text x="111.9" y="109.0" text-anchor="middle" font-size="8" class="muted">1.8</text>
56 + <rect x="95.2" y="125.3" width="33.2" height="22.7" class="bar-1"><title>torque: 1.7 (decode)</title></rect>
57 + <text x="111.9" y="122.3" text-anchor="middle" font-size="8" class="muted">1.7</text>
60 58 <text x="111.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 111.9,158.0)">torque</text>
61 - <rect x="134.5" y="124.0" width="33.2" height="24.0" class="bar-2"><title>simdjsone: 1.2 (decode)</title></rect>
62 - <text x="151.1" y="121.0" text-anchor="middle" font-size="8" class="muted">1.2</text>
59 + <rect x="134.5" y="122.7" width="33.2" height="25.3" class="bar-2"><title>simdjsone: 1.9 (decode)</title></rect>
60 + <text x="151.1" y="119.7" text-anchor="middle" font-size="8" class="muted">1.9</text>
63 61 <text x="151.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 151.1,158.0)">simdjsone</text>
64 - <rect x="173.8" y="112.0" width="33.2" height="36.0" class="bar-3"><title>jiffy: 1.8 (decode)</title></rect>
65 - <text x="190.4" y="109.0" text-anchor="middle" font-size="8" class="muted">1.8</text>
62 + <rect x="173.8" y="114.7" width="33.2" height="33.3" class="bar-3"><title>jiffy: 2.5 (decode)</title></rect>
63 + <text x="190.4" y="111.7" text-anchor="middle" font-size="8" class="muted">2.5</text>
66 64 <text x="190.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 190.4,158.0)">jiffy</text>
67 - <rect x="213.0" y="92.0" width="33.2" height="56.0" class="bar-4"><title>jason: 2.8 (decode)</title></rect>
68 - <text x="229.6" y="89.0" text-anchor="middle" font-size="8" class="muted">2.8</text>
65 + <rect x="213.0" y="89.3" width="33.2" height="58.7" class="bar-4"><title>jason: 4.4 (decode)</title></rect>
66 + <text x="229.6" y="86.3" text-anchor="middle" font-size="8" class="muted">4.4</text>
69 67 <text x="229.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 229.6,158.0)">jason</text>
70 - <rect x="252.2" y="96.0" width="33.2" height="52.0" class="bar-5"><title>thoas: 2.6 (decode)</title></rect>
71 - <text x="268.9" y="93.0" text-anchor="middle" font-size="8" class="muted">2.6</text>
68 + <rect x="252.2" y="105.3" width="33.2" height="42.7" class="bar-5"><title>thoas: 3.2 (decode)</title></rect>
69 + <text x="268.9" y="102.3" text-anchor="middle" font-size="8" class="muted">3.2</text>
72 70 <text x="268.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 268.9,158.0)">thoas</text>
73 - <rect x="291.5" y="86.0" width="33.2" height="62.0" class="bar-6"><title>euneus: 3.1 (decode)</title></rect>
74 - <text x="308.1" y="83.0" text-anchor="middle" font-size="8" class="muted">3.1</text>
71 + <rect x="291.5" y="94.7" width="33.2" height="53.3" class="bar-6"><title>euneus: 4 (decode)</title></rect>
72 + <text x="308.1" y="91.7" text-anchor="middle" font-size="8" class="muted">4</text>
75 73 <text x="308.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 308.1,158.0)">euneus</text>
76 - <rect x="330.8" y="98.0" width="33.2" height="50.0" class="bar-7"><title>json: 2.5 (decode)</title></rect>
77 - <text x="347.4" y="95.0" text-anchor="middle" font-size="8" class="muted">2.5</text>
74 + <rect x="330.8" y="94.7" width="33.2" height="53.3" class="bar-7"><title>json: 4 (decode)</title></rect>
75 + <text x="347.4" y="91.7" text-anchor="middle" font-size="8" class="muted">4</text>
78 76 <text x="347.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 347.4,158.0)">json</text>
79 77 <line x1="50.0" y1="148.0" x2="370.0" y2="148.0" class="axis" stroke-width="1"/>
80 78 <text x="570.0" y="66.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">JSON encode — 0.1K</text>
81 79 <line x1="430.0" y1="148.0" x2="750.0" y2="148.0" class="grid" stroke-width="1"/>
82 80 <text x="424.0" y="151.0" text-anchor="end" font-size="9" class="muted">0</text>
83 81 <line x1="430.0" y1="121.3" x2="750.0" y2="121.3" class="grid" stroke-width="1"/>
84 - <text x="424.0" y="124.3" text-anchor="end" font-size="9" class="muted">1</text>
82 + <text x="424.0" y="124.3" text-anchor="end" font-size="9" class="muted">2</text>
85 83 <line x1="430.0" y1="94.7" x2="750.0" y2="94.7" class="grid" stroke-width="1"/>
86 - <text x="424.0" y="97.7" text-anchor="end" font-size="9" class="muted">2</text>
84 + <text x="424.0" y="97.7" text-anchor="end" font-size="9" class="muted">4</text>
87 85 <line x1="430.0" y1="68.0" x2="750.0" y2="68.0" class="grid" stroke-width="1"/>
88 - <text x="424.0" y="71.0" text-anchor="end" font-size="9" class="muted">3</text>
89 - <rect x="436.0" y="126.7" width="33.2" height="21.3" class="bar-glazer"><title>glazer: 0.8 (encode)</title></rect>
90 - <text x="452.6" y="123.7" text-anchor="middle" font-size="8" class="muted">0.8</text>
86 + <text x="424.0" y="71.0" text-anchor="end" font-size="9" class="muted">6</text>
87 + <rect x="436.0" y="134.7" width="33.2" height="13.3" class="bar-glazer"><title>glazer: 1 (encode)</title></rect>
88 + <text x="452.6" y="131.7" text-anchor="middle" font-size="8" class="muted">1</text>
91 89 <text x="452.6" y="158.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 452.6,158.0)">glazer</text>
92 - <rect x="475.2" y="110.7" width="33.2" height="37.3" class="bar-1"><title>torque: 1.4 (encode)</title></rect>
93 - <text x="491.9" y="107.7" text-anchor="middle" font-size="8" class="muted">1.4</text>
90 + <rect x="475.2" y="130.7" width="33.2" height="17.3" class="bar-1"><title>torque: 1.3 (encode)</title></rect>
91 + <text x="491.9" y="127.7" text-anchor="middle" font-size="8" class="muted">1.3</text>
94 92 <text x="491.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 491.9,158.0)">torque</text>
95 - <rect x="514.5" y="92.0" width="33.2" height="56.0" class="bar-2"><title>simdjsone: 2.1 (encode)</title></rect>
96 - <text x="531.1" y="89.0" text-anchor="middle" font-size="8" class="muted">2.1</text>
93 + <rect x="514.5" y="100.0" width="33.2" height="48.0" class="bar-2"><title>simdjsone: 3.6 (encode)</title></rect>
94 + <text x="531.1" y="97.0" text-anchor="middle" font-size="8" class="muted">3.6</text>
97 95 <text x="531.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 531.1,158.0)">simdjsone</text>
98 - <rect x="553.8" y="97.3" width="33.2" height="50.7" class="bar-3"><title>jiffy: 1.9 (encode)</title></rect>
99 - <text x="570.4" y="94.3" text-anchor="middle" font-size="8" class="muted">1.9</text>
96 + <rect x="553.8" y="97.3" width="33.2" height="50.7" class="bar-3"><title>jiffy: 3.8 (encode)</title></rect>
97 + <text x="570.4" y="94.3" text-anchor="middle" font-size="8" class="muted">3.8</text>
100 98 <text x="570.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 570.4,158.0)">jiffy</text>
101 - <rect x="593.0" y="89.3" width="33.2" height="58.7" class="bar-4"><title>jason: 2.2 (encode)</title></rect>
102 - <text x="609.6" y="86.3" text-anchor="middle" font-size="8" class="muted">2.2</text>
99 + <rect x="593.0" y="109.3" width="33.2" height="38.7" class="bar-4"><title>jason: 2.9 (encode)</title></rect>
100 + <text x="609.6" y="106.3" text-anchor="middle" font-size="8" class="muted">2.9</text>
103 101 <text x="609.6" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 609.6,158.0)">jason</text>
104 - <rect x="632.2" y="86.7" width="33.2" height="61.3" class="bar-5"><title>thoas: 2.3 (encode)</title></rect>
105 - <text x="648.9" y="83.7" text-anchor="middle" font-size="8" class="muted">2.3</text>
102 + <rect x="632.2" y="96.0" width="33.2" height="52.0" class="bar-5"><title>thoas: 3.9 (encode)</title></rect>
103 + <text x="648.9" y="93.0" text-anchor="middle" font-size="8" class="muted">3.9</text>
106 104 <text x="648.9" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 648.9,158.0)">thoas</text>
107 - <rect x="671.5" y="92.0" width="33.2" height="56.0" class="bar-6"><title>euneus: 2.1 (encode)</title></rect>
108 - <text x="688.1" y="89.0" text-anchor="middle" font-size="8" class="muted">2.1</text>
105 + <rect x="671.5" y="86.7" width="33.2" height="61.3" class="bar-6"><title>euneus: 4.6 (encode)</title></rect>
106 + <text x="688.1" y="83.7" text-anchor="middle" font-size="8" class="muted">4.6</text>
109 107 <text x="688.1" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 688.1,158.0)">euneus</text>
110 - <rect x="710.8" y="100.0" width="33.2" height="48.0" class="bar-7"><title>json: 1.8 (encode)</title></rect>
111 - <text x="727.4" y="97.0" text-anchor="middle" font-size="8" class="muted">1.8</text>
108 + <rect x="710.8" y="88.0" width="33.2" height="60.0" class="bar-7"><title>json: 4.5 (encode)</title></rect>
109 + <text x="727.4" y="85.0" text-anchor="middle" font-size="8" class="muted">4.5</text>
112 110 <text x="727.4" y="158.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 727.4,158.0)">json</text>
113 111 <line x1="430.0" y1="148.0" x2="750.0" y2="148.0" class="axis" stroke-width="1"/>
114 112 <text x="190.0" y="204.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">YAML decode — 0.1K</text>
  @@ -122,17 +120,17 @@
122 120 <text x="44.0" y="229.0" text-anchor="end" font-size="9" class="muted">600</text>
123 121 <line x1="50.0" y1="206.0" x2="370.0" y2="206.0" class="grid" stroke-width="1"/>
124 122 <text x="44.0" y="209.0" text-anchor="end" font-size="9" class="muted">800</text>
125 - <rect x="56.0" y="285.1" width="56.8" height="0.9" class="bar-glazer"><title>glazer: 9.1 (decode)</title></rect>
126 - <text x="84.4" y="282.1" text-anchor="middle" font-size="8" class="muted">9.1</text>
123 + <rect x="56.0" y="284.9" width="56.8" height="1.1" class="bar-glazer"><title>glazer: 11.5 (decode)</title></rect>
124 + <text x="84.4" y="281.9" text-anchor="middle" font-size="8" class="muted">11.5</text>
127 125 <text x="84.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 84.4,296.0)">glazer</text>
128 - <rect x="118.8" y="284.6" width="56.8" height="1.4" class="bar-1"><title>yaml_rustler: 14.4 (decode)</title></rect>
129 - <text x="147.2" y="281.6" text-anchor="middle" font-size="8" class="muted">14.4</text>
126 + <rect x="118.8" y="284.3" width="56.8" height="1.7" class="bar-1"><title>yaml_rustler: 16.9 (decode)</title></rect>
127 + <text x="147.2" y="281.3" text-anchor="middle" font-size="8" class="muted">16.9</text>
130 128 <text x="147.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 147.2,296.0)">yaml_rustler</text>
131 - <rect x="181.6" y="283.0" width="56.8" height="3.0" class="bar-2"><title>fast_yaml: 29.9 (decode)</title></rect>
132 - <text x="210.0" y="280.0" text-anchor="middle" font-size="8" class="muted">29.9</text>
129 + <rect x="181.6" y="283.3" width="56.8" height="2.7" class="bar-2"><title>fast_yaml: 26.7 (decode)</title></rect>
130 + <text x="210.0" y="280.3" text-anchor="middle" font-size="8" class="muted">26.7</text>
133 131 <text x="210.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 210.0,296.0)">fast_yaml</text>
134 - <rect x="244.4" y="210.7" width="56.8" height="75.3" class="bar-3"><title>yamerl: 753.5 (decode)</title></rect>
135 - <text x="272.8" y="207.7" text-anchor="middle" font-size="8" class="muted">753.5</text>
132 + <rect x="244.4" y="218.4" width="56.8" height="67.6" class="bar-3"><title>yamerl: 676.1 (decode)</title></rect>
133 + <text x="272.8" y="215.4" text-anchor="middle" font-size="8" class="muted">676.1</text>
136 134 <text x="272.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 272.8,296.0)">yamerl</text>
137 135 <text x="335.6" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
138 136 <text x="335.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 335.6,296.0)">ymlr</text>
  @@ -140,70 +138,72 @@
140 138 <text x="570.0" y="204.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">YAML encode — 0.1K</text>
141 139 <line x1="430.0" y1="286.0" x2="750.0" y2="286.0" class="grid" stroke-width="1"/>
142 140 <text x="424.0" y="289.0" text-anchor="end" font-size="9" class="muted">0</text>
143 - <line x1="430.0" y1="259.3" x2="750.0" y2="259.3" class="grid" stroke-width="1"/>
144 - <text x="424.0" y="262.3" text-anchor="end" font-size="9" class="muted">5</text>
145 - <line x1="430.0" y1="232.7" x2="750.0" y2="232.7" class="grid" stroke-width="1"/>
146 - <text x="424.0" y="235.7" text-anchor="end" font-size="9" class="muted">10</text>
141 + <line x1="430.0" y1="266.0" x2="750.0" y2="266.0" class="grid" stroke-width="1"/>
142 + <text x="424.0" y="269.0" text-anchor="end" font-size="9" class="muted">2</text>
143 + <line x1="430.0" y1="246.0" x2="750.0" y2="246.0" class="grid" stroke-width="1"/>
144 + <text x="424.0" y="249.0" text-anchor="end" font-size="9" class="muted">4</text>
145 + <line x1="430.0" y1="226.0" x2="750.0" y2="226.0" class="grid" stroke-width="1"/>
146 + <text x="424.0" y="229.0" text-anchor="end" font-size="9" class="muted">6</text>
147 147 <line x1="430.0" y1="206.0" x2="750.0" y2="206.0" class="grid" stroke-width="1"/>
148 - <text x="424.0" y="209.0" text-anchor="end" font-size="9" class="muted">15</text>
149 - <rect x="436.0" y="280.1" width="56.8" height="5.9" class="bar-glazer"><title>glazer: 1.1 (encode)</title></rect>
150 - <text x="464.4" y="277.1" text-anchor="middle" font-size="8" class="muted">1.1</text>
148 + <text x="424.0" y="209.0" text-anchor="end" font-size="9" class="muted">8</text>
149 + <rect x="436.0" y="264.0" width="56.8" height="22.0" class="bar-glazer"><title>glazer: 2.2 (encode)</title></rect>
150 + <text x="464.4" y="261.0" text-anchor="middle" font-size="8" class="muted">2.2</text>
151 151 <text x="464.4" y="296.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 464.4,296.0)">glazer</text>
152 152 <text x="527.2" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
153 153 <text x="527.2" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 527.2,296.0)">yaml_rustler</text>
154 - <rect x="561.6" y="241.7" width="56.8" height="44.3" class="bar-2"><title>fast_yaml: 8.3 (encode)</title></rect>
155 - <text x="590.0" y="238.7" text-anchor="middle" font-size="8" class="muted">8.3</text>
154 + <rect x="561.6" y="210.0" width="56.8" height="76.0" class="bar-2"><title>fast_yaml: 7.6 (encode)</title></rect>
155 + <text x="590.0" y="207.0" text-anchor="middle" font-size="8" class="muted">7.6</text>
156 156 <text x="590.0" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 590.0,296.0)">fast_yaml</text>
157 157 <text x="652.8" y="282.0" text-anchor="middle" font-size="8" class="faint">N/A</text>
158 158 <text x="652.8" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 652.8,296.0)">yamerl</text>
159 - <rect x="687.2" y="207.1" width="56.8" height="78.9" class="bar-4"><title>ymlr: 14.8 (encode)</title></rect>
160 - <text x="715.6" y="204.1" text-anchor="middle" font-size="8" class="muted">14.8</text>
159 + <rect x="687.2" y="227.0" width="56.8" height="59.0" class="bar-4"><title>ymlr: 5.9 (encode)</title></rect>
160 + <text x="715.6" y="224.0" text-anchor="middle" font-size="8" class="muted">5.9</text>
161 161 <text x="715.6" y="296.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 715.6,296.0)">ymlr</text>
162 162 <line x1="430.0" y1="286.0" x2="750.0" y2="286.0" class="axis" stroke-width="1"/>
163 163 <text x="190.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV decode — 1.3K</text>
164 164 <line x1="50.0" y1="424.0" x2="370.0" y2="424.0" class="grid" stroke-width="1"/>
165 165 <text x="44.0" y="427.0" text-anchor="end" font-size="9" class="muted">0</text>
166 166 <line x1="50.0" y1="404.0" x2="370.0" y2="404.0" class="grid" stroke-width="1"/>
167 - <text x="44.0" y="407.0" text-anchor="end" font-size="9" class="muted">100</text>
167 + <text x="44.0" y="407.0" text-anchor="end" font-size="9" class="muted">200</text>
168 168 <line x1="50.0" y1="384.0" x2="370.0" y2="384.0" class="grid" stroke-width="1"/>
169 - <text x="44.0" y="387.0" text-anchor="end" font-size="9" class="muted">200</text>
169 + <text x="44.0" y="387.0" text-anchor="end" font-size="9" class="muted">400</text>
170 170 <line x1="50.0" y1="364.0" x2="370.0" y2="364.0" class="grid" stroke-width="1"/>
171 - <text x="44.0" y="367.0" text-anchor="end" font-size="9" class="muted">300</text>
171 + <text x="44.0" y="367.0" text-anchor="end" font-size="9" class="muted">600</text>
172 172 <line x1="50.0" y1="344.0" x2="370.0" y2="344.0" class="grid" stroke-width="1"/>
173 - <text x="44.0" y="347.0" text-anchor="end" font-size="9" class="muted">400</text>
174 - <rect x="56.0" y="420.6" width="72.5" height="3.4" class="bar-glazer"><title>glazer: 17.2 (decode)</title></rect>
175 - <text x="92.2" y="417.6" text-anchor="middle" font-size="8" class="muted">17.2</text>
173 + <text x="44.0" y="347.0" text-anchor="end" font-size="9" class="muted">800</text>
174 + <rect x="56.0" y="422.9" width="72.5" height="1.1" class="bar-glazer"><title>glazer: 10.7 (decode)</title></rect>
175 + <text x="92.2" y="419.9" text-anchor="middle" font-size="8" class="muted">10.7</text>
176 176 <text x="92.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 92.2,434.0)">glazer</text>
177 - <rect x="134.5" y="414.9" width="72.5" height="9.1" class="bar-1"><title>nimble_csv: 45.7 (decode)</title></rect>
178 - <text x="170.8" y="411.9" text-anchor="middle" font-size="8" class="muted">45.7</text>
177 + <rect x="134.5" y="419.5" width="72.5" height="4.5" class="bar-1"><title>nimble_csv: 44.8 (decode)</title></rect>
178 + <text x="170.8" y="416.5" text-anchor="middle" font-size="8" class="muted">44.8</text>
179 179 <text x="170.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 170.8,434.0)">nimble_csv</text>
180 - <rect x="213.0" y="406.0" width="72.5" height="18.0" class="bar-2"><title>csv: 89.8 (decode)</title></rect>
181 - <text x="249.2" y="403.0" text-anchor="middle" font-size="8" class="muted">89.8</text>
180 + <rect x="213.0" y="414.1" width="72.5" height="9.9" class="bar-2"><title>csv: 99.3 (decode)</title></rect>
181 + <text x="249.2" y="411.1" text-anchor="middle" font-size="8" class="muted">99.3</text>
182 182 <text x="249.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 249.2,434.0)">csv</text>
183 - <rect x="291.5" y="347.5" width="72.5" height="76.5" class="bar-3"><title>erl_csv: 382.3 (decode)</title></rect>
184 - <text x="327.8" y="344.5" text-anchor="middle" font-size="8" class="muted">382.3</text>
183 + <rect x="291.5" y="353.4" width="72.5" height="70.6" class="bar-3"><title>erl_csv: 705.5 (decode)</title></rect>
184 + <text x="327.8" y="350.4" text-anchor="middle" font-size="8" class="muted">705.5</text>
185 185 <text x="327.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 327.8,434.0)">erl_csv</text>
186 186 <line x1="50.0" y1="424.0" x2="370.0" y2="424.0" class="axis" stroke-width="1"/>
187 187 <text x="570.0" y="342.0" text-anchor="middle" font-size="13" font-weight="bold" class="fg">CSV encode — 1.3K</text>
188 188 <line x1="430.0" y1="424.0" x2="750.0" y2="424.0" class="grid" stroke-width="1"/>
189 189 <text x="424.0" y="427.0" text-anchor="end" font-size="9" class="muted">0</text>
190 190 <line x1="430.0" y1="397.3" x2="750.0" y2="397.3" class="grid" stroke-width="1"/>
191 - <text x="424.0" y="400.3" text-anchor="end" font-size="9" class="muted">100</text>
191 + <text x="424.0" y="400.3" text-anchor="end" font-size="9" class="muted">200</text>
192 192 <line x1="430.0" y1="370.7" x2="750.0" y2="370.7" class="grid" stroke-width="1"/>
193 - <text x="424.0" y="373.7" text-anchor="end" font-size="9" class="muted">200</text>
193 + <text x="424.0" y="373.7" text-anchor="end" font-size="9" class="muted">400</text>
194 194 <line x1="430.0" y1="344.0" x2="750.0" y2="344.0" class="grid" stroke-width="1"/>
195 - <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">300</text>
196 - <rect x="436.0" y="422.1" width="72.5" height="1.9" class="bar-glazer"><title>glazer: 7.3 (encode)</title></rect>
197 - <text x="472.2" y="419.1" text-anchor="middle" font-size="8" class="muted">7.3</text>
195 + <text x="424.0" y="347.0" text-anchor="end" font-size="9" class="muted">600</text>
196 + <rect x="436.0" y="423.6" width="72.5" height="0.4" class="bar-glazer"><title>glazer: 3.3 (encode)</title></rect>
197 + <text x="472.2" y="420.6" text-anchor="middle" font-size="8" class="muted">3.3</text>
198 198 <text x="472.2" y="434.0" text-anchor="end" font-size="9" class="fg" font-weight="bold" transform="rotate(-35 472.2,434.0)">glazer</text>
199 - <rect x="514.5" y="415.7" width="72.5" height="8.3" class="bar-1"><title>nimble_csv: 31.3 (encode)</title></rect>
200 - <text x="550.8" y="412.7" text-anchor="middle" font-size="8" class="muted">31.3</text>
199 + <rect x="514.5" y="418.8" width="72.5" height="5.2" class="bar-1"><title>nimble_csv: 38.8 (encode)</title></rect>
200 + <text x="550.8" y="415.8" text-anchor="middle" font-size="8" class="muted">38.8</text>
201 201 <text x="550.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 550.8,434.0)">nimble_csv</text>
202 - <rect x="593.0" y="375.5" width="72.5" height="48.5" class="bar-2"><title>csv: 182 (encode)</title></rect>
203 - <text x="629.2" y="372.5" text-anchor="middle" font-size="8" class="muted">182</text>
202 + <rect x="593.0" y="389.7" width="72.5" height="34.3" class="bar-2"><title>csv: 257.3 (encode)</title></rect>
203 + <text x="629.2" y="386.7" text-anchor="middle" font-size="8" class="muted">257.3</text>
204 204 <text x="629.2" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 629.2,434.0)">csv</text>
205 - <rect x="671.5" y="348.0" width="72.5" height="76.0" class="bar-3"><title>erl_csv: 285.1 (encode)</title></rect>
206 - <text x="707.8" y="345.0" text-anchor="middle" font-size="8" class="muted">285.1</text>
205 + <rect x="671.5" y="367.0" width="72.5" height="57.0" class="bar-3"><title>erl_csv: 427.4 (encode)</title></rect>
206 + <text x="707.8" y="364.0" text-anchor="middle" font-size="8" class="muted">427.4</text>
207 207 <text x="707.8" y="434.0" text-anchor="end" font-size="9" class="fg" transform="rotate(-35 707.8,434.0)">erl_csv</text>
208 208 <line x1="430.0" y1="424.0" x2="750.0" y2="424.0" class="axis" stroke-width="1"/>
209 209 </svg>
\ No newline at end of file
Loading more files…