nanoid

package module
v1.14.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2024 License: Apache-2.0 Imports: 10 Imported by: 1

README

nanoid Nano ID logo by Anton Lovchikov

CI Go Quality Gate Status GitHub issues Go Reference Go Report Card

A simple, fast, and efficient Go implementation of Nano ID, a tiny, secure, URL-friendly, unique string ID generator.

Features

  • Short & Unique IDs: Generates compact and collision-resistant identifiers.
  • Cryptographically Secure: Utilizes Go's crypto/rand package for generating cryptographically secure random numbers. This guarantees that the generated IDs are both unpredictable and suitable for security-sensitive applications.
  • Customizable:
    • Define your own set of characters for ID generation with a minimum length of 2 characters and maximum length of 256 characters.
    • Define your own random number generator.
    • Unicode and ASCII alphabets supported.
  • Concurrency Safe: Designed to be safe for use in concurrent environments.
  • High Performance: Optimized with buffer pooling to minimize allocations and enhance speed.
  • Optimized for Low Allocations: Carefully structured to minimize heap allocations, reducing memory overhead and improving cache locality. This optimization is crucial for applications where performance and resource usage are critical.
    • 1 allocs/op for ASCII and Unicode alphabets.
  • Zero Dependencies: Lightweight implementation with no external dependencies beyond the standard library.
  • Supports io.Reader Interface:
    • The Nano ID generator now satisfies the io.Reader interface, allowing it to be used interchangeably with any io.Reader implementations.
    • Developers can now utilize the Nano ID generator in contexts such as streaming data processing, pipelines, and other I/O-driven operations.

Installation

To install the package, use:

go get -u github.com/sixafter/nanoid

Importing the Package

To use the NanoID package in your Go project, import it as follows:

import "github.com/sixafter/nanoid"

Usage

Basic Usage with Default Settings

The simplest way to generate a Nano ID is by using the default settings. This utilizes the predefined alphabet and default ID length.

package main

import (
  "fmt"
  "github.com/sixafter/nanoid"
)

func main() {
  id, err := nanoid.New() 
  if err != nil {
    panic(err)
  }
  fmt.Println("Generated ID:", id)
}

Output:

Generated ID: mGbzQkkPBidjL4IP_MwBM
Generating a Nano ID with Custom length

Generate a NanoID with a custom length.

package main

import (
  "fmt"
  "github.com/sixafter/nanoid"
)

func main() {
  id, err := nanoid.NewWithLength(10)
  if err != nil {
    panic(err)
  }
  fmt.Println("Generated ID:", id)
}

Output:

Generated ID: 1A3F5B7C9D
Using io.Reader Interface

Here's a simple example demonstrating how to use the Nano ID generator as an io.Reader:

package main

import (
  "fmt"
  "io"
  "github.com/sixafter/nanoid"
)

func main() {
	// Nano ID default length is 21
	buf := make([]byte, nanoid.DefaultLength)

	// Read a Nano ID into the buffer
	_, err := nanoid.Read(buf)
	if err != nil && err != io.EOF {
		panic(err)
	}

	// Convert the byte slice to a string
	id := string(buf)
	fmt.Printf("Generated ID: %s\n", id)
}

Output:

Generated ID: 2mhTvy21bBZhZcd80ZydM
Customizing the Alphabet and ID Length

You can customize the alphabet by using the WithAlphabet option and generate an ID with a custom length.

package main

import (
	"fmt"

	"github.com/sixafter/nanoid"
)

func main() {
	// Define a custom alphabet
	alphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

	// Create a new generator with custom alphabet and length hint
	gen, err := nanoid.NewGenerator(
		nanoid.WithAlphabet(alphabet),
		nanoid.WithLengthHint(10),
	)
	if err != nil {
		fmt.Println("Error creating Nano ID generator:", err)
		return
	}

	// Generate a Nano ID using the custom generator
	id, err := gen.New(10)
	if err != nil {
		fmt.Println("Error generating Nano ID:", err)
		return
	}

	fmt.Println("Custom Nano ID:", id)
}

Output"

Custom Nano ID: G5J8K2M0QZ

Functions

New

