nanoid

A tiny, secure, URL-friendly, unique string ID generator for Go
git clone git://git.lair.cx/nanoid
Log | Files | Refs | README

nanoid.go (2305B)


      1 package nanoid
      2 
      3 import (
      4 	"database/sql/driver"
      5 	"io"
      6 	"sync"
      7 	"unsafe"
      8 
      9 	cryptoRand "crypto/rand"
     10 )
     11 
     12 const (
     13 	characters = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
     14 	idSize     = 21
     15 )
     16 
     17 type NanoID [idSize]byte
     18 
     19 // Nil is empty id.
     20 var Nil = NanoID{}
     21 
     22 // Parse parses the string representation of the id.
     23 func Parse(s string) NanoID {
     24 	var id NanoID
     25 	copy(id[:], s)
     26 	return id
     27 }
     28 
     29 // Generator generates nanoid.
     30 type Generator interface {
     31 	Generate() NanoID
     32 }
     33 
     34 type generator struct {
     35 	mu      sync.Mutex
     36 	source  io.Reader
     37 	buf     []byte
     38 	bufSize int
     39 	offset  int
     40 }
     41 
     42 // New returns a new generator.
     43 func New(n int) Generator {
     44 	return newWithReader(cryptoRand.Reader, n)
     45 }
     46 
     47 // NewWithReader returns a new generator with custom source.
     48 func NewWithReader(r io.Reader, n int) Generator {
     49 	return newWithReader(r, n)
     50 }
     51 
     52 func newWithReader(source io.Reader, length int) Generator {
     53 	return &generator{
     54 		source:  source,
     55 		buf:     make([]byte, length*idSize),
     56 		bufSize: length * idSize,
     57 		offset:  length * idSize,
     58 	}
     59 }
     60 
     61 func (g *generator) Generate() NanoID {
     62 	g.mu.Lock()
     63 	defer g.mu.Unlock()
     64 
     65 	if g.offset == g.bufSize {
     66 		g.source.Read(g.buf)
     67 		g.offset = 0
     68 	}
     69 
     70 	var id = (*NanoID)(unsafe.Pointer(&g.buf[g.offset]))
     71 
     72 	for i, v := range id {
     73 		id[i] = characters[v&0b00111111]
     74 	}
     75 
     76 	g.offset += idSize
     77 
     78 	return *id
     79 }
     80 
     81 // Generate generates a new id.
     82 func Generate() NanoID {
     83 	return New(1).Generate()
     84 }
     85 
     86 // GenerateN generates n new ids.
     87 func GenerateN(n int) []NanoID {
     88 	gen := New(n)
     89 	ids := make([]NanoID, n)
     90 
     91 	for i := range ids {
     92 		ids[i] = gen.Generate()
     93 	}
     94 
     95 	return ids
     96 }
     97 
     98 // String returns the string representation of the id.
     99 func (id NanoID) String() string {
    100 	return string(id[:])
    101 }
    102 
    103 // GoString returns the string representation of the id.
    104 func (id NanoID) GoString() string {
    105 	return id.String()
    106 }
    107 
    108 // Bytes returns the byte slice representation of the id.
    109 func (id NanoID) Bytes() []byte {
    110 	return id[:]
    111 }
    112 
    113 // Scan implements the sql.Scanner interface.
    114 func (id *NanoID) Scan(src interface{}) error {
    115 	switch src := src.(type) {
    116 	case []byte:
    117 		copy(id[:], src)
    118 		return nil
    119 	case string:
    120 		copy(id[:], src)
    121 		return nil
    122 	}
    123 
    124 	return nil
    125 }
    126 
    127 // Value implements the driver.Valuer interface.
    128 func (id NanoID) Value() (driver.Value, error) {
    129 	return id.String(), nil
    130 }