hexa

package
v1.19.2 Latest Latest
Warning

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

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 5 Imported by: 0

README

Hexadecimal Encoding Package

Go Version GoDoc

Hexadecimal encoding and decoding with streaming I/O support.

AI Disclaimer (EU AI Act Article 50.4): AI assistance was used solely for testing, documentation, and bug resolution under human supervision.


Table of Contents


Overview

The hexa package provides standard hexadecimal encoding and decoding for Go applications. It implements the encoding.Coder interface for consistent hex operations across the golib ecosystem.

Design Philosophy
  • Simplicity: Standard hex encoding (0-9, a-f)
  • Efficiency: Direct byte slice operations without intermediate buffers
  • Flexibility: Both byte slice and streaming I/O operations
  • Stateless: No configuration needed, thread-safe
  • Compatibility: Case-insensitive decoding

Key Features

Feature Description
Standard Hex RFC 4648 compliant encoding
Case-Insensitive Accepts both uppercase and lowercase
Streaming Support io.Reader and io.Writer interfaces
Memory Efficient Direct operations, no buffering
Thread-Safe Stateless, safe for concurrent use
Zero Config No initialization parameters needed
Lossless Perfect round-trip encoding/decoding

Architecture

Package Structure
encoding/hexa/
├── interface.go        # Public API (New function)
└── model.go           # Core implementation (Coder interface)
Component Architecture
┌─────────────────────────────────────────────────────┐
│              Hexa Package                            │
│                                                      │
│  ┌──────────────────────────────────────────────┐  │
│  │         Coder Interface                      │  │
│  │  - Encode(bytes) → hex string               │  │
│  │  - Decode(hex) → bytes                      │  │
│  │  - EncodeReader(io.Reader) → io.Reader     │  │
│  │  - DecodeReader(io.Reader) → io.Reader     │  │
│  │  - Reset() (no-op, stateless)              │  │
│  └──────────────────────────────────────────────┘  │
│                        │                             │
│                        ▼                             │
│  ┌──────────────────────────────────────────────┐  │
│  │      Go encoding/hex Package                 │  │
│  │  - hex.Encode() / hex.EncodeToString()     │  │
│  │  - hex.Decode() / hex.DecodeString()       │  │
│  └──────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘
Data Flow
Encoding Flow:
  Binary Data → Hex Encode → "48656c6c6f" (2× size)
  [0x48, 0x65, 0x6c, 0x6c, 0x6f] → "48656c6c6f"

Decoding Flow:
  "48656c6c6f" → Hex Decode → Binary Data (½ size)
  "48656c6c6f" → [0x48, 0x65, 0x6c, 0x6c, 0x6f]

Size Relationship:
  - Encoded size = Original size × 2
  - Decoded size = Encoded size ÷ 2
  - 1 byte = 2 hex characters

Installation

go get github.com/nabbar/golib/encoding/hexa

Dependencies:

  • Go standard library (encoding/hex)
  • github.com/nabbar/golib/encoding (interface definitions)

Quick Start

Basic Encoding/Decoding
package main

import (
    "fmt"
    
    enchex "github.com/nabbar/golib/encoding/hexa"
)

func main() {
    // Create a new coder
    coder := enchex.New()
    
    // Encode data to hexadecimal
    plaintext := []byte("Hello, World!")
    encoded := coder.Encode(plaintext)
    fmt.Printf("Encoded: %s\n", encoded)
    // Output: Encoded: 48656c6c6f2c20576f726c6421
    
    // Decode hexadecimal data
    decoded, err := coder.Decode(encoded)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Decoded: %s\n", decoded)
    // Output: Decoded: Hello, World!
}

Core Concepts

Hexadecimal Encoding

Hexadecimal encoding converts each byte (8 bits) into two hexadecimal characters (0-9, a-f).

Encoding Process:

Byte:     0x48      0x65      0x6c
Binary:   01001000  01100101  01101100
Hex:      4    8    6    5    6    c
Result:   "48"      "65"      "6c"

Properties:

  • Input: Binary data (bytes)
  • Output: Hexadecimal string (2× size)
  • Format: Lowercase hex (default)
  • Reversible: Lossless encoding/decoding
  • Character Set: 0-9, a-f (16 characters)
Case Insensitivity

Decoding accepts both uppercase and lowercase:

coder := enchex.New()

