base32

package
v3.0.0-next.12 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 3 Imported by: 0

README

Base32 Package

Go Reference

Crockford Base32 encoding and CRC-10 checksums for human-readable, error-correcting identifiers.

Features

  • Crockford Base32 Encoding

    • Human-readable alphabet (excludes ambiguous characters: I, L, O, U)
    • Case-insensitive decoding
    • Automatic error correction (I→1, L→1, O→0)
    • Fixed-length and compact encoding modes
    • URL-safe output
  • CRC-10 Checksums

    • 99.9%+ error detection rate
    • Detects single character errors (100%)
    • Detects transpositions (99.9%+)
    • Detects double errors (99.9%+)
    • Only 2 characters overhead

Installation

go get github.com/jasoet/pkg/v3

Quick Start

package main

import (
    "fmt"
    "github.com/jasoet/pkg/v3/base32"
)

func main() {
    // Encode a number
    id, err := base32.EncodeBase32(12345, 8)  // "00000C1S", nil
    if err != nil {
        panic(err)
    }

    // Add checksum for error detection
    idWithChecksum, err := base32.AppendChecksum(id)
    if err != nil {
        panic(err)
    }

    // Validate checksum
    if base32.ValidateChecksum(idWithChecksum) {
        fmt.Println("Valid ID!")
    }

    // Decode back
    value, _ := base32.DecodeBase32(id)
    fmt.Println(value)  // 12345
}

Use Cases

1. URL Shorteners
// Database ID to short code
databaseID := uint64(123456789)
shortCode := base32.EncodeBase32Compact(databaseID)
// https://short.url/3NQK8N

// Decode back
decoded, _ := base32.DecodeBase32(shortCode)  // 123456789
2. Order/Transaction IDs
// Generate order ID with timestamp and sequence
timestamp := uint64(time.Now().Unix())
sequence := uint64(12345)

timeCode, _ := base32.EncodeBase32(timestamp, 8)
seqCode, _ := base32.EncodeBase32(sequence, 4)  // "0C1S"

// AppendChecksum normalizes its input first: dashes are removed and
// common lookalikes are corrected (note: "ORD" contains O, which → 0)
orderID, _ := base32.AppendChecksum("ORD-" + timeCode + "-" + seqCode)
// "0RD" + timeCode + seqCode + 2-char checksum, e.g. "0RD01N62VHA0C1S27"
3. License Keys
productID := uint64(42)
customerID := uint64(789)

product, _ := base32.EncodeBase32(productID, 2)    // "1A"
customer, _ := base32.EncodeBase32(customerID, 4)  // "00RN"

licenseKey, _ := base32.AppendChecksum(product + customer)
// "1A00RN7D"
4. Voucher/Coupon Codes
voucherID := uint64(9999)
code, _ := base32.EncodeBase32(voucherID, 4)           // "09RF"
codeWithChecksum, _ := base32.AppendChecksum(code)     // "09RFZB"
// Easy to type, error-correcting
5. IoT Device IDs
deviceSerial := uint64(123456)
deviceID := base32.EncodeBase32Compact(deviceSerial)
// Compact, human-readable device identifier

API Reference

Base32 Encoding
EncodeBase32(value uint64, length int) (string, error)

Encodes an unsigned integer to a fixed-length Base32 string.

encoded, err := base32.EncodeBase32(42, 4)     // "001A", nil
encoded, err := base32.EncodeBase32(12345, 6)  // "000C1S", nil
EncodeBase32Compact(value uint64) string

Encodes to the minimum number of characters needed (no error return).

encoded := base32.EncodeBase32Compact(0)      // "0"
encoded := base32.EncodeBase32Compact(12345)  // "C1S"
DecodeBase32(encoded string) (uint64, error)

Decodes a Base32 string to an unsigned integer.

value, err := base32.DecodeBase32("C1S")  // 12345, nil
value, err := base32.DecodeBase32("c1s")  // 12345, nil (case-insensitive)
value, err := base32.DecodeBase32("I0")   // 32, nil (I→1 correction)
NormalizeBase32(input string) string

Normalizes Base32 input by:

  • Converting to uppercase
  • Removing dashes and spaces
  • Correcting common mistakes (I→1, L→1, O→0)
base32.NormalizeBase32("abc-def")  // "ABCDEF"
base32.NormalizeBase32("1O 2I")    // "1021"
IsValidBase32Char(c rune) bool

