jsonfast

package module
v0.2.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 10 Imported by: 0

README

go-jsonfast

A zero-allocation JSON builder and scanner for Go, tailored for fixed schemas and ultra-high-throughput pipelines.

Go Version Lint Test Security Fuzz Go Reference Zero Dependencies

Features

  • Zero-allocation Builder with Acquire / Release pool and WarmPool for pre-seeding; the zero value is ready to use.
  • Pre-computed FieldKey for static field names (typed string, literal-constructible).
  • io.Writer / io.WriterTo on both Builder and BatchWriter for pipeline interop.
  • NDJSON BatchWriter with pool, pre-seed, and write sinks.
  • SWAR-accelerated string escape and scanner (SkipValueAt, SkipStringAt, SkipBracedAt); portable byte-wise fallback via -tags=purego or on alignment-strict architectures.
  • Direct-scan FindField, IterateFields, IterateArray, IterateStringArray (zero-alloc borrow); strict scanning — trailing commas and trailing content are rejected.
  • FlattenObject / AddFlattenedMapField for dot-notation flattening.
  • RFC 3339 time formatting without time.Format allocations, UTC-normalised.
  • RFC 8259 compliance: invalid UTF-8 → U+FFFD (Unicode maximal-subpart, identical to encoding/json); NaN / ±Inf → null; float output byte-compatible with encoding/json.
  • IsStructuralJSON single-pass validator for untrusted payloads.
  • testing.AllocsPerRun CI gate: every hot path is asserted zero-alloc.
  • Native Go fuzzers for every scanner and validator, plus encoding/json round-trip oracles.
  • Profile-guided optimisation: default.pgo tracked; make pgo regenerates.

Requires Go 1.25+.

Quick start

package main

import (
    "fmt"
    "time"

    "github.com/ubyte-source/go-jsonfast"
)

func main() {
    b := jsonfast.Acquire()
    defer jsonfast.Release(b)

    b.BeginObject()
    b.AddStringField("message", "hello world")
    b.AddIntField("severity", 3)
    b.AddTimeRFC3339Field("timestamp", time.Now())
    b.EndObject()

    fmt.Println(string(b.Bytes()))
}
Reuse without allocation
b := jsonfast.New(512)

b.BeginObject()
b.AddStringField("k", "v1")
b.EndObject()
process(b.Bytes())

b.Reset() // ready for reuse, no allocation
b.BeginObject()
b.AddStringField("k", "v2")
b.EndObject()
process(b.Bytes())
NDJSON batching
bw := jsonfast.AcquireBatchWriter()
defer jsonfast.ReleaseBatchWriter(bw)

b := jsonfast.Acquire()
defer jsonfast.Release(b)

for _, msg := range messages {
    b.Reset()
    b.BeginObject()
    b.AddStringField("msg", msg)
    b.EndObject()
    bw.Append(b.Bytes())
}

// bw.Bytes() is the NDJSON payload; stream via io.WriterTo:
if _, err := bw.WriteTo(conn); err != nil {
    return err
}
Pre-computed field keys

Static field names known at init time can be lifted to FieldKey to eliminate the per-call quoting. Construct via NewFieldKey or as a typed string literal:

var (
    keyMessage  = jsonfast.NewFieldKey("message")
    keySeverity = jsonfast.NewFieldKey("severity")

    // Equivalent literal form (matching the documented layout):
    keyVersion jsonfast.FieldKey = `,"version":`
)

b := jsonfast.Acquire()
b.BeginObject()
b.AddStringFieldKey(keyMessage, "hello world")
b.AddIntFieldKey(keySeverity, 3)
b.AddIntFieldKey(keyVersion, 1)
b.EndObject()
jsonfast.Release(b)
Warm-up before the hot path

Pre-seed the pools to smooth tail latency at start-up:

jsonfast.WarmPool(128)
jsonfast.WarmBatchWriterPool(32)
Map flattening
nested := map[string]map[string]string{
    "sd@123": {"key1": "val1", "key2": "val2"},
}
flat := jsonfast.FlattenMap(nested, nil)
// flat["sd@123.key1"] == "val1"
Structural validation
if !jsonfast.IsStructuralJSON(payload) {
    return fmt.Errorf("not a structurally valid JSON object/array")
}

API overview

See the package documentation for the full godoc.

Builder
Function Description
New(capacity int) *Builder New builder with initial capacity (default 256).
Acquire() *Builder Take a builder from the pool.
Release(*Builder) Return a builder to the pool.
WarmPool(n int) Pre-seed the pool with n builders.
(*Builder).Reset() Clear for reuse; buffer retained.
(*Builder).Bytes() []byte Current payload (aliases internal buffer).
(*Builder).Len() int Current byte length.
(*Builder).Grow(n int) Ensure n spare bytes of capacity.
(*Builder).Write(p []byte) (int, error) io.Writer: append p unchanged.
(*Builder).WriteTo(w io.Writer) (int64, error) io.WriterTo: flush payload.
Field methods (string-keyed)
Method Output
BeginObject() / EndObject() { / }
BeginObjectField(name) / EndObjectField() "name":{ / }, manages inner separator state
AddStringField(name, value) "name":"value" with escaping
AddStringArrayField(name, values) "name":["v1","v2",...] with escaping
AddIntField(name, v int) "name":123
AddInt64Field(name, v int64) "name":123
AddUint64Field(name, v uint64) "name":123
AddFloat64Field(name, v float64) "name":3.14 — encoding/json parity: 'e' notation for |v| < 1e-6 or ≥ 1e21; NaN/±Inf → null
AddBoolField(name, v bool) "name":true / "name":false
AddNullField(name) "name":null
AddTimeRFC3339Field(name, t time.Time) "name":"YYYY-MM-DDThh:mm:ss[.fffffffff]Z"
AddRawJSONField(name, rawJSON []byte) "name":<raw> — no escaping; rawJSON must already be valid JSON (caller verifies).
AddNestedStringMapField(name, m) "name":{outer:{inner:"v",...},...}, sorted
AddStringMapObject(m, rawJSONKey) Writes m as a standalone JSON object (no field name), keys sorted. The entry whose key equals rawJSONKey is emitted as raw JSON when IsStructuralJSON accepts the value, otherwise as a regular string field.
AddStringMapObjectField(name, m, rawJSONKey) "name":{...} form of AddStringMapObject.
AddTimeRFC3339OffsetField(name, t time.Time) "name":"YYYY-MM-DDThh:mm:ss[.fffffffff]<±HH:MM|Z>" — preserves t's offset, truncated to whole minutes; falls back to the UTC form when a negative offset would need a pre-epoch wall date.
AddFlattenedMapField(m) flat "outer.inner":"value" fields, sorted

Field name requirement: safe ASCII (no escaping required). For untrusted names, escape via EscapeString or compose via AppendEscapedString + AppendRawString.

AddRawJSONField and AppendRaw do not inspect their payload. For untrusted bytes, validate first with IsStructuralJSON (or encoding/json.Valid).

Pre-computed FieldKey methods

Each entry has the same output as the corresponding string-keyed Add*Field method; the only difference is that the ,"name": prefix is emitted from the cached FieldKey instead of being escaped per call.

Function Notes
type FieldKey string Typed string holding ,"name":
NewFieldKey(name string) FieldKey Factory; call at init time. Panics if name needs JSON escaping.
AddStringFieldKey / AddStringArrayFieldKey String / string-array
AddIntFieldKey / AddInt64FieldKey / AddUint64FieldKey Integers
AddFloat64FieldKey / AddBoolFieldKey / AddNullFieldKey Other scalars
AddRawJSONFieldKey Raw JSON value (caller verifies validity)
AddTimeRFC3339FieldKey / AddTimeRFC3339OffsetFieldKey UTC / offset-preserving
BeginObjectFieldKey Open a nested object; pair with EndObjectField
Raw appenders
Method Description
AppendRaw([]byte) Append raw bytes, no escaping or framing
AppendRawString(string) Append raw string, no escaping or framing
AppendEscapedString(string) Append with JSON escaping, zero-alloc
AddRawBytesField(name, value []byte) "name":<value> with name as raw bytes
Scanner
Function Description
IterateFields(data []byte, fn) bool Callback for each "key":value pair. True only for one complete object (trailing whitespace allowed); trailing commas/content rejected.
IterateFieldsString(s string, fn) bool Same, string input.
FindField(data []byte, key string) ([]byte, bool) Direct lookup without callback; stops at the first match.
FindFieldString(s, key string) ([]byte, bool) Same, string input.
IterateArray(data []byte, fn) bool Callback for each element. Same strictness as IterateFields.
IterateArrayString(s string, fn) bool Same, string input.
IterateStringArray(data []byte, fn func(val string) bool) bool Zero-alloc borrow of the raw string body (escapes not decoded); see lifetime note.
IterateStringArrayString(s string, fn) bool Same, string input.
FlattenObject(b *Builder, data []byte) bool Recursive flatten into builder (≤ 64 levels). Leaf names only — colliding leaves produce duplicate keys.
SkipWS / SkipValueAt / SkipStringAt / SkipBracedAt Low-level SWAR scanners.
IsStructuralJSON(s string) bool Single-pass grammar validator with trailing-content rejection.
EscapeString(s string) string Returns s unchanged if already safe; allocates only when escape needed.

IterateStringArray passes a zero-allocation string view aliasing the input data. The view is only valid for the duration of the callback — clone via strings.Clone to retain.

Scalar decoders

The scanners surface raw JSON bytes; these helpers convert one such slice into the corresponding Go value. They are typically composed with FindField or with the per-field callback of IterateFields.

Function Description
DecodeString(raw []byte) (string, bool) Decodes a quoted JSON string, resolving escapes and surrogate pairs. Rejects raw control bytes, unescaped quotes, and trailing content.
DecodeBool(raw []byte) (value, ok bool) Decodes the literal true / false.
DecodeInt64(raw []byte) (int64, bool) Strict integer: rejects fractional, exponent, leading +, and leading-zero forms (only 0 and -0 accepted).
DecodeUint64(raw []byte) (uint64, bool) Same rules as DecodeInt64, plus rejects negative input.
DecodeFloat64(raw []byte) (float64, bool) Any RFC 8259 number; overflow returns ok=false.
v, ok := jsonfast.FindField(data, "severity")
if !ok { return errMissing }
sev, ok := jsonfast.DecodeInt64(v)
if !ok { return errBadType }
BatchWriter (NDJSON)
Function Description
NewBatchWriter(capacity int) *BatchWriter New writer with initial capacity (default 4096).
AcquireBatchWriter() / ReleaseBatchWriter(*BatchWriter) Pool API.
WarmBatchWriterPool(n int) Pre-seed the pool.
(*BatchWriter).Append([]byte) / .AppendString(string) Record + newline.
(*BatchWriter).Write(p []byte) (int, error) io.Writer: one record per call.
(*BatchWriter).WriteTo(w io.Writer) (int64, error) io.WriterTo: flush payload.
(*BatchWriter).Bytes() / .Len() / .Count() / .Reset() / .Grow(n) Buffer management.
FlattenMap
Function Description
FlattenMap(m, dst map[string]string) map[string]string Materialise a flat map; pass dst to avoid re-allocating.
Out-of-scope

This package deliberately omits:

  • a generic JSON-to-DOM decoder (no map[string]any / []any decoder);
  • a generic any-encoder for the Builder;
  • a whitespace compaction helper.

Fixed-schema callers should use Iterate*/FindField and the typed Add*Field methods directly. Callers who legitimately need to handle arbitrary dynamic data should reach for encoding/json:

data, err := json.Marshal(v) // arbitrary `any`
if err != nil { … }
b.AppendRaw(data)            // splice straight into the Builder

For whitespace compaction use the standard library's encoding/json.Compact.

Benchmarks

Measured on a 32-core x86-64 server, Go 1.25.9, with default.pgo enabled.

BenchmarkBuilder_FullSyslogObject-32             231 ns/op     0 B/op    0 allocs/op
BenchmarkBuilder_FullSyslogObject_FieldKey-32    145 ns/op     0 B/op    0 allocs/op
BenchmarkBuilder_EscapeString_PureASCII-32        34 ns/op     0 B/op    0 allocs/op
BenchmarkBuilder_NestedStringMapField-32         392 ns/op     0 B/op    0 allocs/op
BenchmarkBuilder_AcquireRelease-32                36 ns/op     0 B/op    0 allocs/op
BenchmarkIterateFields-32                        144 ns/op     0 B/op    0 allocs/op
BenchmarkFindField-32                            126 ns/op     0 B/op    0 allocs/op
BenchmarkIterateArray_Strings100-32             1271 ns/op     0 B/op    0 allocs/op
BenchmarkParallel_FindField-32                   8.6 ns/op   9.7 GB/s   0 allocs/op
BenchmarkParallel_EscapeString_PureASCII-32      1.9 ns/op    62 GB/s   0 allocs/op

Run with:

make bench        # sequential benchmarks
make parallel     # RunParallel benchmarks across CPU counts

Testing and gates

make test       # all tests with race detector
make alloc      # zero-allocation assertions (AllocsPerRun)
make fuzz       # native Go fuzzing, 30s per target (override with FUZZTIME=10m)
make cover      # HTML coverage report
make lint       # staticcheck -checks=all + golangci-lint (0 issues, 0 nolint for complexity)
make pgo        # regenerate default.pgo
make ci         # vet + lint + test + alloc + cover + bench

Operational limits

Parameter Value Rationale
Builder pool max retained capacity 256 KiB Bounds per-instance retention.
BatchWriter pool max retained capacity 4 MiB Bounds per-instance retention for batch sinks.
FlattenObject maximum depth 64 Defends against pathological nesting.
AddNestedStringMapField stack buffer 8 keys (both levels) Zero-alloc path; larger maps get one heap buffer per oversized map.
AddFlattenedMapField stack buffer 16 total entries Zero-alloc path; larger maps get one heap buffer.
EscapeString stack buffer 506 bytes after escape margin Zero-alloc for the common case.

Design principles

  1. Zero-alloc is a constraint, not a goal. Every hot path is gated by testing.AllocsPerRun; a regression fails CI.
  2. Fixed schemas only. This is not a general-purpose JSON encoder.
  3. Deterministic output. Keys are sorted wherever the caller may dedupe or cache the output.
  4. No dependencies. Pure Go, no cgo, no code generation.
  5. The buffer is the API. All writes land in []byte; no intermediate representations.

Project layout

go-jsonfast/
├── jsonfast.go        # Builder: pool, field methods, escape, time, integer formatting
├── scan.go            # Scanner: Skip*, Iterate*, FindField, FlattenObject, IsStructuralJSON
├── swar.go            # SWAR constants and byte-classification helpers
├── swar_unsafe.go     # Single unaligned 8-byte load (amd64/arm64/ppc64le/s390x)
├── swar_purego.go     # Portable byte-wise load (other architectures or -tags=purego)
├── flatten.go         # FlattenMap and AddFlattenedMapField
├── ndjson.go          # BatchWriter: pool, append, io.Writer / io.WriterTo
├── doc.go             # Package documentation
├── alloc_test.go      # Zero-allocation CI gate (testing.AllocsPerRun)
├── parallel_test.go   # RunParallel benchmarks for pool-contention profiling
├── io_test.go         # io.Writer / io.WriterTo interface contract
├── fuzz_test.go       # Native Go fuzzers for scanners and validators
├── *_test.go          # Unit tests, example tests
├── default.pgo        # Profile-guided optimisation profile
├── .golangci.yml      # Strict linter configuration
└── Makefile           # test, bench, fuzz, lint, cover, pgo, ci

Contributing

See CONTRIBUTING.md. For security issues, see SECURITY.md.

Versioning

We use SemVer. Releases are tracked in the repository tags.

Authors

See also the list of contributors.

License

MIT — see LICENSE.

Support

If go-jsonfast is useful for your pipelines, consider supporting the work:

Buy Me A Coffee

Documentation

Overview

Package jsonfast is a zero-allocation JSON builder and scanner for fixed schemas.

Usage

b := jsonfast.Acquire()
b.BeginObject()
b.AddStringField("message", "hello")
b.AddIntField("severity", 3)
b.AddTimeRFC3339Field("timestamp", time.Now())
b.EndObject()
data := b.Bytes()
jsonfast.Release(b)

The zero value of Builder is ready to use.

Pre-computed keys

var keyMessage = jsonfast.NewFieldKey("message")
b.AddStringFieldKey(keyMessage, "hello")

Field names passed to Add*Field are JSON-escaped on output, so any Go string is safe. NewFieldKey caches the prefix verbatim and therefore requires a safe-ASCII name (printable ASCII excluding '"' and '\\'); it panics on any other input, like regexp.MustCompile.

Numbers

Float fields match encoding/json output: shortest round-trip form, exponent notation for magnitudes below 1e-6 or at least 1e21, and null for NaN and ±Inf. Integral floats in (-1e18, 1e18) are emitted in exact integer form.

Scanning

IterateFields, FindField, IterateArray, IterateStringArray parse JSON structurally without building a DOM. IterateFields and IterateArray return true only for a single complete value with at most trailing whitespace; trailing commas and trailing content are rejected. FindField stops at the first match. The *String variants take a string input and alias its backing memory; slices/strings passed to callbacks must not outlive the call (clone via strings.Clone to retain).

Raw JSON value bytes returned by the scanners can be promoted to Go values via DecodeString, DecodeBool, DecodeInt64, DecodeUint64, and DecodeFloat64. Each returns ok=false on malformed input; DecodeString rejects raw control bytes and unescaped quotes, and the integer decoders also reject fractional, exponent, leading '+', and leading-zero forms.

Out-of-scope

jsonfast intentionally omits a generic JSON-to-DOM decoder (map[string]any / []any), a generic any-encoder for Builder, and a whitespace-compacting helper. Fixed-schema callers should use the Iterate* / FindField scanners and the typed Add*Field methods directly. When dynamic data is genuinely required, encode it with encoding/json and splice the result via Builder.AppendRaw or Builder.AddRawJSONField; for whitespace compaction use encoding/json.Compact.

NDJSON

BatchWriter appends JSON records separated by '\n' and implements io.Writer (one record per Write) and io.WriterTo.

Portability

The SWAR fast paths use a single unaligned 8-byte load on architectures that support it (amd64, arm64, ppc64le, s390x) and a byte-wise fallback elsewhere. Build with -tags=purego to force the fallback on every architecture.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeBool added in v0.2.0

func DecodeBool(raw []byte) (value, ok bool)

DecodeBool decodes "true" or "false".

func DecodeFloat64 added in v0.2.0

func DecodeFloat64(raw []byte) (float64, bool)

DecodeFloat64 decodes any RFC 8259 number into a float64. NaN and Inf are rejected.

func DecodeInt64 added in v0.2.0

func DecodeInt64(raw []byte) (int64, bool)

DecodeInt64 decodes a JSON integer. Rejects fractional forms, exponents, leading '+' and leading zeros (except "0" and "-0").

func DecodeString added in v0.2.0

func DecodeString(raw []byte) (string, bool)

DecodeString decodes a JSON string (including surrounding quotes) into its Go form. The input must be exactly one grammar-valid JSON string: raw control bytes, unescaped quotes, and trailing content are rejected. The returned string is a fresh allocation.

func DecodeUint64 added in v0.2.0

func DecodeUint64(raw []byte) (uint64, bool)

DecodeUint64 decodes a non-negative JSON integer. Same rejection rules as DecodeInt64.

func EscapeString

func EscapeString(s string) string

EscapeString returns s with JSON escaping. If s is already safe ASCII it is returned unchanged (zero allocation). Invalid UTF-8 sequences are replaced with U+FFFD, matching encoding/json.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	escaped := jsonfast.EscapeString(`She said "hello" and left`)
	fmt.Println(escaped)
}
Output:
She said \"hello\" and left

func FindField

func FindField(data []byte, key string) ([]byte, bool)

FindField returns the raw value bytes for the first top-level field matching key, or (nil, false) if not found. Keys with JSON escape sequences are decoded on the fly. The scan stops at the first match, so malformed content after it is not detected.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	data := []byte(`{"hostname":"fw01","severity":4,"message":"ok"}`)
	if val, ok := jsonfast.FindField(data, "severity"); ok {
		fmt.Printf("severity=%s\n", val)
	}
}
Output:
severity=4

func FindFieldString added in v0.0.2

func FindFieldString(s, key string) ([]byte, bool)

FindFieldString is FindField with a string input. The returned slice aliases s and must not be mutated.

func FlattenMap

func FlattenMap(m map[string]map[string]string, dst map[string]string) map[string]string

FlattenMap flattens a nested map into outer.inner → value form. If dst is nil a new map is allocated sized to the total entry count.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	m := map[string]map[string]string{
		"host": {"name": "fw01", "ip": "10.0.0.1"},
	}
	flat := jsonfast.FlattenMap(m, nil)
	fmt.Println(flat["host.name"])
	fmt.Println(flat["host.ip"])
}
Output:
fw01
10.0.0.1

func FlattenObject

func FlattenObject(b *Builder, data []byte) bool

FlattenObject recursively flattens a JSON object's leaves into b (up to 64 levels deep), discarding the enclosing keys: nested leaves are emitted under their own names, so colliding leaf names produce duplicate keys. Non-object input is skipped silently; trailing content after the object is rejected.

func IsStructuralJSON added in v0.1.0

func IsStructuralJSON(s string) bool

IsStructuralJSON reports whether s is a grammar-valid JSON object or array (every nested value checked) with no trailing content. Duplicate keys are not rejected; invalid UTF-8 bytes are passed through.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	fmt.Println(jsonfast.IsStructuralJSON(`{"key":"value"}`))
	fmt.Println(jsonfast.IsStructuralJSON(`not json`))
	fmt.Println(jsonfast.IsStructuralJSON(`{not json}`))
}
Output:
true
false
false

func IterateArray added in v0.0.2

func IterateArray(data []byte, fn func(element []byte) bool) bool

IterateArray calls fn for each element; element is the raw JSON bytes of the value. It returns true only when data holds exactly one complete array (trailing whitespace allowed) and fn never returned false.

func IterateArrayString added in v0.0.2

func IterateArrayString(s string, fn func(element []byte) bool) bool

IterateArrayString is IterateArray with a string input.

func IterateFields

func IterateFields(data []byte, fn func(key, value []byte) bool) bool

IterateFields calls fn for each top-level field of the JSON object in data. key includes the surrounding quotes; value is the raw JSON bytes. It returns true only when data holds exactly one complete object (trailing whitespace allowed) and fn never returned false. Values are structurally balanced but not grammar-validated; promote them with the Decode* helpers or check with IsStructuralJSON.

func IterateFieldsString

func IterateFieldsString(s string, fn func(key, value []byte) bool) bool

IterateFieldsString is IterateFields with a string input. The slices passed to fn alias s and must not be mutated.

func IterateStringArray added in v0.0.2

func IterateStringArray(data []byte, fn func(val string) bool) bool

IterateStringArray calls fn for each string element. val is the raw string body between the quotes: escape sequences are not decoded (use DecodeString on IterateArray elements when escapes may occur). val aliases the input and is only valid for the duration of the callback; use strings.Clone to retain. Non-string elements abort the iteration.

func IterateStringArrayString added in v0.0.2

func IterateStringArrayString(s string, fn func(val string) bool) bool

IterateStringArrayString is IterateStringArray with a string input.

func Release

func Release(b *Builder)

Release returns b to the pool. Buffers larger than 256 KB are discarded.

func ReleaseBatchWriter

func ReleaseBatchWriter(bw *BatchWriter)

ReleaseBatchWriter returns bw to the pool. Buffers larger than 4 MB are discarded.

func SkipBracedAt

func SkipBracedAt(data []byte, i int, opener, closer byte) (int, bool)

SkipBracedAt skips a balanced opener/closer pair starting at data[i].

func SkipStringAt

func SkipStringAt(data []byte, i int) (int, bool)

SkipStringAt skips a JSON string starting at data[i] (which must be '"') and returns the index past the closing quote. Raw control bytes (< 0x20) are rejected.

func SkipValueAt

func SkipValueAt(data []byte, i int) (int, bool)

SkipValueAt skips one JSON value starting at data[i] and returns the index past it.

func SkipWS

func SkipWS(data []byte, i int) int

SkipWS returns the index of the first non-whitespace byte at or after i.

func WarmBatchWriterPool added in v0.1.0

func WarmBatchWriterPool(n int)

WarmBatchWriterPool pre-allocates n BatchWriters and returns them to the pool.

func WarmPool added in v0.1.0

func WarmPool(n int)

WarmPool pre-allocates n Builders and returns them to the pool.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	jsonfast.WarmPool(128)

	b := jsonfast.Acquire()
	b.BeginObject()
	b.AddStringField("warmed", "yes")
	b.EndObject()
	fmt.Println(string(b.Bytes()))
	jsonfast.Release(b)
}
Output:
{"warmed":"yes"}

Types

type BatchWriter

type BatchWriter struct {
	// contains filtered or unexported fields
}

BatchWriter accumulates newline-delimited JSON records. Not safe for concurrent use.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	bw := jsonfast.NewBatchWriter(256)
	bw.Append([]byte(`{"line":1}`))
	bw.Append([]byte(`{"line":2}`))

	fmt.Print(string(bw.Bytes()))
}
Output:
{"line":1}
{"line":2}

func AcquireBatchWriter

func AcquireBatchWriter() *BatchWriter

AcquireBatchWriter returns a BatchWriter from the pool.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	bw := jsonfast.AcquireBatchWriter()
	defer jsonfast.ReleaseBatchWriter(bw)

	bw.Append([]byte(`{"pooled":true}`))
	fmt.Print(string(bw.Bytes()))
}
Output:
{"pooled":true}

func NewBatchWriter

func NewBatchWriter(capacity int) *BatchWriter

NewBatchWriter returns a BatchWriter with the given initial capacity. Non-positive capacities are clamped to 4096.

func (*BatchWriter) Append

func (w *BatchWriter) Append(record []byte)

Append writes record followed by '\n'.

func (*BatchWriter) AppendString

func (w *BatchWriter) AppendString(record string)

AppendString writes record followed by '\n'.

func (*BatchWriter) Bytes

func (w *BatchWriter) Bytes() []byte

Bytes returns the accumulated payload. The slice aliases the internal buffer.

func (*BatchWriter) Count

func (w *BatchWriter) Count() int

Count returns the number of records in the batch.

func (*BatchWriter) Grow

func (w *BatchWriter) Grow(n int)

Grow ensures at least n bytes of spare capacity.

func (*BatchWriter) Len

func (w *BatchWriter) Len() int

Len returns the current byte length.

func (*BatchWriter) Reset

func (w *BatchWriter) Reset()

Reset clears the batch contents while retaining the backing array.

func (*BatchWriter) Write added in v0.1.0

func (w *BatchWriter) Write(p []byte) (int, error)

Write implements io.Writer by appending p as one NDJSON record.

func (*BatchWriter) WriteTo added in v0.1.0

func (w *BatchWriter) WriteTo(target io.Writer) (int64, error)

WriteTo implements io.WriterTo. The batch state is unchanged.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder appends JSON into a reusable byte slice. The zero value is ready to use. Not safe for concurrent use.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	b := jsonfast.New(256)
	b.BeginObject()
	b.AddStringField("message", "Hello, World!")
	b.AddIntField("severity", 5)
	b.AddBoolField("active", true)
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"message":"Hello, World!","severity":5,"active":true}
Example (WithTimestamp)
package main

import (
	"fmt"
	"time"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	b := jsonfast.New(256)
	ts := time.Date(2024, 3, 15, 14, 30, 45, 0, time.UTC)

	b.BeginObject()
	b.AddStringField("msg", "test")
	b.AddTimeRFC3339Field("ts", ts)
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"msg":"test","ts":"2024-03-15T14:30:45Z"}

func Acquire

func Acquire() *Builder

