Initial release: Go Jdenticon library v0.1.0

- Core library with SVG and PNG generation
- CLI tool with generate and batch commands
- Cross-platform path handling for Windows compatibility
- Comprehensive test suite with integration tests
This commit is contained in:
Kevin McIntyre
2026-01-02 23:56:48 -05:00
parent f84b511895
commit d9e84812ff
292 changed files with 19725 additions and 38884 deletions

476
README.md
View File

@@ -1,105 +1,461 @@
# [Jdenticon-go](https://jdenticon.com)
# Go Jdenticon
Go library for generating highly recognizable identicons.
[![CI](https://github.com/ungluedlabs/go-jdenticon/workflows/CI/badge.svg)](https://github.com/ungluedlabs/go-jdenticon/actions/workflows/ci.yml)
[![Benchmarks](https://github.com/ungluedlabs/go-jdenticon/workflows/Benchmarks/badge.svg)](https://github.com/ungluedlabs/go-jdenticon/actions/workflows/performance-regression.yml)
[![GoDoc](https://pkg.go.dev/badge/github.com/ungluedlabs/go-jdenticon?status.svg)](https://pkg.go.dev/github.com/ungluedlabs/go-jdenticon)
[![Go Report Card](https://goreportcard.com/badge/github.com/ungluedlabs/go-jdenticon)](https://goreportcard.com/report/github.com/ungluedlabs/go-jdenticon)
[![codecov](https://codecov.io/gh/kevinmcintyre/go-jdenticon/branch/main/graph/badge.svg)](https://codecov.io/gh/kevinmcintyre/go-jdenticon)
[![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic--2.0-blue.svg)](LICENSE)
A high-performance, idiomatic Go library for generating [Jdenticon](https://jdenticon.com) identicons. This library provides a simple API for creating PNG and SVG avatars, is fully configurable, and includes a command-line tool for easy generation.
It produces identical SVG output to the original [Jdenticon JavaScript library](https://github.com/dmester/jdenticon).
![Sample identicons](https://jdenticon.com/hosted/github-samples.png)
## Features
go-jdenticon is a Go port of the JavaScript library [Jdenticon](https://github.com/dmester/jdenticon).
* **Simple API:** Generate icons with just a few lines of code
* **PNG & SVG Support:** Output to standard raster and vector formats
* **Highly Configurable:** Adjust colors, saturation, padding, and more
* **Performance Optimized:** Built-in LRU caching and efficient rendering
* **Concurrent Batch Processing:** High-performance worker pool for bulk generation
* **CLI Included:** Generate icons directly from your terminal
* **Comprehensive Error Handling:** Structured error types with actionable messages
* **Identical SVG Output:** Produces the same SVGs as the original JavaScript library
* **High-Quality PNG Output:** Visually very close PNGs (different rendering engine; differences only noticeable in side-by-side comparison)
* Renders identicons as PNG or SVG with no external dependencies
* Generates consistent, deterministic identicons from any input string
* Highly customizable color themes and styling options
* Simple, clean API for easy integration
* Command-line tool included for standalone usage
## Quick Start
## Installation
This will get you generating your first icon in under a minute.
```bash
go get github.com/kevin/go-jdenticon
### 1. Installation
**Library:**
```sh
go get -u github.com/ungluedlabs/go-jdenticon
```
## Usage
**Command-Line Tool (Optional):**
```sh
go install github.com/ungluedlabs/go-jdenticon/cmd/jdenticon@latest
```
### Basic Usage
### 2. Basic Usage (Library)
Create a file `main.go` and add the following code:
```go
package main
import (
"fmt"
"github.com/kevin/go-jdenticon/jdenticon"
"context"
"log"
"os"
"github.com/ungluedlabs/go-jdenticon/jdenticon"
)
func main() {
// Generate an identicon
icon := jdenticon.Generate("user@example.com", 200)
// Get SVG output
svg := icon.ToSVG()
fmt.Println(svg)
// Get PNG output
png := icon.ToPNG()
// Save or use PNG data...
// Generate an icon from a string value
icon, err := jdenticon.Generate(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatalf("Failed to create icon: %v", err)
}
// Save the icon as SVG
svg, err := icon.ToSVG()
if err != nil {
log.Fatalf("Failed to generate SVG: %v", err)
}
if err := os.WriteFile("my-icon.svg", []byte(svg), 0644); err != nil {
log.Fatalf("Failed to save SVG: %v", err)
}
// Save the icon as PNG
png, err := icon.ToPNG()
if err != nil {
log.Fatalf("Failed to generate PNG: %v", err)
}
if err := os.WriteFile("my-icon.png", png, 0644); err != nil {
log.Fatalf("Failed to save PNG: %v", err)
}
log.Println("Generated my-icon.svg and my-icon.png successfully!")
// Note: SVG output is identical to JavaScript library
// PNG output is visually very close but uses a different rendering engine
}
```
### Custom Configuration
Run the code:
```sh
go run main.go
```
You will find `my-icon.svg` and `my-icon.png` files in the same directory.
## Advanced Usage & Configuration
The library offers fine-grained control over the icon's appearance via the `Config` struct and functional options.
### Customizing an Icon
Here's how to generate an icon with custom colors and settings:
```go
config := &jdenticon.Config{
Hue: 0.3,
Saturation: 0.7,
Lightness: 0.5,
Padding: 0.1,
package main
import (
"context"
"log"
"os"
"github.com/ungluedlabs/go-jdenticon/jdenticon"
)
func main() {
// Create a custom configuration using functional options
config, err := jdenticon.Configure(
jdenticon.WithHueRestrictions([]float64{120, 180, 240}), // Greens, teals, blues only
jdenticon.WithColorSaturation(0.7), // More vivid colors
jdenticon.WithPadding(0.1), // 10% padding
jdenticon.WithBackgroundColor("#f0f0f0"), // Light gray background
)
if err != nil {
log.Fatalf("Failed to create config: %v", err)
}
// Create a generator with the custom config and caching
generator, err := jdenticon.NewGeneratorWithConfig(config, 1000)
if err != nil {
log.Fatalf("Failed to create generator: %v", err)
}
// Generate the icon
icon, err := generator.Generate(context.Background(), "custom-identifier", 200)
if err != nil {
log.Fatalf("Failed to generate icon: %v", err)
}
// Save as SVG
svg, err := icon.ToSVG()
if err != nil {
log.Fatalf("Failed to generate SVG: %v", err)
}
if err := os.WriteFile("custom-icon.svg", []byte(svg), 0644); err != nil {
log.Fatalf("Failed to save SVG: %v", err)
}
log.Println("Generated custom-icon.svg successfully!")
}
```
### Package-Level Functions
For simple use cases, you can use the package-level functions:
```go
// Generate SVG directly
svg, err := jdenticon.ToSVG(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatal(err)
}
icon := jdenticon.GenerateWithConfig("user@example.com", 200, config)
// Generate PNG directly
png, err := jdenticon.ToPNG(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatal(err)
}
// With custom configuration
config := jdenticon.Config{
ColorSaturation: 0.8,
Padding: 0.15,
}
generator, _ := jdenticon.NewGeneratorWithConfig(config, 100)
icon, _ := generator.Generate(context.Background(), "user@example.com", 200)
```
### Command Line Tool
For a full list of configuration options, please see the [GoDoc for the Config struct](https://pkg.go.dev/github.com/ungluedlabs/go-jdenticon/jdenticon#Config).
```bash
# Generate SVG
go run cmd/jdenticon/main.go -value "user@example.com" -size 200
## Concurrency & Performance
# Generate PNG file
go run cmd/jdenticon/main.go -value "user@example.com" -format png -output icon.png
### Thread Safety
**All public functions and types are safe for concurrent use by multiple goroutines.** The library achieves thread safety through several mechanisms:
- **Immutable Icons**: Once created, Icon instances are read-only and can be safely shared across goroutines
- **Thread-Safe Caching**: Internal LRU cache uses RWMutex for optimal concurrent read performance
- **Atomic Operations**: Performance metrics use lock-free atomic counters
- **Protected State**: All shared mutable state is properly synchronized
### Concurrent Usage Patterns
#### ✅ Recommended: Reuse Generator Instances
```go
// Create one generator and share it across goroutines
generator, err := jdenticon.NewGeneratorWithConfig(config, 1000)
if err != nil {
log.Fatal(err)
}
// Safe: Multiple goroutines can use the same generator
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
userID := fmt.Sprintf("user-%d@example.com", id)
icon, err := generator.Generate(context.Background(), userID, 64)
if err != nil {
log.Printf("Failed to generate icon: %v", err)
return
}
// Icons are immutable and safe to share
svg, _ := icon.ToSVG()
// Use svg...
}(i)
}
wg.Wait()
```
#### ✅ Also Safe: Package-Level Functions
```go
// Safe: Uses internal singleton with caching
var wg sync.WaitGroup
for _, email := range emails {
wg.Add(1)
go func(e string) {
defer wg.Done()
icon, _ := jdenticon.Generate(context.Background(), e, 64)
// Process icon...
}(email)
}
wg.Wait()
```
### Performance Recommendations
1. **Larger Cache Sizes**: Use 1000+ entries for high-concurrency scenarios
2. **Generator Reuse**: Create one generator per configuration, share across goroutines
3. **Icon Sharing**: Generated icons are immutable and efficient to share
4. **Monitor Metrics**: Use `GetCacheMetrics()` to track cache performance
```go
// Example: Monitor cache performance
hits, misses := generator.GetCacheMetrics()
ratio := float64(hits) / float64(hits + misses) * 100
log.Printf("Cache hit ratio: %.1f%%", ratio)
```
### Benchmarks
The library is optimized for concurrent workloads:
- **Single-threaded**: ~15,000 icons/second (64x64 PNG)
- **Concurrent (8 cores)**: ~85,000 icons/second with 99%+ cache hits
- **Memory efficient**: ~2.1 KB per operation with LRU caching
Run benchmarks locally:
```sh
go test -bench=. -benchmem ./...
```
## Command-Line Interface (CLI)
The `jdenticon` tool supports both single icon generation and high-performance batch processing.
### Single Icon Generation
**Generate a default 200x200 SVG icon:**
```sh
jdenticon generate "my-cli-icon" > my-cli-icon.svg
```
**Generate a 256x256 PNG icon:**
```sh
jdenticon generate "my-cli-icon" -s 256 -f png -o my-cli-icon.png
```
### Batch Processing
**Generate multiple icons from a text file (one value per line):**
```sh
jdenticon batch users.txt --output-dir ./avatars
```
**High-performance concurrent batch processing:**
```sh
jdenticon batch large-list.txt --output-dir ./avatars --concurrency 8 --format png --size 128
```
**Key batch features:**
- **Concurrent Processing**: Uses worker pool pattern for optimal performance
- **Configurable Workers**: `--concurrency` flag (defaults to CPU count)
- **Progress Tracking**: Real-time progress bar with completion statistics
- **Graceful Shutdown**: Responds to Ctrl+C for clean termination
- **Performance**: Up to 3-4x speedup vs sequential processing
**All available options:**
```sh
jdenticon --help
jdenticon batch --help
```
## Development & Testing
This project includes comprehensive testing and validation infrastructure to ensure quality and identical SVG output.
### Directory Structure
#### `examples/`
Contains practical Go usage examples demonstrating best practices:
- **`concurrent-usage.go`** - Thread-safe patterns, performance optimization, and cache monitoring
- Run examples: `go run examples/concurrent-usage.go`
- Run with race detection: `go run -race examples/concurrent-usage.go`
#### `benchmark/`
Dedicated performance testing infrastructure:
- **Purpose**: Performance comparison between Go and JavaScript implementations
- **Coverage**: Speed benchmarks, memory analysis, regression testing
- **Usage**: Validates performance claims and catches regressions
### Building and Testing
To contribute or build from source:
1. Clone the repository:
```sh
git clone https://github.com/ungluedlabs/go-jdenticon.git
cd go-jdenticon
```
2. Run tests:
```sh
go test ./...
```
3. Run tests with race detection:
```sh
go test -race ./...
```
4. Run benchmarks:
```sh
go test -bench=. ./...
```
5. Build the CLI tool:
```sh
go build ./cmd/jdenticon
```
## API Reference
### Functions
### Core Functions
- `Generate(value string, size int) *Icon` - Generate an identicon with default settings
- `GenerateWithConfig(value string, size int, config *Config) *Icon` - Generate with custom configuration
- `DefaultConfig() *Config` - Get default configuration settings
- `Generate(ctx context.Context, value string, size int) (*Icon, error)` - Generate an icon with default settings
- `ToSVG(ctx context.Context, value string, size int) (string, error)` - Generate SVG directly
- `ToPNG(ctx context.Context, value string, size int) ([]byte, error)` - Generate PNG directly
### Types
### Generator Type
#### Icon
- `ToSVG() string` - Render as SVG string
- `ToPNG() []byte` - Render as PNG byte data
For better performance with multiple icons:
#### Config
- `Hue float64` - Color hue (0.0-1.0)
- `Saturation float64` - Color saturation (0.0-1.0)
- `Lightness float64` - Color lightness (0.0-1.0)
- `BackgroundColor string` - Background color (hex or empty for transparent)
- `Padding float64` - Padding as percentage of size (0.0-0.5)
- `NewGenerator() (*Generator, error)` - Create generator with default cache (1000 entries)
- `NewGeneratorWithCacheSize(size int) (*Generator, error)` - Create generator with custom cache size
- `NewGeneratorWithConfig(config Config, cacheSize int) (*Generator, error)` - Create generator with custom config
- `Generate(ctx context.Context, value string, size int) (*Icon, error)` - Generate icon using this generator
### Icon Type
- `ToSVG() (string, error)` - Render as SVG string
- `ToPNG() ([]byte, error)` - Render as PNG byte data
- `ToPNGWithSize(outputSize int) ([]byte, error)` - Render PNG at different size
### Configuration
Use functional options for flexible configuration:
```go
config, err := jdenticon.Configure(
jdenticon.WithColorSaturation(0.7),
jdenticon.WithPadding(0.1),
jdenticon.WithBackgroundColor("#ffffff"),
jdenticon.WithHueRestrictions([]float64{0, 120, 240}),
)
```
## Security & Input Limits
Go Jdenticon includes configurable DoS protection to prevent resource exhaustion attacks:
```go
config := jdenticon.Config{
MaxIconSize: 4096, // Maximum icon dimension in pixels (default: 4096)
MaxInputLength: 1048576, // Maximum input string length in bytes (default: 1MB)
// ... other config options
}
// Use 0 for default limits, positive values for custom limits, -1 to disable
icon, err := jdenticon.ToSVGWithConfig(context.Background(), "user@example.com", 512, config)
```
**Default Security Limits:**
- **Icon Size:** 4096×4096 pixels maximum (~64MB memory usage)
- **Input Length:** 1MB maximum string length
- **PNG Effective Size:** Automatically adjusts supersampling to stay within limits
**Features:**
- **Smart PNG Handling:** `ToPNG()` automatically reduces supersampling for large icons
- **Configurable Limits:** Override defaults or disable limits entirely for special use cases
- **Structured Errors:** Clear error messages explain limit violations and how to resolve them
- **Fail-Fast Validation:** Invalid inputs are rejected before expensive operations
The Go implementation provides superior DoS protection compared to the JavaScript reference library while producing identical output.
## Performance
The library includes several performance optimizations:
- **LRU Caching:** Generators cache generated icons for repeated use
- **Efficient Rendering:** Optimized SVG and PNG generation
- **Reusable Generators:** Create once, use many times
- **Minimal Allocations:** Careful memory management
Example with caching:
```go
generator, _ := jdenticon.NewGenerator()
// These will be cached
icon1, _ := generator.Generate(context.Background(), "user1@example.com", 64)
icon2, _ := generator.Generate(context.Background(), "user2@example.com", 64)
// Check cache performance
hits, misses := generator.GetCacheMetrics()
fmt.Printf("Cache: %d hits, %d misses\n", hits, misses)
```
## License
MIT License - see the original [Jdenticon](https://github.com/dmester/jdenticon) project for details.
This project is licensed under the Elastic License 2.0 - see the [LICENSE](LICENSE) file for details.
## Contributing
**Key points:**
- Free to use, modify, and distribute
- Cannot be offered as a hosted/managed service
- Cannot remove or circumvent license key functionality
Contributions are welcome! Please ensure all tests pass and follow Go conventions.
## Acknowledgments
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request
* This library is a Go port of the excellent [Jdenticon](https://jdenticon.com) by Daniel Mester
* Special thanks to the original JavaScript implementation for the visual algorithm