refid

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2023 License: MIT Imports: 11 Imported by: 1

README

RefId

Build Status GoDoc Go Report Card License

About

A RefId is a sortable unique identifier, similar to UUIDv7, with a few difference.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           unix_ts_µs                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   unix_ts_µs                  |      tag      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             rand_b                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             rand_b                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
7 bytes unix_ts_µs:
    48 bits of microseconds from 1970 (about 2280 or so years worth)
1 byte tag:
    255 separate tags (0 being untagged)
8 bytes random pad:
    fill with crypto/rand random

Features

  • tagging (support for 255 distinct tags)
  • unix timestamp with microsecond precision
  • supports go/sql scanner/valuer
  • multiple encodings supported: native (base32), base64, base16 (hex)
  • similar to UUIDv7, with different tradeoffs
    • slightly larger random section
    • tag support
    • no UUID version field
    • not an rfc standard

Non-Features

  • refids, like UUIDs, do not internally perform any signature verification. If the validity of the encoded timestamp and tag are required for any secure operations, the refid SHOULD be externally verified before parsing/decoding. An example of this could be a wrapping encoder/decoder doing hmac signing and verification.

Inspirations

Installation

go get -u github.com/dropwhile/refid

Usage

Simple
// generate refid
rId, err := refid.New()
// generate refid (or panic)
rId = refid.Must(refid.New())

// encoding...
// encode to native encoding (base32 with Crockford alphabet)
s := rId.String() // "0r326xw2xbpga5tya7px89m7hw"
// encode to base64 encoding
s = rId.ToBase64String() // "BgYjd4Lq7QUXXlHt1CaHjw"
// encode to hex encoding
s = rId.ToHexString() // "0606237782eaed05175e51edd426878f"
// raw bytes
b := rId.Bytes()

// decoding...
// decode from native
rId2, err := refid.Parse(s)
// decode from base64
rId2, err = refid.FromBase64String(s)
// decode from hex
rId2, err = refid.FromHexString(s)

// get the time out of a RefId (as a time.Time)
var ts time.Time = rId2.Time()
Tagging

Simple tagging usage:

myTag := 2

// generate a RefId with tag set to 1
rId = refid.Must(refid.NewTagged(1))
// you can also set it manually after generation
rId.SetTag(myTag)
// check if it is tagged
rId.Tagged() // true
// check if it has a specific tag
rId.HasTag(1) // false
rId.HasTag(2) // true


s := rId.String()
// require desired tag or fail parsing
r, err := refid.ParseTagged(1, s) // err != nil here, as refid was tagged 2
r, err = refid.ParseTagged(2, s) // err == nil here, as refid was tagged 2
What use is tagging?

Tag support ensures that a refid of a certain tag type can be made distinct from other refids -- those of a different tag type, or those with no tag type.

A hypothetical example is a refid url paramater for a type named "author", can be enforced as invalid when someone attempts to supply it as input for a different refid url parameter for a type named "book".

Making tagging usage easier with RefIdTagger:

// AuthorRefId ensures it will only succesfully generate and parse tag=2 refids
AuthorRefIdT := refid.RefIdTagger(2)
// BookRefId ensures it will only succesfully generate and parse tag=3 refids
BookRefIdT := refid.RefIdTagger(3)

authorRefId := refid.Must(AuthorRefIdT.New()) // generated with a tag of 2
authorRefId.HasTag(2) // true
bookRefId := refid.Must(BookRefIdT.New()) // generated with a tag of 3
bookRefId.HasTag(3) // true

r, err := AuthorRefIdT.Parse(authorRefId.String()) // succeeds; err == nil
r, err = bookRefId.Parse(authorRefId.String()) // fails; err != nil

reftool command like utility

Installation:

go install github.com/dropwhile/refid/cmd/reftool@latest
# generate a refid with a tag of 5
% reftool generate -t 5
native enc:   0r326xw2xbpga5tya7px89m7hw
hex enc:      0606237782eaed05175e51edd426878f
base64 enc:   BgYjd4Lq7QUXXlHt1CaHjw
tag value:    5
time(string): 2023-09-24T23:47:38.954477Z
time(micros): 1695599258954477

# generate a refid with a tag of 5, and only output the native(base32) encoding
% reftool generate -t 5 -o
0r34ky6h51r012an8skhbsvxt0

# generate a refid with a tag of 5, and only output the hex encoding
% reftool generate -t 5 -o=hex
060649f82794f10039169e91d0696763

# generate a refid with a tag of 5, and only output the base64 encoding
% reftool generate -o=base64
BgZJ-i1F2wALdZFJrWvNzA

# genrate a refid with a tag of 2, at a specific timestamp
% reftool generate -t 2 -w "2023-01-01T00:00:11.123456Z"
native enc:   0qrjh15pzc004nzrkbpcp2v0wm
hex enc:      05f12884b6fb000257f89aeccb0b60e5
base64 enc:   BfEohLb7AAJX-Jrsywtg5Q
tag value:    2
time(string): 2023-01-01T00:00:11.123456Z
time(micros): 1672531211123456

# decode a refid and display
% reftool decode 0qrjh15pzc004nzrkbpcp2v0wm
native enc:   0qrjh15pzc004nzrkbpcp2v0wm
hex enc:      05f12884b6fb000257f89aeccb0b60e5
base64 enc:   BfEohLb7AAJX-Jrsywtg5Q
tag value:    2
time(string): 2023-01-01T00:00:11.123456Z
time(micros): 1672531211123456

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// Nil is the nil RefId, that has all 128 bits set to zero.
	Nil = RefId{}
)

Functions

This section is empty.

Types

type AnyMatcher

type AnyMatcher struct {
	// contains filtered or unexported fields
}

A matcher that supports the following interfaces:

func MatchAny

func MatchAny(tag byte) AnyMatcher

Create a AnyMatcher matcher that matches that matches against a specific Tag. Any valid RefIds that do not match the tag specified, will be considered not matching.

If tag is 0, will support matching any RefId (tag is then ignored)

Example usage:

mock.ExpectQuery("^INSERT INTO some_table (.+)").
 WithArgs(refid.MatchAny(1), 1).
 WillReturnRows(rows)

func (AnyMatcher) Match

func (a AnyMatcher) Match(v interface{}) bool

type NullRefId

type NullRefId struct {
	RefId RefId
	Valid bool
}

NullRefId can be used with the standard sql package to represent a RefId value that can be NULL in the database.

func (NullRefId) MarshalJSON

func (u NullRefId) MarshalJSON() ([]byte, error)

MarshalJSON marshals the NullRefId as null or the nested RefId

func (*NullRefId) Scan

func (u *NullRefId) Scan(src interface{}) error

Scan implements the sql.Scanner interface.

func (*NullRefId) UnmarshalJSON

func (u *NullRefId) UnmarshalJSON(b []byte) error

UnmarshalJSON unmarshals a NullRefId

func (NullRefId) Value

func (u NullRefId) Value() (driver.Value, error)

Value implements the sql/driver.Valuer interface.

type RefId

type RefId [size]byte

A RefId is a 16 byte identifier that is:

  • unix timestamp with microsecond precision 48 bits of microseconds from 1970 (about 2280 or so years worth)
  • sql index friendly
  • tagging support (support for 255 distinct tag types)
  • supports go/sql scanner/valuer
  • multiple encodings supported: native (base32), base64, base16 (hex)
  • similar to UUIDv7, with different tradeoffs: slightly larger random section, tag support, no UUID version field, not an rfc standard

func FromBase64String

func FromBase64String(input string) (RefId, error)

FromBase64String parses a base64 string and returns a RefId. Returns an error if the base64 string is of improper size or otherwise fails to parse.

func FromBytes

func FromBytes(input []byte) (RefId, error)

FromBytes creates a new RefId from a byte slice. Returns an error if the slice does not have a length of 16. The bytes are copied from the slice.

func FromHexString

func FromHexString(input string) (RefId, error)

FromHexString parses a base16/hex string and returns a RefId. Returns an error if the base16/hex string is of improper size or otherwise fails to parse.

func FromString

func FromString(s string) (RefId, error)

FromString is an alias of Parse.

func Must

func Must(r RefId, err error) RefId

Must is a helper that wraps a call to a function returning (RefId, error) and panics if the error is non-nil. It is intended for use in variable initializations such as

var (
	refA = refid.Must(refid.New())
	refB = refid.Must(refid.NewTagged(2))
	refC = refid.Must(refid.Parse("0r2nbq0wqhjg186167t0gcd1gw"))
	refD = refid.Must(refid.ParseTagged("0r2nbq0wqhjg186167t0gcd1gw", 2))
)

func New

func New() (RefId, error)

New returns a new RefId.

If random bytes cannot be generated, it will return an error.

func NewTagged

func NewTagged(tag byte) (RefId, error)

NewTagged returns a RefId tagged with tag.

If random bytes cannot be generated, it will return an error.

func Parse

func Parse(s string) (RefId, error)

Parse parses a textual RefId representation, and returns a RefId. Supports parsing the following text formats:

  • native/base32 (Crockford's alphabet)
  • base64
  • base16/hex

Will return an error on parse failure.

func ParseTagged

func ParseTagged(tag byte, s string) (RefId, error)

ParseTagged parses a textual RefId representation (same formats as Parse),while additionally requiring the parsed RefId to be tagged with tag, and returns a RefId.

Returns an error if RefId fails to parse or if RefId is not tagged with tag.

func (RefId) Bytes

func (r RefId) Bytes() []byte

Bytes returns a slice of a copy of the current RefId underlying data.

func (*RefId) ClearTag

func (r *RefId) ClearTag() *RefId

ClearTag clears the RefId tag.

func (RefId) Equal

func (r RefId) Equal(other RefId) bool

Equal compares a RefId to another RefId to see if they have the same underlying bytes.

func (RefId) Format

func (r RefId) Format(f fmt.State, c rune)

Format implements the fmt.Formatter interface.

func (RefId) HasTag

func (r RefId) HasTag(tag byte) bool

IsTagged reports whether the RefId is tagged and if so, if it is tagged with tag.

func (RefId) IsNil

func (r RefId) IsNil() bool

IsNil reports if the RefId is the nil value RefId.

func (RefId) IsTagged

func (r RefId) IsTagged() bool

IsTagged reports whether the RefId is tagged.

func (RefId) MarshalBinary

func (r RefId) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface.

func (RefId) MarshalJSON

func (r RefId) MarshalJSON() ([]byte, error)

MarshalJson implements the json.Marshaler interface.

func (RefId) MarshalText

func (r RefId) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (*RefId) Scan

func (r *RefId) Scan(src interface{}) error

Scan implements the sql.Scanner interface. A 16-byte slice will be handled by RefId.UnmarshalBinary, while a longer byte slice or a string will be handled by RefId.UnmarshalText.

func (*RefId) SetTag

func (r *RefId) SetTag(tag byte) *RefId

SetTag sets the RefId tag to the specified value.

func (*RefId) SetTime

func (r *RefId) SetTime(ts time.Time) *RefId

SetTime sets the time component of a RefId to the time specified by ts.

func (RefId) String

func (r RefId) String() string

String returns the native (base32 w/Crockford alphabet) textual represenation of a RefId

func (RefId) Tag

func (r RefId) Tag() byte

Tag returns the current tag of the RefId. If the RefId is untagged, it will retrun 0.

func (RefId) Time

func (r RefId) Time() time.Time

Time returns the timestamp portion of a RefId as a time.Time

func (RefId) ToBase64String

func (r RefId) ToBase64String() string

String returns the base64 textual represenation of a RefId

func (RefId) ToHexString

func (r RefId) ToHexString() string

String returns the base16/hex textual represenation of a RefId

func (RefId) ToString

func (r RefId) ToString() string

ToString is an alias of [String]

func (*RefId) UnmarshalBinary

func (r *RefId) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. It will return an error if the slice isn't of appropriate size.

func (*RefId) UnmarshalJSON

func (r *RefId) UnmarshalJSON(b []byte) error

UnmarshalJson implements the json.Unmarshaler interface.

func (*RefId) UnmarshalText

func (r *RefId) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface. It will return an error if the slice isn't of appropriate size.

func (RefId) Value

func (r RefId) Value() (driver.Value, error)

Value implements the sql/driver.Valuer interface.

type Tagger

type Tagger byte

A Tagger is a conveniece container for encoding and parsing RefId's of a specific tag.

func NewTagger

func NewTagger(tag byte) Tagger

NewTagger returns a new Tagger with tag

func (Tagger) AnyMatcher

func (t Tagger) AnyMatcher() AnyMatcher

AnyMather returns an AnyMatcher, which will match only against a RefId tagged with the same tag as the Tagger

func (Tagger) HasCorrectTag

func (t Tagger) HasCorrectTag(r RefId) bool

HasTag reports whether a RefId is tagged with the same tag as the Tagger

func (Tagger) HasTag

func (t Tagger) HasTag(r RefId, tag byte) bool

HasTag reports whether a RefId is tagged with a given tag

func (Tagger) IsTagged

func (t Tagger) IsTagged(r RefId) bool

IsTagged reports wheater a RefId is tagged at all. Note: This only checks that the RefId is tagged, not that it is tagged with the same tag as Tagger. For that functionality use Tagger.HasCorrectTag.

func (Tagger) New

func (t Tagger) New() (RefId, error)

New generates a new RefId with tag set to the tag of the Tagger

func (Tagger) Parse

func (t Tagger) Parse(s string) (RefId, error)

Parse parses a RefId, additionally enforcing that it is is tagged with the same tag as the Tagger

func (Tagger) Tag

func (t Tagger) Tag() byte

Tag returns the tag of the Tagger

Directories

Path Synopsis
cmd
reftool module

Jump to

Keyboard shortcuts

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