Acquire returns a Builder from the pool, ready for use.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	b := jsonfast.Acquire()
	defer jsonfast.Release(b)

	b.BeginObject()
	b.AddStringField("source", "pool")
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"source":"pool"}

func New

func New(capacity int) *Builder

New returns a Builder with the given initial capacity. Non-positive capacities are clamped to 256.

func (*Builder) AddBoolField

func (b *Builder) AddBoolField(name string, v bool)

AddBoolField adds "name":true or "name":false.

func (*Builder) AddBoolFieldKey

func (b *Builder) AddBoolFieldKey(k FieldKey, v bool)

AddBoolFieldKey adds "name":true or "name":false using a pre-computed key.

func (*Builder) AddFlattenedMapField

func (b *Builder) AddFlattenedMapField(m map[string]map[string]string)

AddFlattenedMapField emits nested map data as sorted "outer.inner":"value" fields into the current object. Up to 16 total entries fit in a stack buffer; larger maps allocate once.

func (*Builder) AddFloat64Field

func (b *Builder) AddFloat64Field(name string, v float64)

AddFloat64Field adds "name":<float64>. NaN and ±Inf are emitted as null.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	b := jsonfast.New(64)
	b.BeginObject()
	b.AddFloat64Field("pi", 3.14159)
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"pi":3.14159}

func (*Builder) AddFloat64FieldKey

func (b *Builder) AddFloat64FieldKey(k FieldKey, v float64)

AddFloat64FieldKey adds "name":<float64> using a pre-computed key.

func (*Builder) AddInt64Field

func (b *Builder) AddInt64Field(name string, v int64)

AddInt64Field adds "name":<int64>.

func (*Builder) AddInt64FieldKey

func (b *Builder) AddInt64FieldKey(k FieldKey, v int64)

AddInt64FieldKey adds "name":<int64> using a pre-computed key.

func (*Builder) AddIntField

func (b *Builder) AddIntField(name string, v int)

AddIntField adds "name":<int>.

func (*Builder) AddIntFieldKey

func (b *Builder) AddIntFieldKey(k FieldKey, v int)

AddIntFieldKey adds "name":<int> using a pre-computed key.

func (*Builder) AddNestedStringMapField

func (b *Builder) AddNestedStringMapField(name string, m map[string]map[string]string)

AddNestedStringMapField adds "name":{outer:{inner:"v"}}, keys sorted at both levels.

func (*Builder) AddNullField

func (b *Builder) AddNullField(name string)

AddNullField adds "name":null.

func (*Builder) AddNullFieldKey

func (b *Builder) AddNullFieldKey(k FieldKey)

AddNullFieldKey adds "name":null using a pre-computed key.

func (*Builder) AddRawBytesField

func (b *Builder) AddRawBytesField(name, value []byte)

AddRawBytesField adds "name":<value> where name and value are copied verbatim.

func (*Builder) AddRawJSONField

func (b *Builder) AddRawJSONField(name string, rawJSON []byte)

AddRawJSONField adds "name":<raw> without escaping. rawJSON must be well-formed JSON.

func (*Builder) AddRawJSONFieldKey

func (b *Builder) AddRawJSONFieldKey(k FieldKey, rawJSON []byte)

AddRawJSONFieldKey adds "name":<raw> using a pre-computed key.

func (*Builder) AddStringArrayField added in v0.2.0

func (b *Builder) AddStringArrayField(name string, values []string)

AddStringArrayField adds "name":["v1","v2",...] with value escaping.

func (*Builder) AddStringArrayFieldKey added in v0.2.0

func (b *Builder) AddStringArrayFieldKey(k FieldKey, values []string)

AddStringArrayFieldKey adds "name":["v1",...] using a pre-computed key.

func (*Builder) AddStringField

func (b *Builder) AddStringField(name, value string)

AddStringField adds "name":"value" with value escaping.

func (*Builder) AddStringFieldKey

func (b *Builder) AddStringFieldKey(k FieldKey, value string)

AddStringFieldKey adds "name":"value" using a pre-computed key.

func (*Builder) AddStringMapObject

func (b *Builder) AddStringMapObject(m map[string]string, rawJSONKey string)

AddStringMapObject writes m as a standalone JSON object (no name, no leading comma). Keys are sorted. If rawJSONKey is non-empty, values for that key are embedded as raw JSON when IsStructuralJSON accepts them.

func (*Builder) AddStringMapObjectField added in v0.2.0

func (b *Builder) AddStringMapObjectField(name string, m map[string]string, rawJSONKey string)

AddStringMapObjectField adds "name":{...} with m encoded as a JSON object. Semantics match AddStringMapObject.

func (*Builder) AddTimeRFC3339Field

func (b *Builder) AddTimeRFC3339Field(name string, t time.Time)