Checks if a character is valid in Base32 encoding.

base32.IsValidBase32Char('A')  // true
base32.IsValidBase32Char('O')  // true (auto-corrected)
base32.IsValidBase32Char('U')  // false
Checksums
CalculateChecksum(data string) (string, error)

Computes a 2-character CRC-10 checksum.

checksum, err := base32.CalculateChecksum("ABC123")  // "TF", nil
AppendChecksum(data string) (string, error)

Adds checksum to the end of data. The input is normalized via NormalizeBase32 first (uppercased, dashes/spaces removed, I→1 / L→1 / O→0), so dashed or lowercase identifiers work; clean input is unaffected.

withChecksum, err := base32.AppendChecksum("ABC123")     // "ABC123TF", nil
withChecksum, err := base32.AppendChecksum("0000-c1p9")  // "0000C1P9Q0", nil
ValidateChecksum(input string) bool

Verifies checksum validity. The input is normalized via NormalizeBase32 first, so dashed or lowercase checksummed strings validate; clean input is unaffected.

valid := base32.ValidateChecksum("ABC123TF")    // true
valid := base32.ValidateChecksum("abc-123-tf")  // true (normalized)
valid := base32.ValidateChecksum("ABC123ZZ")    // false
StripChecksum(input string) string

Removes the last 2 characters (checksum).

data := base32.StripChecksum("ABC123TF")  // "ABC123"
ExtractChecksum(input string) string

Extracts the last 2 characters (checksum).

checksum := base32.ExtractChecksum("ABC123TF")  // "TF"

Error Detection

The CRC-10 checksum provides excellent error detection:

Error Type Detection Rate
Single character error 100%
Transposition (AB→BA) 99.9%+
Double errors 99.9%+
Insertion/deletion High
Example
// Valid ID
validID, _ := base32.AppendChecksum("ABC123")

// Corrupted ID (A → X)
corrupted := "XBC123" + base32.ExtractChecksum(validID)
base32.ValidateChecksum(corrupted)  // false - detected!

// Transposition (AB → BA)
chars := []rune(validID)
chars[0], chars[1] = chars[1], chars[0]
transposed := string(chars)
base32.ValidateChecksum(transposed)  // false - detected!

Examples

Run the comprehensive walkthrough (no build tag needed):

go run ./examples/base32

Or the compact demo in this directory (requires the example build tag):

go run -tags=example ./base32/examples

See examples/main.go and ../examples/base32/example.go for detailed usage patterns.

Performance

Benchmarks on modern hardware:

BenchmarkEncodeBase32-8          50000000    25.3 ns/op
BenchmarkDecodeBase32-8          30000000    45.2 ns/op
BenchmarkCalculateChecksum-8     10000000   125.0 ns/op
BenchmarkValidateChecksum-8       8000000   160.0 ns/op

Alphabet Reference

Crockford Base32 Alphabet
0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R S T V W X Y Z

Excluded characters:

  • I - Looks like 1 (auto-corrected to 1)
  • L - Looks like 1 (auto-corrected to 1)
  • O - Looks like 0 (auto-corrected to 0)
  • U - Could be confused with V

This design minimizes human transcription errors.

Best Practices

  1. Always use checksums for user-facing IDs

    // ✓ Good
    id, err := base32.AppendChecksum(data)
    
    // ✗ Avoid
    id := data  // No error detection
    
  2. Normalize user input

    userInput := "ab-cd-ef"
    normalized := base32.NormalizeBase32(userInput)
    
  3. Use fixed-length encoding for databases

    // Consistent length for indexing
    id := base32.EncodeBase32(value, 10)
    
  4. Use compact encoding for URLs

    // Shorter URLs
    shortCode := base32.EncodeBase32Compact(value)
    

Migration from tix-core

If migrating from github.com/jasoet/tix-core/encoding:

Before:

import "github.com/jasoet/tix-core/encoding"

After:

import "github.com/jasoet/pkg/v3/base32"

API is 100% compatible - only the import path and package name change.

Contributing

See the main pkg/v3 repository for contribution guidelines.

License

MIT License - see LICENSE for details.


Part of github.com/jasoet/pkg/v3 - Production-ready Go utility packages.

Documentation

Overview

