internal/encoder/json/fast/strconv.go

package fast

import (
	"math"
	"strconv"
)

// This file hosts integer and float encoding helpers that complement the
// pooled Buffer type. They exist so that the hot path in the JSON encoder
// can avoid importing strconv directly (allowing the rest of the tree to be
// compiled without it during TinyGo experiments).

// Base-10 lookup for two-digit integers. Useful for formatting timestamps
// without allocating through strconv.Itoa in the common case of small
// values such as minutes, seconds, and milliseconds.
var twoDigits = [100][2]byte{}

func init() {
	for i := 0; i < 100; i++ {
		twoDigits[i][0] = byte('0' + i/10)
		twoDigits[i][1] = byte('0' + i%10)
	}
}

// AppendTwoDigits writes v zero-padded to exactly two bytes. It panics if v
// is outside [0,99], which is fine for the intended callers (Hour/Minute/
// Second accessors from the time package).
func AppendTwoDigits(b *Buffer, v int) {
	if v < 0 || v > 99 {
		panic("fast.AppendTwoDigits: out of range")
	}
	pair := twoDigits[v]
	b.b = append(b.b, pair[0], pair[1])
}

// AppendQuotedString appends s surrounded by double quotes. It is the
// minimal fast path: for strings that need escaping, the escape package is
// used instead.
func AppendQuotedString(b *Buffer, s string) {
	b.b = append(b.b, '"')
	b.b = append(b.b, s...)
	b.b = append(b.b, '"')
}

// AppendFloatSafe is AppendFloat with NaN and +/-Inf collapsed to null
// literal tokens so the result is always valid JSON.
func AppendFloatSafe(b *Buffer, f float64) {
	switch {
	case math.IsNaN(f):
		b.AppendString("null")
	case math.IsInf(f, 1):
		b.AppendString("null")
	case math.IsInf(f, -1):
		b.AppendString("null")
	default:
		b.b = strconv.AppendFloat(b.b, f, 'g', -1, 64)
	}
}

// AppendBool writes the literal true or false tokens.
func AppendBool(b *Buffer, v bool) {
	if v {
		b.AppendString("true")
	} else {
		b.AppendString("false")
	}
}

// AppendHex writes v as a fixed-width lowercase hex string of width bytes.
func AppendHex(b *Buffer, v uint64, width int) {
	const digits = "0123456789abcdef"
	var tmp [16]byte
	for i := width - 1; i >= 0; i-- {
		tmp[i] = digits[v&0xf]
		v >>= 4
	}
	b.b = append(b.b, tmp[:width]...)
}

// AppendMillis writes d in milliseconds (integer).
func AppendMillis(b *Buffer, ms int64) {
	AppendInt(b, ms)
}

// AppendKeyValueSep writes the characters separating a key from a value in
// an object. Centralised here so alternative encodings (e.g. logfmt) can
// swap it out.
func AppendKeyValueSep(b *Buffer) { b.AppendByte(':') }

// AppendPairSep writes the character separating two key-value pairs.
func AppendPairSep(b *Buffer) { b.AppendByte(',') }