Generates a Nano ID with the specified length using the default generator.

func New() (string, error)
  • Returns:
    • string: The generated Nano ID.
    • error: An error if the generation fails.
NewWithLength

Generates a Nano ID with the specified length using the default generator.

func NewWithLength(length int) (string, error)
  • Parameters:
    • length (int): The desired length of the Nano ID. Must be a positive integer.
  • Returns:
    • string: The generated Nano ID.
    • error: An error if the generation fails.
Must

Generates a Nano ID with the specified length using the default generator.

func Must() string
  • Returns:
    • string: The generated Nano ID.
MustWithLength

Generates a Nano ID with the specified length using the default generator.

func NewWithLength(length int) string
  • Parameters:
    • length (int): The desired length of the Nano ID. Must be a positive integer.
  • Returns:
    • string: The generated Nano ID.
NewGenerator

Creates a new Nano ID generator with a custom alphabet and random source.

func NewGenerator(options ...Option) (Generator, error)
  • Parameters:
    • options (Option): Variadic Option parameters to configure the Generator. Options are:
      • WithAlphabet: Sets a custom alphabet for the Generator, allowing the user to specify which characters will be used in the generated IDs.
      • WithRandReader: Sets a custom random reader for the Generator, enabling the use of a specific source of randomness.
      • WithLengthHint: Sets a hint for the intended length of the generated IDs, helping to optimize internal allocations based on the expected ID size.
  • Returns:
    • Generator: A new Nano ID generator.
    • error: An error if the configuration is invalid.
Read

Generates a Nano ID with the specified length using the default generator.

func Read(p []byte) (n int, err error)
  • Parameters:
    • p ([]byte): The byte slice to store the generated ID.
  • Returns:
    • n: The actual number of bytes read.
    • error: An error if the generation fails.
Generator Interface

Defines the method to generate Nano IDs.

// Generator defines the interface for generating Nano IDs.
type Generator interface {
    // New returns a new Nano ID of the specified length.
	New(length int) (string, error)

    // Read reads up to len(p) bytes into p. It returns the number of bytes read.
    Read(p []byte) (n int, err error)
}
Configuration Interface

Provides access to the generator's configuration.

// Configuration defines the interface for retrieving generator configuration.
type Configuration interface {
    // Config returns the configuration of the generator.
	Config() Config
}

Error Handling

The nanoid module defines several error types to handle various failure scenarios:

  • ErrDuplicateCharacters: Returned when the alphabet contains duplicate characters.
  • ErrExceededMaxAttempts: Returned when the generation process exceeds the maximum number of attempts.
  • ErrInvalidLength: Returned when a non-positive Nano ID length is specified.
  • ErrInvalidAlphabet: Returned when an alphabet is invalid; e.g. due to length constraints.
  • ErrNonUTF8Alphabet: Returned when an alphabet contains invalid UTF-8 characters.
  • ErrAlphabetTooShort: Returned when alphabet length is less than 2 characters.
  • ErrAlphabetTooLong: Returned when an alphabet length exceeds 256 characters.
  • ErrNilRandReader: Returned when a nil random reader is provided.

Constants

  • DefaultAlphabet: The default alphabet used for ID generation: _-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  • DefaultLength: The default length of the generated ID: 21
  • MinAlphabetLength: The minimum allowed length for the alphabet: 2
  • MaxAlphabetLength: The maximum allowed length for the alphabet: 256

Performance Optimizations

Buffer Pooling with sync.Pool

The nanoid generator utilizes sync.Pool to manage byte slice buffers efficiently. This approach minimizes memory allocations and enhances performance, especially in high-concurrency scenarios.

How It Works:

  • Storing Pointers: sync.Pool stores pointers to []byte slices (*[]byte) instead of the slices themselves. This avoids unnecessary allocations and aligns with best practices for using sync.Pool.
  • Zeroing Buffers: Before returning buffers to the pool, they are zeroed out to prevent data leaks.
Struct Optimization

The generator struct is optimized for memory alignment and size by ordering from largest to smallest to minimize padding and optimize memory usage.

Execute Benchmarks:

Run the benchmarks using the go test command with the bench make target:

