Glazejson - fast JSON encoder/decoder for Erlang or Elixir

I released a new JSON encoding/decoding library that uses NIF C++ backend and consistently outperforms most of other implementations.

Fast Erlang NIF JSON encoder/decoder with a hand-rolled recursive-descent decoder and direct term-to-JSON encoder that produce/consume native Erlang terms in a single pass. Inspired by the glaze C++ library, glazer has matured into a standalone implementation with no external C++ dependencies.

Features

  • Decoding straight to Erlang terms: maps, lists, binaries, integers (including bignums), floats, booleans, and null

  • Encoding Erlang terms straight to JSON, including big integers

  • Incremental/streaming decoding of partial input (e.g. NDJSON over a socket) via stream_decoder/0,1, stream_feed/2, stream_eof/1

  • Configurable representation of JSON null and JSON object keys

  • minify/1 and prettify/1 helpers

  • Standalone big-integer encode/decode helpers (encode_bigint/1, decode_bigint/1)

(numbers in µs)
JSON files:   twitter (616.7K)     twitter2 (758.0K)     openrtb (1.2K)      esad (1.3K)         small (0.1K)
               decode   encode     decode   encode     decode   encode     decode   encode     decode   encode
---------------------------------------------------------------------------------------------------------------------
glazejson     10097.9   3947.9    14904.2   8186.0       17.4     12.5       14.8      8.7        1.3      1.5
torque        10151.7   4358.7    12899.5   6798.9       18.3     13.2       19.9      7.1        4.5      1.7
simdjsone     10345.9   7541.2    18973.3  13482.5       25.6     27.5       19.5     18.5        1.7      4.5
jiffy         30645.2   4347.6    51053.1   9500.1       50.0     28.6       32.2     19.0        7.4      4.2
jason         21005.7  12918.1    40277.2  25064.8       56.4     26.2       33.7     22.1        6.0      3.7
thoas         21151.4  13779.6    41390.0  25625.0       57.4     29.9       35.0     26.7        7.5      3.8
euneus        20488.9  12319.9    31853.9  25111.0       40.7     32.7       25.2     19.0        7.3      3.3
json          19887.1  11679.8    30902.8  24087.7       41.5     26.9       40.1     10.6        4.8      4.1

Docs: glazer

Github: glazer

6 Likes
➜ rebar3 version
rebar 3.27.0 on Erlang/OTP 29 Erts 17.0
➜ cat glazejson-in-source-build.patch 
From: redacted
Date: Mon, 8 Jun 2026 00:00:00 +0000
Subject: [PATCH] Makefile: use out-of-source cmake build dir

When glazejson is consumed as a rebar3 dependency, the build fails with:

    CMake Error at _deps/glaze-src/cmake/prelude.cmake:4 (message):
      In-source builds are not supported.

Root cause: the `make nif` pre-hook runs `cmake -S c_src -B $(BUILD_DIR)`
with `BUILD_DIR = $(REBAR_BUILD_DIR)/lib/glazejson/c_src`. rebar3 runs the
hook from the copied dependency root (`_build/default/lib/glazejson`) and
exports `REBAR_BUILD_DIR=_build/default`, so both the source dir (`-S c_src`)
and the build dir (`-B`) resolve to the *same* path:

    _build/default/lib/glazejson/c_src

glaze's prelude.cmake rejects that as an in-source build. It only works in
glazejson's own checkout, where `_build/default/lib/glazejson/c_src` differs
from `c_src`; as a dependency, rebar copies the tree and the two collapse.

Fix: point the cmake build directory at a sibling of `c_src` instead of
`c_src` itself, so `-S` and `-B` are always distinct.

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 0000000..0000001 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 REBAR_BUILD_DIR ?= _build/default
-BUILD_DIR  ?= $(REBAR_BUILD_DIR)/lib/glazejson/c_src
+BUILD_DIR  ?= $(REBAR_BUILD_DIR)/lib/glazejson/cmake_build
 PRIV_DIR   := $(abspath priv)
 BUILD_TYPE ?= Release
 NIF_DEBUG  ?= 0
--
2.43.0

Do you plane to add support for streaming?

Fixed. Thank you.

1 Like

See Streaming section

thanks wasn’t there when I checked the doc.

Looks great!

Does it create sub-binaries or decoded result is a copy that is not related to the original JSON binary?

No, it makes copies. Sub-binaries pin the entire parent binary in memory until the sub-binary is garbage collected. For small inputs the copy cost is trivial anyway. For large inputs that go to dirty schedulers the GC pressure from pinning the parent is the dominant concern.

2 Likes

That was exactly the concern, thank you for clarifying :+1: