// Package ident is the reference implementation of system and member // identifiers. package ident import ( "encoding" "errors" "fmt" "math/rand/v2" "strconv" "sync" "time" "unsafe" ) var ( // ErrNewline is returned for identifiers found to contain newline characters. ErrNewline = errors.New("identifier contains newline characters") // ErrSeparator is returned for representations of [F] missing its separator // byte in its expected index. ErrSeparator = errors.New("identifier has incorrect separator byte") ) // UnexpectedSizeError describes a malformed string representation of an // identifier, with unexpected length. type UnexpectedSizeError struct { Data []byte Want int } func (e *UnexpectedSizeError) Error() string { return "got " + strconv.Itoa(len(e.Data)) + " bytes for " + "a " + strconv.Itoa(e.Want) + "-byte identifier" } // MS is a system or member identifier. type MS interface { encoding.TextMarshaler encoding.TextUnmarshaler fmt.Stringer // EncodedSize returns the number of bytes appended by Encode. EncodedSize() int // Encode appends the canonical string representation of MS to dst and // returns the extended buffer. Encode(dst []byte) []byte // ident is a no-op function but serves to distinguish types that are // identifiers from ordinary types with custom marshaler/unmarshaler // behaviour: a type is an identifier part if it has an ident method. ident() } // F represents a full system or member identifier. type F[V any, T interface { MS *V }] struct { // Underlying system or member. I T // Split from instance suffix after [Separator] byte. Remote Remote } // Separator is the byte separating representation of [MS] from [Remote]. const Separator = ':' // Encode appends the canonical string representation of full to dst and returns // the extended buffer. func (full *F[V, T]) Encode(dst []byte) []byte { dst = full.I.Encode(dst) dst = append(dst, Separator) dst = full.Remote.Encode(dst) return dst } // String returns the canonical string representation of full. func (full *F[V, T]) String() string { s := full.Encode(nil) return unsafe.String(unsafe.SliceData(s), len(s)) } // MarshalText returns the result of Encode. func (full *F[V, T]) MarshalText() (data []byte, err error) { return full.Encode(nil), nil } // UnmarshalText strictly decodes data into full. func (full *F[V, T]) UnmarshalText(data []byte) (err error) { sz := full.I.EncodedSize() if len(data) < sz+1 || data[sz] != Separator { return ErrSeparator } if full.I == nil { full.I = new(V) } if err = full.I.UnmarshalText(data[:sz]); err != nil { return } return full.Remote.UnmarshalText(data[sz+1:]) } // PartG is machine-specific state of [Generator]. type PartG struct { // Deployment site, typically denoting a datacenter servicing a region. Site uint32 // Servicing host behind load balancer, unique within its Site. Host uint32 } // A Generator emits unique instances of [S]. type Generator struct { PartG mu sync.Mutex r rand.Source } // S populates the value pointed to by p with a new unique value. func (g *Generator) S(p *S) { g.mu.Lock() *p = S{PartG: g.PartG, Time: uint64(time.Now().UnixNano()), ID: g.r.Uint64()} g.mu.Unlock() } // NewS creates a unique instance of [S] and returns its address. func (g *Generator) NewS() *S { var sid S g.S(&sid) return &sid } // M populates the value pointed to by p with a new unique value. func (g *Generator) M(p *PartM, serial uint64) { g.mu.Lock() *p = PartM{Serial: serial, Time: uint64(time.Now().UnixNano()), ID: g.r.Uint64()} g.mu.Unlock() } // New initialises a new instance of [Generator] and returns its address. func New(site, host uint32) *Generator { return &Generator{PartG{site, host}, sync.Mutex{}, rand.NewPCG( uint64(site)<<32|uint64(host), uint64(time.Now().UnixNano()), )} }