// All these decode to the same result
decoded1, _ := coder.Decode([]byte("48656c6c6f"))  // lowercase
decoded2, _ := coder.Decode([]byte("48656C6C6F"))  // uppercase
decoded3, _ := coder.Decode([]byte("48656C6c6F"))  // mixed

// All equal: []byte("Hello")
Size Calculations
original := []byte("Hello")      // 5 bytes
encoded := coder.Encode(original)  // 10 bytes (5 × 2)
decoded, _ := coder.Decode(encoded)  // 5 bytes (10 ÷ 2)

Formula:

  • encoded_size = original_size × 2
  • decoded_size = encoded_size ÷ 2

API Reference

New

New() encoding.Coder

Creates a new hexadecimal coder instance.

coder := enchex.New()

Returns:

  • Stateless coder instance (thread-safe)

Notes:

  • No configuration needed
  • Multiple instances safe for concurrent use
  • Reset() is a no-op (stateless)
Encode

Encode(p []byte) []byte

Encodes binary data to hexadecimal string.

plaintext := []byte("Hello")
hex := coder.Encode(plaintext)
// hex = []byte("48656c6c6f")

Parameters:

  • p: Binary data to encode

Returns:

  • Hexadecimal representation (2× size)

Output Format:

  • Lowercase hexadecimal (0-9, a-f)
  • No delimiters or spacing
  • No prefix (no "0x")
Decode

Decode(p []byte) ([]byte, error)

Decodes hexadecimal string to binary data.

hexData := []byte("48656c6c6f")
plaintext, err := coder.Decode(hexData)
if err != nil {
    log.Fatal(err)
}
// plaintext = []byte("Hello")

Parameters:

  • p: Hexadecimal data to decode

Returns:

  • Binary data (½ size)
  • Error if invalid hex characters or odd length

Error Conditions:

  • Invalid hex characters (not 0-9, a-f, A-F)
  • Odd length hex string
Reset

Reset()

Clears internal state (no-op for stateless coder).

coder.Reset()  // Does nothing, included for interface compatibility

Notes:

  • Included for encoding.Coder interface compatibility
  • No state to clear (stateless implementation)
  • Safe to call, has no effect

Streaming Operations

Encode Stream

EncodeReader(r io.Reader) io.Reader

Creates a reader that encodes data on-the-fly.

import "os"

file, _ := os.Open("binary.dat")
defer file.Close()

// Create hex-encoded reader
hexReader := coder.EncodeReader(file)

// Write encoded data to output
output, _ := os.Create("encoded.hex")
defer output.Close()

io.Copy(output, hexReader)
Decode Stream

DecodeReader(r io.Reader) io.Reader

Creates a reader that decodes hex data on-the-fly.

file, _ := os.Open("encoded.hex")
defer file.Close()

// Create decoded reader
binaryReader := coder.DecodeReader(file)

// Read decoded data
output, _ := os.Create("decoded.dat")
defer output.Close()

io.Copy(output, binaryReader)
Example: Hex Dump File
func hexDumpFile(inputPath, outputPath string) error {
    coder := enchex.New()
    
    // Open input
    input, err := os.Open(inputPath)
    if err != nil {
        return err
    }
    defer input.Close()
    
    // Create output
    output, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer output.Close()
    
    // Encode stream
    hexReader := coder.EncodeReader(input)
    _, err = io.Copy(output, hexReader)
    return err
}

Performance

Benchmark Results
Operation Throughput Allocation Notes
Encode (1KB) ~1 GB/s 2KB Doubles size
Decode (1KB) ~800 MB/s 512B Halves size
Encode (1MB) ~1.2 GB/s 2MB Large blocks
Decode (1MB) ~900 MB/s 512KB Large blocks
Stream (4KB buffer) ~800 MB/s 8KB Buffered I/O

Benchmarks on AMD64, Go 1.21, Linux

Memory Characteristics
Operation Memory Notes
Coder Instance ~0 bytes Stateless
Encode Input × 2 Output buffer
Decode Input ÷ 2 Output buffer
Stream Buffer 4KB default Configurable
Performance Tips

1. Reuse Coder Instance:

// ✅ Good: Reuse coder (stateless anyway)
coder := enchex.New()
for _, data := range dataList {
    encoded := coder.Encode(data)
}

// Also OK: Create per operation (no overhead)
for _, data := range dataList {
    coder := enchex.New()
    encoded := coder.Encode(data)
}

2. Pre-allocate for Known Sizes:

// For encoding
inputSize := len(data)
output := make([]byte, inputSize*2)
// Use encoding/hex directly for pre-allocated buffer