AddTimeRFC3339Field adds "name":"<RFC3339>" in UTC. Years are clamped to [0, 9999]; pre-epoch timestamps are clamped to the epoch.

func (*Builder) AddTimeRFC3339FieldKey

func (b *Builder) AddTimeRFC3339FieldKey(k FieldKey, t time.Time)

AddTimeRFC3339FieldKey adds "name":"<RFC3339>" in UTC using a pre-computed key.

func (*Builder) AddTimeRFC3339OffsetField added in v0.2.0

func (b *Builder) AddTimeRFC3339OffsetField(name string, t time.Time)

AddTimeRFC3339OffsetField adds "name":"<RFC3339>" preserving the input timezone offset (Z, +HH:MM, or -HH:MM). Offsets are truncated to whole minutes (RFC 3339 cannot express seconds). If a negative offset would require a pre-epoch wall date, the UTC form of the same instant is emitted instead.

func (*Builder) AddTimeRFC3339OffsetFieldKey added in v0.2.0

func (b *Builder) AddTimeRFC3339OffsetFieldKey(k FieldKey, t time.Time)

AddTimeRFC3339OffsetFieldKey adds "name":"<RFC3339>" using a pre-computed key. Semantics match AddTimeRFC3339OffsetField.

func (*Builder) AddUint64Field

func (b *Builder) AddUint64Field(name string, v uint64)

AddUint64Field adds "name":<uint64>.

func (*Builder) AddUint64FieldKey

func (b *Builder) AddUint64FieldKey(k FieldKey, v uint64)

AddUint64FieldKey adds "name":<uint64> using a pre-computed key.

func (*Builder) AppendEscapedString

func (b *Builder) AppendEscapedString(s string)

AppendEscapedString appends s with JSON escaping (no surrounding quotes).

func (*Builder) AppendRaw

func (b *Builder) AppendRaw(p []byte)

AppendRaw appends raw bytes.

func (*Builder) AppendRawString

func (b *Builder) AppendRawString(s string)

AppendRawString appends a raw string.

func (*Builder) BeginObject

func (b *Builder) BeginObject()

BeginObject writes '{' and resets the field separator state.

func (*Builder) BeginObjectField added in v0.2.0

func (b *Builder) BeginObjectField(name string)

BeginObjectField opens a nested object as "name":{, ready for inner Add*Field calls. Pair with EndObjectField.

func (*Builder) BeginObjectFieldKey added in v0.2.0

func (b *Builder) BeginObjectFieldKey(k FieldKey)

BeginObjectFieldKey opens a nested object with a pre-computed key.

func (*Builder) Bytes

func (b *Builder) Bytes() []byte

Bytes returns the accumulated bytes. The slice aliases the internal buffer and must not be used after Reset or Release.

func (*Builder) EndObject

func (b *Builder) EndObject()

EndObject writes '}'.

func (*Builder) EndObjectField added in v0.2.0

func (b *Builder) EndObjectField()

EndObjectField closes a nested object opened by BeginObjectField or BeginObjectFieldKey and restores the outer separator state.

func (*Builder) Grow

func (b *Builder) Grow(n int)

Grow ensures at least n bytes of spare capacity.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	b := jsonfast.New(16)
	b.Grow(1024)
	b.BeginObject()
	b.AddStringField("k", "v")
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"k":"v"}

func (*Builder) Len

func (b *Builder) Len() int

Len returns the current buffer length.

func (*Builder) Reset

func (b *Builder) Reset()

Reset clears the buffer contents while retaining the backing array.

func (*Builder) Write added in v0.1.0

func (b *Builder) Write(p []byte) (int, error)

Write implements io.Writer. It never returns an error.

func (*Builder) WriteTo added in v0.1.0

func (b *Builder) WriteTo(w io.Writer) (int64, error)

WriteTo implements io.WriterTo. The Builder state is unchanged.

type FieldKey

type FieldKey string

FieldKey is a pre-computed field prefix in the form `,"name":`. Construct via NewFieldKey or as a typed string literal matching that layout exactly; the methods index into it and panic on malformed or empty keys.

func NewFieldKey

func NewFieldKey(name string) FieldKey

NewFieldKey returns a FieldKey for the given name. The name must be safe ASCII (printable, excluding '"' and '\\'); NewFieldKey panics otherwise, since the prefix is cached verbatim and an unsafe name would silently corrupt every object that uses it. Call at init time, like regexp.MustCompile.

Example
package main

import (
	"fmt"

	"github.com/ubyte-source/go-jsonfast"
)

func main() {
	var keyLevel = jsonfast.NewFieldKey("level")

	b := jsonfast.New(64)
	b.BeginObject()
	b.AddIntFieldKey(keyLevel, 3)
	b.EndObject()

	fmt.Println(string(b.Bytes()))
}
Output:
{"level":3}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL