summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/uuid.go171
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))
}