3. Use Streaming for Large Files:

// ✅ Good: Stream large files
hexReader := coder.EncodeReader(fileReader)
io.Copy(output, hexReader)

// ❌ Bad: Load entire file into memory
data, _ := io.ReadAll(fileReader)
encoded := coder.Encode(data)

Use Cases

Configuration Files
// Store binary data in config files
type Config struct {
    EncryptionKey string `json:"encryption_key"`  // Hex encoded
}

func SaveConfig(cfg Config, key []byte) error {
    coder := enchex.New()
    cfg.EncryptionKey = string(coder.Encode(key))
    
    data, _ := json.Marshal(cfg)
    return os.WriteFile("config.json", data, 0600)
}

func LoadConfig() ([]byte, error) {
    data, _ := os.ReadFile("config.json")
    
    var cfg Config
    json.Unmarshal(data, &cfg)
    
    coder := enchex.New()
    return coder.Decode([]byte(cfg.EncryptionKey))
}
Checksums and Hashes
import (
    "crypto/sha256"
    enchex "github.com/nabbar/golib/encoding/hexa"
)

func ComputeChecksum(data []byte) string {
    hash := sha256.Sum256(data)
    coder := enchex.New()
    return string(coder.Encode(hash[:]))
}

func VerifyChecksum(data []byte, expected string) bool {
    actual := ComputeChecksum(data)
    return actual == expected
}
Debugging Output
func DebugPrint(label string, data []byte) {
    coder := enchex.New()
    hex := coder.Encode(data)
    
    fmt.Printf("%s: %s\n", label, hex)
    fmt.Printf("  Length: %d bytes\n", len(data))
    fmt.Printf("  Hex: %s\n", hex)
}
Wire Protocol
// Encode binary data for text-based protocols
func SendData(conn net.Conn, data []byte) error {
    coder := enchex.New()
    encoded := coder.Encode(data)
    
    // Send as text with newline
    _, err := fmt.Fprintf(conn, "%s\n", encoded)
    return err
}

func ReceiveData(conn net.Conn) ([]byte, error) {
    scanner := bufio.NewScanner(conn)
    if !scanner.Scan() {
        return nil, scanner.Err()
    }
    
    coder := enchex.New()
    return coder.Decode(scanner.Bytes())
}
Database Storage
// Store binary data as hex in VARCHAR columns
type User struct {
    ID     int
    Avatar []byte
}

func (u *User) SaveAvatar(db *sql.DB) error {
    coder := enchex.New()
    hex := coder.Encode(u.Avatar)
    
    _, err := db.Exec(
        "UPDATE users SET avatar = ? WHERE id = ?",
        string(hex), u.ID,
    )
    return err
}

func (u *User) LoadAvatar(db *sql.DB) error {
    var hex string
    err := db.QueryRow(
        "SELECT avatar FROM users WHERE id = ?", u.ID,
    ).Scan(&hex)
    
    if err != nil {
        return err
    }
    
    coder := enchex.New()
    u.Avatar, err = coder.Decode([]byte(hex))
    return err
}

Best Practices

1. Handle Decode Errors
// ✅ Good: Check decode errors
decoded, err := coder.Decode(hexData)
if err != nil {
    log.Printf("Invalid hex data: %v", err)
    return err
}

// ❌ Bad: Ignoring errors
decoded, _ := coder.Decode(hexData)
2. Validate Input Length
// ✅ Good: Validate before decode
if len(hexData)%2 != 0 {
    return fmt.Errorf("hex data must have even length")
}
decoded, err := coder.Decode(hexData)

// Info: Decode will catch this too, but explicit is better
3. Use Appropriate Encoding
// ✅ Good: Use hex for display/debugging
hex := coder.Encode(binaryData)
fmt.Printf("Data: %s\n", hex)

// ❌ Bad: Use hex for storage (base64 is more efficient)
// Hex: 2× expansion
// Base64: 1.33× expansion
4. Stream Large Files
// ✅ Good: Stream for large files
hexReader := coder.EncodeReader(fileReader)
io.Copy(output, hexReader)

// ❌ Bad: Load everything in memory
data, _ := io.ReadAll(fileReader)  // Memory intensive
encoded := coder.Encode(data)
5. Consider Case Sensitivity
// ✅ Good: Lowercase for consistency
encoded := coder.Encode(data)  // Always lowercase