make bench
Interpreting Results:

Sample output might look like this:

Expand to see results
go clean
go test -bench=. -benchmem -memprofile=mem.out -cpuprofile=cpu.out
goos: darwin
goarch: arm64
pkg: github.com/sixafter/nanoid
cpu: Apple M2 Ultra
BenchmarkNanoIDAllocations-24                            3712894               322.7 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDAllocationsConcurrent-24                  1231219               955.8 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_DefaultLength-24                 4234353               280.7 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_2-24              12416870                93.76 ns/op            2 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_3-24              12173713                99.60 ns/op            3 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_5-24              10923501               107.6 ns/op             5 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_13-24              4239207               270.7 ns/op            16 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_21-24              4208509               281.1 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_VaryingBufferSizes/BufferSize_34-24              3979149               305.3 ns/op            48 B/op          1 allocs/op
BenchmarkGenerator_Read_ZeroLengthBuffer-24                             639281161                1.849 ns/op           0 B/op          0 allocs/op
BenchmarkGenerator_Read_Concurrent/Concurrency_1-24                      4263008               277.4 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_Concurrent/Concurrency_2-24                      2359436               504.1 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_Concurrent/Concurrency_4-24                      2109841               579.5 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_Concurrent/Concurrency_8-24                      1752210               691.3 ns/op            24 B/op          1 allocs/op
BenchmarkGenerator_Read_Concurrent/Concurrency_16-24                     1559118               771.3 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen8-24                  10983956               107.2 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen16-24                  4418779               271.6 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen21-24                  4360071               276.5 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen32-24                  4148890               290.3 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen64-24                  3441111               341.5 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen2/IDLen128-24                 2868350               418.5 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen8-24                 10985125               107.3 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen16-24                 4445092               272.2 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen21-24                 4210738               280.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen32-24                 4063539               293.6 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen64-24                 3444986               343.7 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen16/IDLen128-24                2843654               418.3 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen8-24                 11157402               106.2 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen16-24                 4483576               267.6 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen21-24                 4215984               279.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen32-24                 4068142               289.4 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen64-24                 3521085               339.6 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen32/IDLen128-24                2830250               418.3 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen8-24                 11252030               105.6 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen16-24                 4366692               270.8 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen21-24                 4237465               277.9 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen32-24                 4135947               291.2 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen64-24                 3521300               342.6 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGeneration/ASCII_AlphabetLen64/IDLen128-24                2839201               416.9 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen8-24                 8620464               138.4 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen16-24                3589316               335.3 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen21-24                3283752               362.6 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen32-24                2684456               442.9 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen64-24                1934439               624.9 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen2/IDLen128-24               1239282               952.9 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen8-24                8569690               138.7 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen16-24               3584649               334.2 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen21-24               3297488               362.6 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen32-24               2699508               442.9 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen64-24               1943648               624.9 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen16/IDLen128-24              1254345               968.4 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen8-24                8263024               139.8 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen16-24               3584301               333.1 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen21-24               3330494               368.5 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen32-24               2673807               457.9 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen64-24               1926444               627.1 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen32/IDLen128-24              1234162               970.5 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen8-24                8431142               140.7 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen16-24               3542473               335.7 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen21-24               3319472               365.9 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen32-24               2671075               449.5 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen64-24               1907966               627.3 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDGeneration/Unicode_AlphabetLen64/IDLen128-24              1222561              1001 ns/op             288 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen8-24           2961512               379.5 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen16-24          1529432               786.1 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen21-24          1517160               789.1 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen32-24          1472512               809.6 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen64-24          1341105               899.1 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen2/IDLen128-24         1000000              1007 ns/op             128 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen8-24          3250173               369.9 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen16-24         1548758               777.2 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen21-24         1531372               785.8 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen32-24         1484287               818.3 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen64-24         1349370               897.7 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen16/IDLen128-24        1000000              1005 ns/op             128 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen8-24          3223686               367.9 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen16-24         1559476               763.6 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen21-24         1538126               786.9 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen32-24         1489722               808.4 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen64-24         1334155               893.8 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen32/IDLen128-24        1000000              1007 ns/op             128 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen8-24          3228020               367.0 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen16-24         1562257               772.9 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen21-24         1534208               794.1 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen32-24         1482178               820.8 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen64-24         1291593               912.6 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/ASCII_AlphabetLen64/IDLen128-24        1000000              1013 ns/op             128 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen8-24         2582572               447.4 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen16-24        1356303               884.3 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen21-24        1297418               916.1 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen32-24        1205206               999.9 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen64-24        1000000              1105 ns/op             144 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen2/IDLen128-24        954236              1289 ns/op             288 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen8-24        2734597               439.7 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen16-24       1370898               870.0 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen21-24       1308649               911.4 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen32-24       1207134              1001 ns/op              80 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen64-24       1000000              1096 ns/op             144 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen16/IDLen128-24               909823              1284 ns/op             288 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen8-24                2727334               447.3 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen16-24               1367902               874.9 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen21-24               1304487               909.8 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen32-24               1210693               992.3 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen64-24               1000000              1100 ns/op             144 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen32/IDLen128-24               936051              1282 ns/op             288 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen8-24                2763672               440.0 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen16-24               1374480               877.7 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen21-24               1311806               915.6 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen32-24               1206374               998.4 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen64-24               1000000              1110 ns/op             144 B/op          1 allocs/op
BenchmarkNanoIDGenerationParallel/Unicode_AlphabetLen64/IDLen128-24               905634              1293 ns/op             288 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen8-24          10815402               111.7 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen16-24          4383463               274.0 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen21-24          4291173               281.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen32-24          4070809               293.2 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen64-24          3513301               345.1 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen2/IDLen128-24         2846863               420.5 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen8-24         10877348               106.7 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen16-24         4422064               271.9 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen21-24         4270226               280.4 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen32-24         4150038               290.4 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen64-24         3549493               338.5 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen16/IDLen128-24        2864341               423.0 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen8-24         11224105               107.7 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen16-24         4445605               271.7 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen21-24         4286137               279.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen32-24         4077279               293.0 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen64-24         3550720               338.2 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen32/IDLen128-24        2860690               417.7 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen8-24         11053956               106.8 ns/op             8 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen16-24         4430721               269.8 ns/op            16 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen21-24         4274026               281.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen32-24         4103462               294.2 ns/op            32 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen64-24         3465758               341.4 ns/op            64 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/ASCII_AlphabetLen64/IDLen128-24        2826387               423.0 ns/op           128 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen8-24         8437821               140.1 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen16-24        3514735               333.5 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen21-24        3296300               359.7 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen32-24        2735949               438.0 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen64-24        1939842               617.0 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen2/IDLen128-24       1252837               964.5 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen8-24        8389263               140.8 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen16-24       3565263               334.2 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen21-24       3299416               359.6 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen32-24       2689170               438.2 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen64-24       1944435               616.8 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen16/IDLen128-24      1243393               957.3 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen8-24        8506532               140.3 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen16-24       3623780               335.9 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen21-24       3332631               360.4 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen32-24       2690766               439.7 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen64-24       1938931               611.8 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen32/IDLen128-24      1259449               954.4 ns/op           288 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen8-24        8482705               138.6 ns/op            24 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen16-24       3617001               332.1 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen21-24       3348201               359.2 ns/op            48 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen32-24       2670768               440.9 ns/op            80 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen64-24       1944044               611.5 ns/op           144 B/op          1 allocs/op
BenchmarkNanoIDWithVaryingAlphabetLengths/Unicode_AlphabetLen64/IDLen128-24      1245513               951.2 ns/op           288 B/op          1 allocs/op
PASS
ok      github.com/sixafter/nanoid      260.726s
  • ns/op: Nanoseconds per operation. Lower values indicate faster performance.
  • B/op: Bytes allocated per operation. Lower values indicate more memory-efficient code.
  • allocs/op: Number of memory allocations per operation. Fewer allocations generally lead to better performance.

