Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/uhex"
Package uhex provides fixed-width, lowercase hexadecimal encoders for unsigned integers and fixed-size byte arrays.
Each input byte is mapped through a 256-entry lookup table to its two lowercase hexadecimal characters, which are written with a single 16-bit store.
Compared with generic formatting such as [fmt.Sprintf]("%x", v), uhex avoids
reflection and width handling. Compared with encoding/hex, it focuses on
fixed-width values and exposes helpers that write directly into caller-owned
arrays.
Output
All encoders produce lowercase hexadecimal.
Integer-based helpers always zero-pad to the full width of the input type:
Hex64andHex64UBproduce 16 bytes.Hex32andHex32UBproduce 8 bytes.Hex16andHex16UBproduce 4 bytes.Hex8andHex8UBproduce 2 bytes.
Byte-array helpers encode each input byte in order, two hex characters per byte:
Hex64BandHex64BBencode8byte values.Hex32BandHex32BBencode4byte values.Hex16BandHex16BBencode2byte values.Hex8BandHex8BBencode1byte values.
Allocation Behavior
The slice-returning helpers (Hex64, Hex32, Hex16, Hex8, Hex64B,
Hex32B, Hex16B, and Hex8B) each fill a local array (16, 8, 4, or 2 bytes)
and return a slice over it. Whether that array is allocated on the heap depends
on escape analysis at the call site:
- If the returned slice does not escape the caller (for example, it is read and discarded, or only its bytes are consumed), the array stays on the stack and no allocation occurs.
- If the returned slice escapes (it is stored in a longer-lived structure, returned, sent on a channel, or passed to a function the compiler cannot inline), the backing array is heap-allocated, costing one allocation of the array’s width.
For code that must never allocate regardless of escape analysis, prefer the
buffer-writing helpers that accept a destination array pointer. Hex64UB,
Hex32UB, Hex16UB, and Hex8UB write integer encodings into caller-owned
buffers. Hex64BB, Hex32BB, Hex16BB, and Hex8BB do the same for
fixed-size byte arrays. These never allocate and, for the 32-bit and narrower
widths, are small enough to be inlined into the caller.
Performance
Because every width is known at compile time, the encoders are fully unrolled: there are no loops, no length checks, and no argument validation. Each input byte is translated with a single lookup into a 256-entry table and written with one 16-bit store, so the cost scales linearly with the output width and is independent of the input value (there are no data-dependent branches).
Relative to the standard library and general-purpose formatting, for the small fixed widths this package targets:
- Against
encoding/hex, the buffer-writing helpers are on the order of two to three times faster and, likeencoding/hex.Encode, allocate nothing. Part of the gain on the integer helpers is structural:encoding/hexonly encodes byte slices, so encoding an integer with it additionally requires serializing the value into a scratch buffer first, whereas the integer helpers here read the value directly. - Against [fmt.Sprintf]("%x", v), the difference is roughly an order of magnitude, and uhex avoids the allocations that reflection-based formatting incurs.
- When a []byte or string result is returned rather than written into a
caller-owned buffer, the single heap allocation for that result dominates the
total cost, so the advantage over
encoding/hexnarrows accordingly; the buffer-writing helpers avoid that allocation entirely.
These are relative characteristics, not guarantees; absolute numbers depend on
the hardware and compiler. The package ships benchmarks (run with
go test -bench=.) so the figures can be reproduced on the target platform.
This package is specialized for small, fixed-width values (up to eight bytes).
For arbitrary-length or streaming data, or for decoding, use encoding/hex,
which is optimized for those cases; uhex offers no advantage there and does not
cover them.
Naming
Function suffixes follow this pattern:
- No suffix: encode an unsigned integer and return a []byte.
- UB: encode an unsigned integer into a caller-provided buffer.
- B: encode a fixed-size byte array and return a []byte.
- BB: encode a fixed-size byte array into a caller-provided buffer.
Usage
id := uhex.Hex64(traceID)
sum := uhex.Hex32(checksum)
tag := string(uhex.Hex8(kind))
var dst `16`byte
uhex.Hex64UB(traceID, &dst)
out := dst[:]
src := `8`byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
uhex.Hex64BB(src, &dst)
When To Use
- IDs must render as a fixed number of hex digits, zero-padded.
- The encoding sits on a hot path and should not allocate.
Example
h := uhex.Hex64(uint64(0x0123456789abcdef))
fmt.Println(string(h))
// Output:
// 0123456789abcdef
Full source is in example_uhex_test.go. More runnable examples are on pkg.go.dev.
Dependencies
This package reaches no external module: it uses only the Go standard library.