diff options
Diffstat (limited to '')
-rw-r--r-- | src/uuid.go | 171 |
1 files changed, 158 insertions, 13 deletions
diff --git a/src/uuid.go b/src/uuid.go index 6c4b8f6..fa1e958 100644 --- a/src/uuid.go +++ b/src/uuid.go @@ -2,17 +2,34 @@ package uuid import ( "crypto/rand" + "encoding/binary" "encoding/hex" "errors" + "flag" "fmt" "io" "os" "strings" + "time" ) +type versionFlag byte const ( + versionFlag_v4 versionFlag = 0b01000000 + versionFlag_v7 versionFlag = 0b01110000 +) + +type actionType byte +const( + actionType_generate actionType = iota + actionType_parse +) + +const ( + variant0b10 = 0b10000000 + ByteCount = 16 dashCount = 4 encodedLength = (ByteCount * 2) + dashCount @@ -27,33 +44,85 @@ var ( ) ErrBadDashCount = errors.New("uuid: Bad count of dashes in string") ErrBadDashPosition = errors.New("uuid: Bad char in string") + + DefaultVersion = 4 + + Nil = UUID{ 0, 0, 0, 0, 0, 0, 0, 0, } + Max = UUID{ 255, 255, 255, 255, 255, 255, 255, 255, } ) type UUID [ByteCount]byte +type argsT struct{ + allArgs []string + subArgs []string + action actionType + version uint8 +} + -func NewFrom(r io.Reader) (UUID, error) { +func NewV4From(r io.Reader) (UUID, error) { var uuid UUID + _, err := io.ReadFull(r, uuid[:]) if err != nil { return UUID{}, err } - uuid[6] = (uuid[6] & 0x0f) | 0x40 // v4 - uuid[8] = (uuid[8] & 0x3f) | 0x80 // variant 10 + + uuid[6] = (uuid[6] & 0b00001111) | byte(versionFlag_v4) + uuid[8] = (uuid[8] & 0b00111111) | variant0b10 + return uuid, nil } -func New() UUID { - uuid, err := NewFrom(randomReader) +func NewV4() UUID { + uuid, err := NewV4From(randomReader) if err != nil { panic(err) } return uuid } +func NewV7From(r io.Reader, nowNanoFn func() uint64) (UUID, error) { + var uuid UUID + + now := uint64(nowNanoFn()) + binary.BigEndian.PutUint64(uuid[:8], now) + + _, err := io.ReadFull(r, uuid[8:]) + if err != nil { + return UUID{}, err + } + + uuid[6] = (uuid[6] & 0b00001111) | byte(versionFlag_v7) + uuid[8] = (uuid[8] & 0b00111111) | variant0b10 + + return uuid, nil +} + +func nowNano() uint64 { + return uint64(time.Now().UnixNano()) +} + +func NewV7() UUID { + uuid, err := NewV7From(randomReader, nowNano) + if err != nil { + panic(err) + } + return uuid +} + +func New() UUID { + if DefaultVersion == 4 { + return NewV4() + } else { + return NewV7() + } +} + func (uuid UUID) String() string { dst := [encodedLength]byte { 0, 0, 0, 0, @@ -103,16 +172,92 @@ func FromString(str string) (UUID, error) { return [ByteCount]byte(data), nil } +func usage(argv0 string, w io.Writer) { + fmt.Fprintf( + w, + ("Usage:\n" + + " %s [-v (4|7)]\n" + + " %s STRING\n"), + argv0, + argv0, + ) +} + +func actionForSubargs(subArgs []string) actionType { + if len(subArgs) == 0 { + return actionType_generate + } else { + return actionType_parse + } +} + +func getopt(allArgs []string, w io.Writer) (argsT, int) { + argv0 := allArgs[0] + argv := allArgs[1:] + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Usage = func() {} + fs.SetOutput(w) + versionNumber := fs.Uint( + "v", + 4, + "which UUID version to generate", + ) -func Main() { - if len(os.Args) < 2 { - fmt.Println(New().String()) + if fs.Parse(argv) != nil { + usage(argv0, w) + return argsT{}, 2 + } + + subArgs := fs.Args() + actionType := actionForSubargs(subArgs) + + var version uint8 + if *versionNumber == 4 || *versionNumber == 7 { + version = uint8(*versionNumber) } else { - _, err := FromString(strings.TrimSpace(os.Args[1])) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(3) - } + fmt.Fprintf(w, "Bad value for VERSION: %s.\n", *versionNumber) + usage(argv0, w) + return argsT{}, 2 + } + + return argsT{ + allArgs: allArgs, + subArgs: subArgs, + action: actionType, + version: version, + }, 0 +} + +func run(args argsT, _ io.Reader, stdout io.Writer, stderr io.Writer) int { + switch args.action { + case actionType_generate: + if args.version == 4 { + fmt.Fprintf(stdout, "%v\n", NewV4().String()) + } else if args.version == 7 { + fmt.Fprintf(stdout, "%v\n", NewV7().String()) + } + return 0 + + case actionType_parse: + _, err := FromString(strings.TrimSpace(args.subArgs[0])) + if err != nil { + fmt.Fprintln(stderr, err) + return 3 + } else { + return 0 + } + } + + return 126 +} + + + +func Main() { + args, rc := getopt(os.Args, os.Stderr) + if rc != 0 { + os.Exit(rc) } + os.Exit(run(args, os.Stdin, os.Stdout, os.Stderr)) } |