Nano ID Generation

Nano ID generates unique identifiers based on the following:

  1. Random Byte Generation: Nano ID generates a sequence of random bytes using a secure random source (e.g., crypto/rand.Reader).
  2. Mapping to Alphabet: Each random byte is mapped to a character in a predefined alphabet to form the final ID.
  3. Uniform Distribution: To ensure that each character in the alphabet has an equal probability of being selected, Nano ID employs techniques to avoid bias, especially when the alphabet size isn't a power of two.

Custom Alphabet Constraints

  1. Alphabet Lengths:
    • At Least Two Characters: The custom alphabet must contain at least two unique characters. An alphabet with fewer than two characters cannot produce IDs with sufficient variability or randomness.
    • Maximum Length 256 Characters: The implementation utilizes a rune-based approach, where each character in the alphabet is represented by a single rune. This allows for a broad range of unique characters, accommodating alphabets with up to 256 distinct runes. Attempting to use an alphabet with more than 256 runes will result in an error.
  2. Uniqueness of Characters:
    • All Characters Must Be Unique. Duplicate characters in the alphabet can introduce biases in ID generation and compromise the randomness and uniqueness of the IDs. The generator enforces uniqueness by checking for duplicates during initialization. If duplicates are detected, it will return an ErrDuplicateCharacters error.
  3. Character Encoding:
    • Support for Unicode: The generator accepts alphabets containing Unicode characters, allowing you to include a wide range of symbols, emojis, or characters from various languages.
  4. Power-of-Two Considerations:
    • Mask Calculation: The generator calculates a mask based on the number of bits required to represent the alphabet length minus one.
    k := bits.Len(uint(alphabetLen - 1))
    mask := byte((1 << k) - 1)
    
    • Implications: While the alphabet length doesn't need to be a power of two, the mask is used to efficiently reduce bias in random number generation. The implementation ensures that each character in the alphabet has an equal probability of being selected by using this mask.

Determining Collisions

To determine the practical length for a NanoID for your use cases, see the collision time calculator here.

Contributing

Contributions are welcome. See CONTRIBUTING

License

This project is licensed under the Apache 2.0 License. See LICENSE file.

Documentation

Index

Constants

View Source
const (
	// DefaultAlphabet as per Nano ID specification; A-Za-z0-9_-.
	DefaultAlphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

	// DefaultLength is the default size of the generated Nano ID: 21.
	DefaultLength = 21

	// MinAlphabetLength defines the minimum allowed length for the alphabet.
	MinAlphabetLength = 2

	// MaxAlphabetLength defines the maximum allowed length for the alphabet.
	MaxAlphabetLength = 256
)

Variables

View Source
var (
	ErrDuplicateCharacters = errors.New("duplicate characters in alphabet")
	ErrExceededMaxAttempts = errors.New("exceeded maximum attempts")
	ErrInvalidLength       = errors.New("invalid length")
	ErrInvalidAlphabet     = errors.New("invalid alphabet")
	ErrNonUTF8Alphabet     = errors.New("alphabet contains invalid UTF-8 characters")
	ErrAlphabetTooShort    = errors.New("alphabet length is less than 2")
	ErrAlphabetTooLong     = errors.New("alphabet length exceeds 256")
	ErrNilRandReader       = errors.New("nil random reader")
)

Functions

func Must added in v1.10.0

func Must() string

Must returns a new Nano ID using `DefaultLength` if err is nil or panics otherwise. It simplifies safe initialization of global variables holding compiled UUIDs.

func MustWithLength added in v1.10.0

func MustWithLength(length int) string

MustWithLength returns a new Nano ID of the specified length if err is nil or panics otherwise. It simplifies safe initialization of global variables holding compiled UUIDs.

func New added in v1.3.0

func New() (string, error)

New returns a new Nano ID using `DefaultLength`.

func NewWithLength added in v1.10.0

func NewWithLength(length int) (string, error)

NewWithLength returns a new Nano ID of the specified length.

func Read added in v1.14.0

func Read(p []byte) (n int, err error)

Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.

Reader is the interface that wraps the basic Read method.

When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF.

Callers should always process the n > 0 bytes returned before considering the error err. Doing so correctly handles I/O errors that happen after reading some bytes and also both of the allowed EOF behaviors.

If len(p) == 0, Read should always return n == 0. It may return a non-nil error if some error condition is known, such as EOF.

