package uuid import ( "crypto/rand" "encoding/binary" "encoding/hex" "flag" "fmt" "io" "os" "strings" "time" gt "gotext" ) 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 ) var ( dashIndexes = []int{ 8, 13, 18, 23 } randomReader = rand.Reader ErrBadLength = gt.Error(gt.Gettext( "uuid: str isn't of the correct length", )) ErrBadDashCount = gt.Error(gt.Gettext( "uuid: Bad count of dashes in string", )) ErrBadDashPosition = gt.Error(gt.Gettext( "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 NewV4From(r io.Reader) (UUID, error) { var uuid UUID _, err := io.ReadFull(r, uuid[:]) if err != nil { return UUID{}, err } uuid[6] = (uuid[6] & 0b00001111) | byte(versionFlag_v4) uuid[8] = (uuid[8] & 0b00111111) | variant0b10 return uuid, nil } 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, 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } hex.Encode(dst[ 0:8], uuid[0:4]) hex.Encode(dst[ 9:13], uuid[4:6]) hex.Encode(dst[14:18], uuid[6:8]) hex.Encode(dst[19:23], uuid[8:10]) hex.Encode(dst[24:36], uuid[10:]) return string(dst[:]) } func FromString(str string) (UUID, error) { if len(str) != encodedLength { return UUID{}, ErrBadLength } if strings.Count(str, "-") != dashCount { return UUID{}, ErrBadDashCount } for _, idx := range dashIndexes { if str[idx] != '-' { return UUID{}, ErrBadDashPosition } } hexstr := strings.Join(strings.Split(str, "-"), "") data, err := hex.DecodeString(hexstr) if err != nil { return UUID{}, err } 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", ) 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 { 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)) }