Package base32 provides Crockford Base32 encoding and CRC-10 checksums for human-readable, error-correcting identifiers.

The Base32 implementation uses Crockford's alphabet (excludes I, L, O, U) and provides case-insensitive decoding with automatic error correction.

The CRC-10 checksum provides 99.9%+ error detection for single character errors, transpositions, and double errors.

Example:

// Encode a value
id, err := base32.EncodeBase32(12345, 8)  // "00000C1S", nil

// Add checksum
idWithChecksum, err := base32.AppendChecksum(id)

// Validate
if base32.ValidateChecksum(idWithChecksum) {
    // Valid ID
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendChecksum

func AppendChecksum(data string) (string, error)

AppendChecksum adds a 2-character checksum to the end of the data.

This is the recommended way to create checksummed strings.

The input is normalized via NormalizeBase32 before the checksum is computed, so lowercase, dashed, or spaced input (e.g. "0000-c1p9") works; the returned string is always the normalized data plus its checksum. Clean input is unaffected by normalization.

Returns an error if the normalized input is empty or contains invalid Base32 characters.

Example:

id, _ := base32.EncodeBase32(12345, 6)             // "000C1S"
idWithChecksum, _ := base32.AppendChecksum(id)     // "000C1S69"
withDashes, _ := base32.AppendChecksum("0000-c1p9") // "0000C1P9Q0" (normalized)

Parameters:

  • data: The Base32 string to checksum (normalized before checksumming)

Returns:

  • The normalized input string with a 2-character checksum appended
  • An error if the normalized input contains invalid characters
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	withChecksum, _ := base32.AppendChecksum("ABC123")
	fmt.Println(withChecksum)
}
Output:
ABC123TF
Example (Normalized)

AppendChecksum normalizes its input, so dashed/lowercase identifiers work.

package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	withChecksum, _ := base32.AppendChecksum("0000-c1p9")
	fmt.Println(withChecksum)
}
Output:
0000C1P9Q0

func CalculateChecksum

func CalculateChecksum(data string) (string, error)

CalculateChecksum computes a 2-character Base32 checksum using CRC-10.

The checksum provides 99.9%+ error detection for:

  • Single character errors
  • Character transpositions
  • Double errors
  • Most insertion/deletion errors

The CRC-10 algorithm processes each Base32 character (5 bits) and produces a 10-bit checksum, which is then encoded as 2 Base32 characters.

Returns an error if the input contains invalid Base32 characters.

Example:

checksum, err := base32.CalculateChecksum("ABC123")  // "TF", nil

Parameters:

  • data: The Base32 string to checksum (must contain only valid Base32 characters)

Returns:

  • A 2-character Base32 checksum
  • An error if the input contains invalid characters
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	checksum, _ := base32.CalculateChecksum("ABC123")
	fmt.Println(checksum)
}
Output:
TF

func DecodeBase32

func DecodeBase32(encoded string) (uint64, error)

DecodeBase32 decodes a Base32 string to an unsigned integer.

Returns an error if the string contains invalid characters. Supports case-insensitive input and common error corrections (I→1, L→1, O→0).

Example:

val, err := base32.DecodeBase32("C1S")  // 12345, nil
val, err := base32.DecodeBase32("c1s")  // 12345, nil (case-insensitive)
val, err := base32.DecodeBase32("I0")   // 32, nil (I→1 correction)

Parameters:

  • encoded: The Base32-encoded string to decode

Returns:

  • The decoded unsigned integer value
  • An error if the input contains invalid characters
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	value, _ := base32.DecodeBase32("C1S")
	fmt.Println(value)
}
Output:
12345

func EncodeBase32

func EncodeBase32(value uint64, length int) (string, error)

EncodeBase32 encodes an unsigned integer to a Base32 string of specified length.

The encoded value is left-padded with '0's to reach the specified length. Uses Crockford's Base32 alphabet (0-9, A-Z excluding I, L, O, U).

Returns an error if the value is too large to fit in the specified length.

Example:

base32.EncodeBase32(42, 4)     // "001A", nil
base32.EncodeBase32(999, 3)    // "0Z7", nil
base32.EncodeBase32(32, 2)     // "10", nil
base32.EncodeBase32(1024, 2)   // "", error (overflow)

Parameters:

  • value: The unsigned integer to encode
  • length: The desired length of the output string