Implementations of Read are discouraged from returning a zero byte count with a nil error, except when len(p) == 0. Callers should treat a return of 0 and nil as indicating that nothing happened; in particular it does not indicate EOF.

Implementations must not retain p.

Types

type Config added in v1.5.0

type Config interface {
	// AlphabetLen returns the length of the alphabet used for ID generation.
	AlphabetLen() uint16

	// BaseMultiplier returns the base multiplier used to determine the growth rate of buffer size, accounting for small ID lengths to achieve balance.
	BaseMultiplier() int

	// BitsNeeded returns the number of bits required to generate each character in the ID.
	BitsNeeded() uint

	// BufferMultiplier returns the multiplier used to determine how many characters the buffer should handle per read.
	BufferMultiplier() int

	// BufferSize returns the calculated size of the buffer used for random byte generation.
	BufferSize() int

	// ByteAlphabet returns the slice of bytes for ASCII alphabets.
	ByteAlphabet() []byte

	// BytesNeeded returns the number of bytes required from the random source to produce the entire ID.
	BytesNeeded() uint

	// IsASCII returns true if the alphabet consists solely of ASCII characters.
	IsASCII() bool

	// IsPowerOfTwo returns true if the length of the alphabet is a power of two, optimizing random selection for efficient bit operations.
	IsPowerOfTwo() bool

	// LengthHint returns the hint of the intended length of the IDs to be generated.
	LengthHint() uint16

	// Mask returns the bitmask used to obtain a random value from the character set.
	Mask() uint

	// RandReader returns the source of randomness used for generating IDs.
	RandReader() io.Reader

	// RuneAlphabet returns the slice of runes used for ID generation, allowing support for multibyte characters.
	RuneAlphabet() []rune

	// ScalingFactor returns the scaling factor used to balance the alphabet size and ID length, ensuring smoother growth in buffer size calculations.
	ScalingFactor() int
}

Config holds the runtime configuration for the Nano ID generator. It is immutable after initialization.

type ConfigOptions added in v1.10.0

type ConfigOptions struct {
	// RandReader is the source of randomness used for generating IDs.
	// By default, it uses crypto/rand.Reader, which provides cryptographically secure random bytes.
	RandReader io.Reader

	// Alphabet is the set of characters used to generate the Nano ID.
	// It must be a valid UTF-8 string containing between 2 and 256 unique characters.
	// Using a diverse and appropriately sized alphabet ensures the uniqueness and randomness of the generated IDs.
	Alphabet string

	// LengthHint specifies a typical or default length for generated IDs.
	LengthHint uint16
}

ConfigOptions holds the configurable options for the Generator. It is used with the Function Options pattern.

type Configuration added in v1.5.0

type Configuration interface {
	// Config returns the runtime configuration of the generator.
	Config() Config
}

Configuration defines the interface for retrieving generator configuration.

type Generator added in v1.5.0

type Generator interface {
	// New returns a new Nano ID of the specified length.
	New(length int) (string, error)

	// Read reads up to len(p) bytes into p. It returns the number of bytes read.
	Read(p []byte) (n int, err error)
}

Generator defines the interface for generating Nano IDs.

var DefaultGenerator Generator

DefaultGenerator is a global, shared instance of a Nano ID generator. It is safe for concurrent use.

func NewGenerator added in v1.10.0

func NewGenerator(options ...Option) (Generator, error)

NewGenerator creates a new Generator with buffer pooling enabled. It accepts variadic Option parameters to configure the Generator. It returns an error if the alphabet is invalid or contains invalid UTF-8 characters.

type Option added in v1.10.0

type Option func(*ConfigOptions)

Option defines a function type for configuring the Generator.

func WithAlphabet added in v1.10.0

func WithAlphabet(alphabet string) Option

WithAlphabet sets a custom alphabet for the Generator.

func WithLengthHint added in v1.12.0

func WithLengthHint(hint uint16) Option

WithLengthHint sets the hint of the intended length of the IDs to be generated.

func WithRandReader added in v1.10.0

func WithRandReader(reader io.Reader) Option

WithRandReader sets a custom random reader for the Generator.

Jump to

Keyboard shortcuts

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