Documentation
¶
Overview ¶
Package stringkey solves the practical problem of deriving a stable, compact, non-cryptographic key from multiple text fields for fast lookup, deduplication, and idempotency-style identifiers.
Problem ¶
Applications frequently need a deterministic key built from several strings (for example: first name + last name + country, or provider + external ID + scope). Building these keys manually is error-prone because equivalent inputs may differ only by case, whitespace, or Unicode composition. stringkey applies a canonical normalization pipeline before hashing so semantically equivalent inputs map to the same key more often.
How It Works ¶
New builds a StringKey from one or more input fields using this pipeline:
- Trim leading and trailing Unicode whitespace for each field.
- Collapse repeated Unicode whitespace to a single space.
- Convert text to lowercase.
- Concatenate fields using a tab separator (`\t`) to preserve boundaries.
- Apply Unicode NFC normalization (equivalent to NFD then NFC).
- Hash the resulting byte sequence with FarmHash64.
The resulting key is stored as an internal uint64 and can be retrieved in multiple representations:
- StringKey.Key: raw uint64 value.
- StringKey.String: base-36 string (short, variable length).
- StringKey.Hex: fixed 16-character lowercase hexadecimal string.
Two normalization details are worth calling out:
- Normalization is NFC, not NFKC. Canonically equivalent forms (such as precomposed vs decomposed accents) collapse together, but compatibility variants do not: ligatures (for example "ff"), full-width vs half-width forms, and super/subscripts are left distinct.
- Lowercasing uses the locale-independent unicode.ToLower simple mapping, not language-tailored case folding. For example, the Turkish dotless "ı" and dotted "İ" are not special-cased.
Key Features ¶
- Deterministic canonicalization across case/spacing/Unicode variants.
- Compact 64-bit key suitable for indexing and caching.
- Multiple output formats for storage, logs, and URLs.
- Fast, allocation-light hashing path for small field sets.
Stability ¶
For a fixed input, the key is fully deterministic and does not change across runs, architectures, or Go versions: FarmHash64 is a fixed algorithm and the representations are plain integer encodings.
The one dependency to be aware of is the Unicode normalization step, which uses the Unicode data tables shipped by golang.org/x/text. Keys for pure-ASCII input and for long-assigned characters are effectively permanent. Only rare or newly-assigned code points can normalize differently if the golang.org/x/text Unicode version advances, which would change their keys across such an upgrade. If you persist keys and must survive Unicode-table upgrades unchanged, pin the golang.org/x/text version alongside the stored keys.
Important Limits ¶
This package is not cryptographically secure and must not be used for security tokens, signatures, or untrusted collision-resistant identifiers.
It is designed for reasonably small input sizes and a moderate number of keys. According to the birthday bound for a 64-bit hash space (~1.8x10^19):
- collision probability is about 1% around 6.1x10^8 generated keys;
- collision probability is about 50% around 5.1x10^9 generated keys.
Choose a larger hash or cryptographic construction when your scale or threat model requires stronger collision guarantees.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type StringKey ¶
type StringKey struct {
// contains filtered or unexported fields
}
StringKey stores the encoded key.
func New ¶
New constructs deterministic FarmHash64 key from normalized input strings (whitespace/case/Unicode normalized).
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/stringkey"
)
func main() {
// input strings
args := []string{
"0123456789",
"abcdefghijklmnopqrstuvwxyz",
"Lorem ipsum dolor sit amet",
}
// generate a new key
sk := stringkey.New(args...)
fmt.Println(sk)
}
Output: 2p8dmari397l8
func (*StringKey) Hex ¶
Hex returns fixed 16-character lowercase hexadecimal representation of key, zero-padded.
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/stringkey"
)
func main() {
// generate a new key as fixed-length 16 digits hexadecimal string key.
k := stringkey.New(
"0123456789",
"abcdefghijklmnopqrstuvwxyz",
"Lorem ipsum dolor sit amet",
).Hex()
fmt.Println(k)
}
Output: b19b688e8e3229ac
func (*StringKey) Key ¶
Key returns raw uint64 key value.
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/stringkey"
)
func main() {
// generate a new key as uint64
k := stringkey.New(
"0123456789",
"abcdefghijklmnopqrstuvwxyz",
"Lorem ipsum dolor sit amet",
).Key()
fmt.Println(k)
}
Output: 12797937727583693228
func (*StringKey) String ¶
String returns variable-length base-36 string representation of key.
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/stringkey"
)
func main() {
// generate a new key as 36-char encoded string
k := stringkey.New(
"0123456789",
"abcdefghijklmnopqrstuvwxyz",
"Lorem ipsum dolor sit amet",
).String()
fmt.Println(k)
}
Output: 2p8dmari397l8