// ✅ Good: Accept both for decoding
decoded1, _ := coder.Decode([]byte("48656c6c6f"))  // lowercase
decoded2, _ := coder.Decode([]byte("48656C6C6F"))  // uppercase
// Both work

Testing

Comprehensive testing documentation is available in TESTING.md.

Quick Test:

cd encoding/hexa
go test -v -cover

Test Metrics:

  • 97 test specifications
  • 89.7% code coverage
  • Ginkgo v2 + Gomega framework
  • Edge case testing (invalid input, streaming, etc.)

Contributing

Contributions are welcome! Please follow these guidelines:

Code Contributions

  • Do not use AI to generate package implementation code
  • AI may assist with tests, documentation, and bug fixing
  • All contributions must pass existing tests
  • Maintain or improve test coverage
  • Follow existing code style

Testing

  • Write tests for all new features
  • Test edge cases (empty input, invalid hex, odd length)
  • Verify round-trip encoding/decoding
  • Include benchmarks for performance-critical code

Documentation

  • Update README.md for new features
  • Add examples for common use cases
  • Document all public APIs with GoDoc
  • Keep TESTING.md synchronized

Pull Requests

  • Provide clear description of changes
  • Reference related issues
  • Include test results
  • Update documentation

See CONTRIBUTING.md for detailed guidelines.


Future Enhancements

Potential improvements for future versions:

Features

  • Formatted output options (with delimiters, spacing)
  • Uppercase encoding option
  • Prefix options (0x, \x, etc.)
  • Chunked output (groups of bytes)

Performance

  • SIMD optimizations for bulk encoding
  • Zero-copy encoding where possible
  • Parallel encoding for large data

Utilities

  • Pretty-print hex dump (with ASCII column)
  • Diff hex strings
  • Search in hex data
  • Hex editor integration helpers

Compatibility

  • Custom character sets (e.g., 0-9A-F only)
  • Alternative formats (hexdump, xxd, etc.)

Suggestions and contributions are welcome via GitHub issues.


Go Standard Library
Hex Standards

License

MIT License - See LICENSE file for details.

Copyright (c) 2023 Nicolas JUHEL


Resources


This package is part of the golib project.

Documentation

Overview

Package hexa provides hexadecimal encoding and decoding with streaming I/O support.

This package implements the encoding.Coder interface for consistent hex operations. It uses Go's standard encoding/hex package internally for RFC 4648 compliant encoding.

Features:

  • Standard hexadecimal encoding (0-9, a-f)
  • Case-insensitive decoding (accepts both uppercase and lowercase)
  • Streaming encoding/decoding via io.Reader interfaces
  • Memory efficient operations
  • Stateless and thread-safe
  • Lossless round-trip encoding/decoding

Encoding converts each byte to two hexadecimal characters:

  • Input size N bytes → Output size 2N bytes
  • Output format: lowercase hex (e.g., "48656c6c6f")
  • Character set: 0-9, a-f

Decoding converts hexadecimal strings back to binary:

  • Input size 2N bytes → Output size N bytes
  • Accepts uppercase, lowercase, or mixed case
  • Rejects invalid hex characters or odd length

Example usage:

import enchex "github.com/nabbar/golib/encoding/hexa"

// Create coder
coder := enchex.New()

// Encode binary to hex
plaintext := []byte("Hello")
hex := coder.Encode(plaintext)
fmt.Println(string(hex))  // Output: 48656c6c6f

// Decode hex to binary
decoded, err := coder.Decode(hex)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(decoded))  // Output: Hello

Common use cases:

  • Display binary data in readable format
  • Store binary data in text files/databases
  • Debug binary protocols
  • Encode checksums and hashes

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidBufferSize = errors.New("invalid buffer size")
)

Functions

func New

func New() libenc.Coder

New creates a new hexadecimal coder instance.

The returned coder implements the encoding.Coder interface and provides hexadecimal encoding and decoding functionality. The coder is stateless and safe for concurrent use.

Returns:

  • A new hexadecimal coder instance

Example:

coder := enchex.New()
hex := coder.Encode([]byte("Hello"))
fmt.Println(string(hex))  // Output: 48656c6c6f

Encoding format:

  • Each byte becomes two hex characters
  • Output is lowercase (0-9, a-f)
  • No delimiters or spacing

Decoding format:

  • Accepts uppercase, lowercase, or mixed case
  • Requires even length input
  • Rejects invalid hex characters

Types

This section is empty.

Jump to

Keyboard shortcuts

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