Returns:

  • A Base32-encoded string of exactly 'length' characters, or "" on error
  • An error if length <= 0 or the value overflows the specified length
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	id, _ := base32.EncodeBase32(12345, 8)
	fmt.Println(id)
}
Output:
00000C1S

func EncodeBase32Compact

func EncodeBase32Compact(value uint64) string

EncodeBase32Compact encodes a value to the minimum number of Base32 characters needed.

Unlike EncodeBase32, this function does not pad the output to a fixed length.

Example:

base32.EncodeBase32Compact(0)      // "0"
base32.EncodeBase32Compact(31)     // "Z"
base32.EncodeBase32Compact(32)     // "10"
base32.EncodeBase32Compact(12345)  // "C1S"
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	fmt.Println(base32.EncodeBase32Compact(12345))
	fmt.Println(base32.EncodeBase32Compact(123456789))
}
Output:
C1S
3NQK8N

func ExtractChecksum

func ExtractChecksum(input string) string

ExtractChecksum extracts the last 2 characters (checksum) from a string.

Returns an empty string if the input has fewer than 2 characters.

Example:

checksum := base32.ExtractChecksum("ABC123TF")  // "TF"
checksum := base32.ExtractChecksum("A")         // ""

Parameters:

  • input: The string with checksum appended

Returns:

  • The last 2 characters of the input

func IsValidBase32Char

func IsValidBase32Char(c rune) bool

IsValidBase32Char returns true if the character is valid in Base32 encoding.

Valid characters include: 0-9, A-Z (excluding I, L, O, U), and their lowercase equivalents. Also accepts I, L, O as they are auto-corrected to 1, 1, 0.

Example:

base32.IsValidBase32Char('A')  // true
base32.IsValidBase32Char('a')  // true
base32.IsValidBase32Char('O')  // true (auto-corrected to 0)
base32.IsValidBase32Char('U')  // false

func NormalizeBase32

func NormalizeBase32(input string) string

NormalizeBase32 normalizes a Base32 string by:

  • Converting to uppercase
  • Removing dashes, spaces, tabs, and newlines
  • Correcting common mistakes (I→1, L→1, O→0)

This function is useful for processing user input to ensure consistency.

Example:

base32.NormalizeBase32("abc-def")   // "ABCDEF"
base32.NormalizeBase32("1O 2I")     // "1021"
base32.NormalizeBase32("hell0")     // "HE110" (L→1 correction)
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	fmt.Println(base32.NormalizeBase32("abc-def"))
	fmt.Println(base32.NormalizeBase32("1O 2I"))
}
Output:
ABCDEF
1021

func StripChecksum

func StripChecksum(input string) string

StripChecksum removes the last 2 characters (checksum) from a string.

Returns an empty string if the input has 2 or fewer characters.

Example:

data := base32.StripChecksum("ABC123TF")  // "ABC123"
data := base32.StripChecksum("AB")        // ""

Parameters:

  • input: The string with checksum appended

Returns:

  • The input string without the last 2 characters

func ValidateChecksum

func ValidateChecksum(input string) bool

ValidateChecksum verifies that the checksum in a string is correct.

Expected format: [data][2 chars checksum]

The input is normalized via NormalizeBase32 before validation, so lowercase, dashed, or spaced input (e.g. "0000-c1p9-q0") validates against its normalized form. Clean input is unaffected by normalization.

This function is useful for validating user input or detecting data corruption. Returns false if the input is too short or contains invalid Base32 characters.

Example:

valid := base32.ValidateChecksum("ABC123TF")    // true (TF is the checksum of "ABC123")
valid := base32.ValidateChecksum("abc-123-tf")  // true (normalized before validation)
valid := base32.ValidateChecksum("ABC123ZZ")    // false (ZZ is the wrong checksum)

Parameters:

  • input: The string with checksum appended (minimum 3 characters after normalization)

Returns:

  • true if the checksum is valid, false otherwise
Example
package main

import (
	"fmt"

	"github.com/jasoet/pkg/v3/base32"
)

func main() {
	fmt.Println(base32.ValidateChecksum("ABC123TF"))
	fmt.Println(base32.ValidateChecksum("0000-c1p9-q0")) // normalized before validation
	fmt.Println(base32.ValidateChecksum("ABC123ZZ"))
}
Output:
true
true
false

Types

This section is empty.

Jump to

Keyboard shortcuts

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