Some easy tools make go easier.
toolbox is a small, dependency-free collection of generic utilities for everyday Go
development — cryptography helpers, secure random code/password generation, pointer
helpers, map/slice manipulation, and a few convenience types. Everything is built on
the standard library only.
- Zero external dependencies — pure Go standard library.
- Generics-first — leverages Go 1.18+ generics for type-safe helpers.
- Crypto helpers — RSA-OAEP encrypt/decrypt and executable fingerprinting.
- Secure randomness — configurable random codes and strong passwords built on
crypto/rand. - Fast formatting —
xfmt.Sprintf, a (relatively) faster drop-in forfmt.Sprintfon the common verbs, with byte-identical output. - Well tested — comprehensive unit tests and benchmarks across packages.
- Go 1.24.0 or newer.
go get go.gh.ink/toolbox| Package | Import path | Description |
|---|---|---|
crypto/fingerprint |
go.gh.ink/toolbox/crypto/fingerprint |
Hash the running executable (MD5/SHA1/SHA256/SHA512). |
crypto/rsa |
go.gh.ink/toolbox/crypto/rsa |
RSA-OAEP encryption/decryption and PEM key loading. |
data |
go.gh.ink/toolbox/data |
Slice, map and string manipulation helpers. |
expr |
go.gh.ink/toolbox/expr |
Expression helpers such as a generic ternary. |
pointer |
go.gh.ink/toolbox/pointer |
Type-safe pointer creation, dereference and copy. |
random/code |
go.gh.ink/toolbox/random/code |
Cryptographically secure random code / password generation. |
xfmt |
go.gh.ink/toolbox/xfmt |
Faster drop-in Sprintf for the common format verbs, falling back to fmt. |
xtype |
go.gh.ink/toolbox/xtype |
Convenience type aliases. |
RSA-OAEP encryption and decryption. Public keys must be PKIX-encoded, private keys
PKCS#8-encoded. ReadKey reads and decodes a PEM file into the raw DER block.
import (
"crypto/sha256"
rsacrypt "go.gh.ink/toolbox/crypto/rsa"
)
pub, _ := rsacrypt.ReadKey("public.pem")
priv, _ := rsacrypt.ReadKey("private.pem")
// Encrypt to base64
cipher, err := rsacrypt.EncryptOAEPBase64([]byte("hello"), pub, sha256.New(), nil)
if err != nil {
// handle error
}
// Decrypt from base64
plain, err := rsacrypt.DecryptOAEPBase64(cipher, priv, sha256.New(), nil)API:
EncryptOAEP(plainText, block []byte, hash hash.Hash, label []byte) ([]byte, error)EncryptOAEPBase64(plainText, block []byte, hash hash.Hash, label []byte) (string, error)DecryptOAEP(cipherText, block []byte, hash hash.Hash, label []byte) ([]byte, error)DecryptOAEPBase64(cipherText string, block []byte, hash hash.Hash, label []byte) ([]byte, error)ReadKey(path string) ([]byte, error)
Compute a hash of the currently running executable, useful for integrity checks.
import "go.gh.ink/toolbox/crypto/fingerprint"
sum, err := fingerprint.GetExecutableSHA256()API:
GetExecutableMD5() (string, error)GetExecutableSHA1() (string, error)GetExecutableSHA256() (string, error)GetExecutableSHA512() (string, error)
Cryptographically secure random code and password generation, backed by crypto/rand.
import "go.gh.ink/toolbox/random/code"
// 6-digit numeric code
otp := code.Number(6)
// 16-char strong password (all categories, ambiguous chars removed)
pw := code.StrongPassword()
// Fully configurable
s, err := code.Code(code.Options{
Digit: 12,
UseNumbers: true,
UseLowercase: true,
UseUppercase: true,
UseSymbols: true,
ExcludeChars: "0OIl1", // avoid ambiguous characters
RequireEach: true, // guarantee at least one of each enabled category
})Options:
| Field | Type | Description |
|---|---|---|
Digit |
int |
Length of the generated code (defaults to 6 if <= 0). |
UseNumbers |
bool |
Include digits 0-9. |
UseLowercase |
bool |
Include lowercase a-z. |
UseUppercase |
bool |
Include uppercase A-Z. |
UseSymbols |
bool |
Include symbols (DefaultSymbols = !@#$%^&*?). |
CustomSymbols |
string |
If set, replaces the default symbol set entirely. |
ExcludeChars |
string |
Characters to exclude (e.g. "0OIl1"). |
RequireEach |
bool |
Guarantee at least one character from every enabled category. |
API:
Code(config Options) (string, error)MustCode(config Options) string— panics on error.Number(digit int) stringAlpha(digit int, useLower, useUpper bool) stringMixed(digit int, useNumbers, useLower, useUpper, useSymbols bool) stringStrongPassword() string
Type-safe helpers for working with pointers and generics.
import "go.gh.ink/toolbox/pointer"
p := pointer.Ref(42) // *int
v := pointer.SafeDeref(p) // 42; returns zero value when p is nil
cp := pointer.Copy(p) // new *int pointing to a copy
ptrs := pointer.SliceRef([]int{1, 2, 3}) // []*intAPI:
Ref[T any](v T) *TSliceRef[T any](v []T) []*T— pointers share the slice's backing array.SliceCopyRef[T any](v []T) []*T— each element is copied first.SafeDeref[T any](ptr *T) T— returns the zero value ifptris nil.Copy[T any](p *T) *T
Generic slice, map and string utilities.
import "go.gh.ink/toolbox/data"
n, _ := data.Atoi("123")
s := data.MakeSliceNotNil[int](nil) // []int{} instead of nil
m := map[string]int{"a": 1, "b": 2}
keys := data.MapKeys(m)
vals := data.MapValues(m)
merged := data.MergeMapsInt(
map[string]int{"a": 1},
map[string]int{"a": 2, "b": 3},
) // {"a": 3, "b": 3}API:
- String:
Atoi(s string) (int, error),Atoi32(s string) (int32, error),Atoi64(s string) (int64, error),StrConcat(strs ...string) string - Slice:
MakeSliceNotNil[T any, S ~[]T](slice S) S - Map:
MapKeys,MapValues,MapKeysValues - Merge:
MergeMapsString(concatenates),MergeMapsStringDropMismatch(keeps shared keys only),MergeMapsInt(adds) - Trim:
TrimPrefixMapsString,TrimSuffixMapsString
import "go.gh.ink/toolbox/expr"
max := expr.Ternary(a > b, a, b)API:
Ternary[T any](condition bool, trueVal, falseVal T) T
A faster, drop-in Sprintf. It uses type assertions instead of reflection for
the most common verbs — %s %d %x %X %U %f %p %% %o %b — and defers everything
else (decorated formats such as %.2f or %-10d, and any other verb) to the
standard library on a per-directive basis. Output is byte-for-byte identical to
fmt.Sprintf.
import "go.gh.ink/toolbox/xfmt"
s := xfmt.Sprintf("user=%s id=%d hex=%x", "bob", 7, 255)
// user=bob id=7 hex=ff
// When every argument shares one concrete type, SprintfGeneric receives them as
// ...T (not ...any), so the compiler catches type mismatches at the call site.
s = xfmt.SprintfGeneric[int]("%d-%d-%d", 1, 22, 333)
// Heterogeneous args work too — use SprintfGeneric[any].
s = xfmt.SprintfGeneric[any]("%s id=%d", "k", 7)API:
Sprintf(format string, a ...any) stringSprintfGeneric[T any](format string, a ...T) string
How it works:
- Fast path — the plain form of
%s %d %x %X %U %f %o %b(and%%) is formatted directly with type assertions andstrconv, into a pooled, size-hinted buffer. - Per-directive fallback — any directive carrying a flag, width, precision or
*(e.g.%.2f,%-10d,%08x), and any unhandled verb (%v,%q,%p, …), is handed tofmtfor just that one directive. - Whole fallback — explicit argument indices (
%[1]d) and argument-count mismatches defer tofmt.Sprintfentirely, preserving its exact diagnostics (%!d(MISSING),%!(EXTRA ...)).
Benchmarked against fmt.Sprintf on identical format + arguments, Go 1.24,
go test -bench=Compare -benchmem. Speedup = fmt ÷ implementation (>1 means
faster).
| Scenario | Example | fmt |
Sprintf |
Speedup | SprintfGeneric |
Speedup | Allocs fmt/Sprintf/Generic |
|---|---|---|---|---|---|---|---|
| int | %d |
49.2 | 38.0 | 1.30× | 37.2 | 1.32× | 1/1/1 |
| ints ×4 | %d-%d-%d-%d |
106.6 | 86.4 | 1.23× | 90.5 | 1.18× | 1/1/1 |
| uint hex | %x |
46.7 | 34.2 | 1.37× | 35.4 | 1.32× | 1/1/1 |
| string | %s |
35.9 | 29.4 | 1.22× | 30.7 | 1.17× | 1/1/1 |
| strings ×3 | %s/%s/%s |
68.0 | 56.3 | 1.21× | 53.8 | 1.26× | 1/1/1 |
| float | %f |
69.8 | 59.1 | 1.18× | 59.9 | 1.17× | 1/1/1 |
| bytes hex | %x ([]byte) |
81.2 | 63.8 | 1.27× | 49.1 | 1.65× | 3/3/2 |
| mixed (hot verbs) | %s id=%d hex=%x f=%f |
130.8 | 108.8 | 1.20× | — | — | 1/1/— |
| log line | [%s] svc=%s code=%d |
82.1 | 67.3 | 1.22× | — | — | 1/1/— |
| with fallback | %s took %.2fms |
94.1 | 106.5 | 0.88× | — | — | 1/1/— |
| all fallback | %v %q %t |
209.1 | 271.7 | 0.77× | — | — | 5/5/— |
| Scenario | Example | fmt |
Sprintf |
Speedup | SprintfGeneric |
Speedup | Allocs fmt/Sprintf/Generic |
|---|---|---|---|---|---|---|---|
| int | %d |
40.2 | 36.1 | 1.11× | 38.6 | 1.04× | 1/1/1 |
| ints ×4 | %d-%d-%d-%d |
99.2 | 76.2 | 1.30× | 75.6 | 1.31× | 1/1/1 |
| uint hex | %x |
40.1 | 37.3 | 1.08× | 34.3 | 1.17× | 1/1/1 |
| string | %s |
46.8 | 43.1 | 1.08× | 47.3 | 0.99× | 1/1/1 |
| strings ×3 | %s/%s/%s |
68.1 | 47.5 | 1.43× | 56.5 | 1.21× | 1/1/1 |
| float | %f |
55.4 | 43.8 | 1.26× | 43.7 | 1.27× | 1/1/1 |
| bytes hex | %x ([]byte) |
108.8 | 113.9 | 0.96× | 63.2 | 1.72× | 3/3/2 |
| mixed (hot verbs) | %s id=%d hex=%x f=%f |
135.1 | 121.7 | 1.11× | — | — | 1/1/— |
| log line | [%s] svc=%s code=%d |
88.1 | 70.8 | 1.24× | — | — | 1/1/— |
| with fallback | %s took %.2fms |
103.8 | 112.2 | 0.92× | — | — | 1/1/— |
| all fallback | %v %q %t |
247.1 | 280.9 | 0.88× | — | — | 5/5/— |
Single‑value conversion benchmarks comparing xfmt against a raw strconv call
(the theoretical floor) and fmt.Sprintf. The "tax" is xfmt/fmt minus raw.
go test ./xfmt/ -bench=Overhead -benchmem
| Scenario | raw |
fmt |
xfmt |
fmt tax |
xfmt tax |
|---|---|---|---|---|---|
int → %d |
18.7 | 49.4 | 37.9 | +30.7 | +19.2 |
uint → %x |
17.5 | 47.8 | 35.5 | +30.3 | +18.0 |
int → %o |
17.0 | 41.6 | 36.9 | +24.7 | +19.9 |
int → %b |
17.1 | 41.2 | 36.3 | +24.1 | +19.2 |
float → %f |
43.4 | 69.3 | 59.2 | +25.9 | +15.8 |
%s (no‑op on string) |
2.1 | 35.8 | 29.3 | +33.8 | +27.3 |
[]byte → %x |
19.2 | 73.8 | 64.8 | +54.6 | +45.6 |
[]byte → %x (Generic) |
19.2 | 73.8 | 49.1 | +54.6 | +29.9 |
pre+int (n=%d) |
40.9 | 53.2 | 44.7 | +12.3 | +3.8 |
| Scenario | raw |
fmt |
xfmt |
fmt tax |
xfmt tax |
|---|---|---|---|---|---|
int → %d |
23.4 | 43.4 | 35.5 | +20.0 | +12.1 |
uint → %x |
25.5 | 44.8 | 34.9 | +19.2 | +9.4 |
int → %o |
13.0 | 35.2 | 30.7 | +22.2 | +17.7 |
int → %b |
14.0 | 35.5 | 30.3 | +21.5 | +16.3 |
float → %f |
35.3 | 55.3 | 45.4 | +20.0 | +10.1 |
%s (no‑op on string) |
1.0 | 48.7 | 41.4 | +47.7 | +40.3 |
[]byte → %x |
32.7 | 126.5 | 102.8 | +93.8 | +70.0 |
[]byte → %x (Generic) |
32.7 | 126.5 | 75.9 | +93.8 | +43.2 |
pre+int (n=%d) |
52.9 | 61.0 | 57.1 | +8.1 | +4.2 |
Reproduce locally:
# head-to-head against fmt
go test ./xfmt -bench=Compare -benchmem
# overhead breakdown vs raw strconv
go test ./xfmt -bench=Overhead -benchmem
# at-a-glance table with speedups
XFMT_REPORT=1 go test ./xfmt -run TestComparisonReport -vRaw go test -bench=Compare -benchmem output (both platforms)
Apple M1 Pro (ARM64)
BenchmarkCompare/int/fmt-8 24138008 49.22 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/int/xfmt-8 32191862 38.01 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/int/xfmt_generic-8 32659503 37.16 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/ints_x4/fmt-8 11154662 106.6 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/ints_x4/xfmt-8 13803064 86.43 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/ints_x4/xfmt_generic-8 14480263 90.48 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/uint_hex/fmt-8 25228148 46.74 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/uint_hex/xfmt-8 35498538 34.22 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/uint_hex/xfmt_generic-8 33413152 35.37 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/string/fmt-8 34052816 35.86 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/string/xfmt-8 40588132 29.39 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/string/xfmt_generic-8 40808576 30.74 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/fmt-8 17595027 68.00 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/xfmt-8 22713846 56.33 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/xfmt_generic-8 22474346 53.80 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/float/fmt-8 17246674 69.80 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/float/xfmt-8 20544720 59.10 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/float/xfmt_generic-8 20012799 59.91 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/bytes_hex/fmt-8 15965230 81.23 ns/op 48 B/op 3 allocs/op
BenchmarkCompare/bytes_hex/xfmt-8 19082476 63.83 ns/op 48 B/op 3 allocs/op
BenchmarkCompare/bytes_hex/xfmt_generic-8 24523389 49.11 ns/op 24 B/op 2 allocs/op
BenchmarkCompare/mixed_hot/fmt-8 9214952 130.8 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/mixed_hot/xfmt-8 10965279 108.8 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/log_line/fmt-8 14383666 82.10 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/log_line/xfmt-8 17820006 67.32 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/with_fallback/fmt-8 12770179 94.10 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/with_fallback/xfmt-8 11301354 106.5 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/all_fallback/fmt-8 5722921 209.1 ns/op 72 B/op 5 allocs/op
BenchmarkCompare/all_fallback/xfmt-8 4493806 271.7 ns/op 72 B/op 5 allocs/op
Intel i7-13700K (x86‑64)
BenchmarkCompare/int/fmt-24 25824943 40.17 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/int/xfmt-24 34964701 36.12 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/int/xfmt_generic-24 32031006 38.64 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/ints_x4/fmt-24 13032422 99.23 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/ints_x4/xfmt-24 15398772 76.22 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/ints_x4/xfmt_generic-24 14505160 75.62 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/uint_hex/fmt-24 25859976 40.11 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/uint_hex/xfmt-24 35570955 37.25 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/uint_hex/xfmt_generic-24 35969205 34.25 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/string/fmt-24 38183049 46.78 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/string/xfmt-24 45570930 43.12 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/string/xfmt_generic-24 35418469 47.25 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/fmt-24 18044306 68.11 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/xfmt-24 24852638 47.46 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/strings_x3/xfmt_generic-24 28393248 56.48 ns/op 16 B/op 1 allocs/op
BenchmarkCompare/float/fmt-24 21492949 55.42 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/float/xfmt-24 27909249 43.82 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/float/xfmt_generic-24 27788601 43.66 ns/op 8 B/op 1 allocs/op
BenchmarkCompare/bytes_hex/fmt-24 10662582 108.8 ns/op 48 B/op 3 allocs/op
BenchmarkCompare/bytes_hex/xfmt-24 10773590 113.9 ns/op 48 B/op 3 allocs/op
BenchmarkCompare/bytes_hex/xfmt_generic-24 19580682 63.19 ns/op 24 B/op 2 allocs/op
BenchmarkCompare/mixed_hot/fmt-24 9462279 135.1 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/mixed_hot/xfmt-24 9786537 121.7 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/log_line/fmt-24 13548580 88.11 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/log_line/xfmt-24 17041275 70.81 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/with_fallback/fmt-24 12643534 103.8 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/with_fallback/xfmt-24 11922300 112.2 ns/op 24 B/op 1 allocs/op
BenchmarkCompare/all_fallback/fmt-24 4864030 247.1 ns/op 72 B/op 5 allocs/op
BenchmarkCompare/all_fallback/xfmt-24 3856126 280.9 ns/op 72 B/op 5 allocs/op
Convenience type aliases for common generic maps.
import "go.gh.ink/toolbox/xtype"
var payload xtype.H // map[string]any
var counts xtype.MS[int] // map[string]intAPI:
type H = map[string]anytype MS[V any] = map[string]V
go test ./...Benchmarks (e.g. in random/code) can be run with:
go test ./random/code -bench .Licensed under the Apache License 2.0.