├── doc.go ├── internal ├── dcesecurity │ ├── domain.go │ └── dcesecurity.go ├── namebased │ ├── namebased.go │ ├── md5 │ │ └── md5.go │ └── sha1 │ │ └── sha1.go ├── random │ └── random.go ├── version.go ├── layout.go └── timebased │ └── timebased.go ├── domain.go ├── namespace.go ├── style.go ├── layout.go ├── README.md ├── parse.go ├── version.go ├── parse_test.go ├── uuid.go └── LICENSE /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Package uuid implements UUID described in RFC 4122. 4 | 5 | */ 6 | package uuid 7 | -------------------------------------------------------------------------------- /internal/dcesecurity/domain.go: -------------------------------------------------------------------------------- 1 | package dcesecurity 2 | 3 | // Domain represents the identifier for a local domain. 4 | type Domain byte 5 | 6 | const ( 7 | // User represents POSIX UID domain. 8 | User Domain = iota + 1 9 | // Group represents POSIX GID domain. 10 | Group 11 | ) 12 | -------------------------------------------------------------------------------- /internal/namebased/namebased.go: -------------------------------------------------------------------------------- 1 | package namebased 2 | 3 | // Standard Namespaces. 4 | const ( 5 | NamespaceDNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8" 6 | NamespaceURL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8" 7 | NamespaceOID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8" 8 | NamespaceX500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8" 9 | ) 10 | -------------------------------------------------------------------------------- /domain.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "github.com/golang-plus/uuid/internal/dcesecurity" 5 | ) 6 | 7 | // Domain represents the identifier for a local domain. 8 | type Domain byte 9 | 10 | const ( 11 | // DomainUser represents POSIX UID domain. 12 | DomainUser = Domain(dcesecurity.User) 13 | // DomainGroup represents POSIX GID domain. 14 | DomainGroup = Domain(dcesecurity.Group) 15 | ) 16 | -------------------------------------------------------------------------------- /namespace.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "github.com/golang-plus/uuid/internal/namebased" 5 | ) 6 | 7 | // Standard Namespaces. 8 | const ( 9 | NamespaceDNS = namebased.NamespaceDNS // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" 10 | NamespaceURL = namebased.NamespaceURL // "6ba7b811-9dad-11d1-80b4-00c04fd430c8" 11 | NamespaceOID = namebased.NamespaceOID // "6ba7b812-9dad-11d1-80b4-00c04fd430c8" 12 | NamespaceX500 = namebased.NamespaceX500 // "6ba7b814-9dad-11d1-80b4-00c04fd430c8" 13 | ) 14 | -------------------------------------------------------------------------------- /internal/random/random.go: -------------------------------------------------------------------------------- 1 | package random 2 | 3 | import ( 4 | "crypto/rand" 5 | 6 | "github.com/golang-plus/uuid/internal" 7 | 8 | "github.com/golang-plus/errors" 9 | ) 10 | 11 | // NewUUID returns a new randomly uuid. 12 | func NewUUID() ([]byte, error) { 13 | uuid := make([]byte, 16) 14 | n, err := rand.Read(uuid[:]) 15 | if err != nil { 16 | return nil, errors.Wrap(err, "could not generate random bytes") 17 | } 18 | if n != len(uuid) { 19 | return nil, errors.New("could not generate random bytes with 16 length") 20 | } 21 | 22 | // set version(v4) 23 | internal.SetVersion(uuid, internal.VersionRandom) 24 | // set layout(RFC4122) 25 | internal.SetLayout(uuid, internal.LayoutRFC4122) 26 | 27 | return uuid, nil 28 | } 29 | -------------------------------------------------------------------------------- /style.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | // Style represents the style of UUID string. 4 | type Style byte 5 | 6 | const ( 7 | // StyleStandard represents the standard style of UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12, length: 36). 8 | StyleStandard Style = iota + 1 9 | // StyleWithoutDash represents the style without dash of UUID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (length: 32). 10 | StyleWithoutDash 11 | ) 12 | 13 | // String returns English description of style. 14 | func (s Style) String() string { 15 | switch s { 16 | case StyleStandard: 17 | return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)" 18 | case StyleWithoutDash: 19 | return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 20 | default: 21 | return "Unknown" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /internal/namebased/md5/md5.go: -------------------------------------------------------------------------------- 1 | package md5 2 | 3 | import ( 4 | "crypto/md5" 5 | 6 | "github.com/golang-plus/uuid/internal" 7 | 8 | "github.com/golang-plus/errors" 9 | ) 10 | 11 | // NewUUID returns a new name-based uses SHA-1 hashing uuid. 12 | func NewUUID(namespace, name string) ([]byte, error) { 13 | hash := md5.New() 14 | _, err := hash.Write([]byte(namespace)) 15 | if err != nil { 16 | return nil, errors.Wrapf(err, "could not compute hash value for namespace %q", namespace) 17 | } 18 | _, err = hash.Write([]byte(name)) 19 | if err != nil { 20 | return nil, errors.Wrapf(err, "could not compute hash value for name %q", name) 21 | } 22 | 23 | sum := hash.Sum(nil) 24 | 25 | uuid := make([]byte, 16) 26 | copy(uuid, sum) 27 | 28 | // set version(v3) 29 | internal.SetVersion(uuid, internal.VersionNameBasedMD5) 30 | // set layout(RFC4122) 31 | internal.SetLayout(uuid, internal.LayoutRFC4122) 32 | 33 | return uuid, nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/namebased/sha1/sha1.go: -------------------------------------------------------------------------------- 1 | package sha1 2 | 3 | import ( 4 | "crypto/sha1" 5 | 6 | "github.com/golang-plus/uuid/internal" 7 | 8 | "github.com/golang-plus/errors" 9 | ) 10 | 11 | // NewUUID returns a new name-based uses SHA-1 hashing uuid. 12 | func NewUUID(namespace, name string) ([]byte, error) { 13 | hash := sha1.New() 14 | _, err := hash.Write([]byte(namespace)) 15 | if err != nil { 16 | return nil, errors.Wrapf(err, "could not compute hash value for namespace %q", namespace) 17 | } 18 | _, err = hash.Write([]byte(name)) 19 | if err != nil { 20 | return nil, errors.Wrapf(err, "could not compute hash value for name %q", name) 21 | } 22 | 23 | sum := hash.Sum(nil) 24 | 25 | uuid := make([]byte, 16) 26 | copy(uuid, sum) 27 | 28 | // set version(v5) 29 | internal.SetVersion(uuid, internal.VersionNameBasedSHA1) 30 | // set layout(RFC4122) 31 | internal.SetLayout(uuid, internal.LayoutRFC4122) 32 | 33 | return uuid, nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/dcesecurity/dcesecurity.go: -------------------------------------------------------------------------------- 1 | package dcesecurity 2 | 3 | import ( 4 | "encoding/binary" 5 | "os" 6 | 7 | "github.com/golang-plus/uuid/internal" 8 | "github.com/golang-plus/uuid/internal/timebased" 9 | 10 | "github.com/golang-plus/errors" 11 | ) 12 | 13 | // NewUUID Generate returns a new DCE security uuid. 14 | func NewUUID(domain Domain) ([]byte, error) { 15 | uuid, err := timebased.NewUUID() 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | switch domain { 21 | case User: 22 | uid := os.Getuid() 23 | binary.BigEndian.PutUint32(uuid[0:], uint32(uid)) // network byte order 24 | case Group: 25 | gid := os.Getgid() 26 | binary.BigEndian.PutUint32(uuid[0:], uint32(gid)) // network byte order 27 | default: 28 | return nil, errors.New("domain is invalid") 29 | } 30 | 31 | // set version(v2) 32 | internal.SetVersion(uuid, internal.VersionDCESecurity) 33 | // set layout(RFC4122) 34 | internal.SetLayout(uuid, internal.LayoutRFC4122) 35 | 36 | return uuid, nil 37 | } 38 | -------------------------------------------------------------------------------- /layout.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "github.com/golang-plus/uuid/internal" 5 | ) 6 | 7 | // Layout represents the layout of UUID. See page 5 in RFC 4122. 8 | type Layout byte 9 | 10 | const ( 11 | // LayoutInvalid represents invalid layout. 12 | LayoutInvalid = Layout(internal.LayoutInvalid) 13 | // LayoutNCS represents the layout: Reserved, NCS backward compatibility. 14 | LayoutNCS = Layout(internal.LayoutNCS) 15 | // LayoutRFC4122 represents the layout: The variant specified in RFC 4122. 16 | LayoutRFC4122 = Layout(internal.LayoutRFC4122) 17 | // LayoutMicrosoft represents the layout: Reserved, Microsoft Corporation backward compatibility. 18 | LayoutMicrosoft = Layout(internal.LayoutMicrosoft) 19 | // LayoutFuture represents the layout: Reserved for future definition. 20 | LayoutFuture = Layout(internal.LayoutFuture) 21 | ) 22 | 23 | // String returns English description of layout. 24 | func (l Layout) String() string { 25 | switch l { 26 | case LayoutNCS: 27 | return "Reserved For NCS" 28 | case LayoutRFC4122: 29 | return "RFC 4122" 30 | case LayoutMicrosoft: 31 | return "Reserved For Microsoft" 32 | case LayoutFuture: 33 | return "Reserved For Future" 34 | default: 35 | return "Invalid" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uuid 2 | 3 | Package **uuid** implements UUID [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt). 4 | 5 | ## Usage 6 | 7 | ### Generating 8 | 9 | #### Time-Based (Version 1) 10 | 11 | uuid.NewTimeBased() (uuid.UUID, error) 12 | uuid.NewV1() (uuid.UUID, error) 13 | 14 | #### DCE Security (Version 2) 15 | 16 | uuid.NewDCESecurity(uuid.Domain) (uuid.UUID, error) 17 | uuid.NewV2(uuid.Domain) (uuid.UUID, error) 18 | 19 | #### Name-Based uses MD5 hashing (Version 3) 20 | 21 | uuid.NewNameBasedMD5(namespace, name string) (uuid.UUID, error) 22 | uuid.NewV3(namespace, name string) (uuid.UUID, error) 23 | 24 | #### Random (Version 4) 25 | 26 | uuid.NewRandom() (uuid.UUID, error) 27 | uuid.NewV4() (uuid.UUID, error) 28 | 29 | #### Name-Based uses SHA-1 hashing (Version 5) 30 | 31 | uuid.NewNameBasedSHA1(namespace, name string) (uuid.UUID, error) 32 | uuid.NewV5(namespace, name string) (uuid.UUID, error) 33 | 34 | ### Styles 35 | 36 | * Standard: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12, length: 36) 37 | * Without Dash: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (length: 32) 38 | 39 | ### Formatting & Parsing 40 | 41 | uuid.UUID.String() string // format to standard style 42 | uuid.UUID.Format(uuid.Style) string // format to uuid.StyleStandard or uuid.StyleWithoutDash 43 | 44 | uuid.Parse(string) (uuid.UUID, error) // parse from UUID string 45 | 46 | -------------------------------------------------------------------------------- /internal/version.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // Version represents the version of UUID. See page 7 in RFC 4122. 4 | type Version byte 5 | 6 | // Version List 7 | const ( 8 | VersionUnknown Version = iota // Unknwon 9 | VersionTimeBased // V1: The time-based version 10 | VersionDCESecurity // V2: The DCE security version, with embedded POSIX UIDs 11 | VersionNameBasedMD5 // V3: The name-based version that uses MD5 hashing 12 | VersionRandom // V4: The randomly or pseudo-randomly generated version 13 | VersionNameBasedSHA1 // V5: The name-based version that uses SHA-1 hashing 14 | ) 15 | 16 | // SetVersion sets the version for uuid. 17 | // This is intended to be called from the New function in packages that implement uuid generating functions. 18 | func SetVersion(uuid []byte, version Version) { 19 | switch version { 20 | case VersionTimeBased: 21 | uuid[6] = (uuid[6] | 0x10) & 0x1f 22 | case VersionDCESecurity: 23 | uuid[6] = (uuid[6] | 0x20) & 0x2f 24 | case VersionNameBasedMD5: 25 | uuid[6] = (uuid[6] | 0x30) & 0x3f 26 | case VersionRandom: 27 | uuid[6] = (uuid[6] | 0x40) & 0x4f 28 | case VersionNameBasedSHA1: 29 | uuid[6] = (uuid[6] | 0x50) & 0x5f 30 | default: 31 | panic("version is unknown") 32 | } 33 | } 34 | 35 | // GetVersion gets the version of uuid. 36 | func GetVersion(uuid []byte) Version { 37 | ver := uuid[6] >> 4 38 | if ver > 0 && ver < 6 { 39 | return Version(ver) 40 | } 41 | 42 | return VersionUnknown 43 | } 44 | -------------------------------------------------------------------------------- /parse.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "encoding/hex" 5 | 6 | "github.com/golang-plus/errors" 7 | ) 8 | 9 | // Parse parses the UUID string. 10 | func Parse(str string) (UUID, error) { 11 | length := len(str) 12 | buffer := make([]byte, 16) 13 | indexes := []int{} 14 | switch length { 15 | case 36: 16 | if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' { 17 | return Nil, errors.Newf("format of UUID string %q is invalid, it should be xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)", str) 18 | } 19 | indexes = []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} 20 | case 32: 21 | indexes = []int{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30} 22 | default: 23 | return Nil, errors.Newf("length of UUID string %q is invalid, it should be 36 (standard) or 32 (without dash)", str) 24 | } 25 | 26 | var err error 27 | for i, v := range indexes { 28 | if c, e := hex.DecodeString(str[v : v+2]); e == nil { 29 | buffer[i] = c[0] 30 | } else { 31 | err = e 32 | break 33 | } 34 | } 35 | 36 | if err != nil { 37 | return Nil, errors.Wrapf(err, "UUID string %q is invalid", str) 38 | } 39 | 40 | uuid := UUID{} 41 | copy(uuid[:], buffer) 42 | 43 | if !uuid.Equal(Nil) { 44 | if uuid.Layout() == LayoutInvalid { 45 | return Nil, errors.Newf("layout of UUID %q is invalid", str) 46 | } 47 | 48 | if uuid.Version() == VersionUnknown { 49 | return Nil, errors.Newf("version of UUID %q is unknown", str) 50 | } 51 | } 52 | 53 | return uuid, nil 54 | } 55 | 56 | // IsValid reports whether the passed string is a valid uuid string. 57 | func IsValid(uuid string) bool { 58 | _, err := Parse(uuid) 59 | return err == nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/layout.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // Layout represents the layout of UUID. See page 5 in RFC 4122. 4 | type Layout byte 5 | 6 | const ( 7 | // LayoutInvalid represents invalid layout. 8 | LayoutInvalid Layout = iota 9 | // LayoutNCS represents the NCS layout: Reserved, NCS backward compatibility (Values: 0x00-0x07). 10 | LayoutNCS 11 | // LayoutRFC4122 represents the RFC4122 layout: The variant specified in RFC 4122 (Values: 0x08-0x0b). 12 | LayoutRFC4122 13 | // LayoutMicrosoft represents the Microsoft layout: Reserved, Microsoft Corporation backward compatibility (Values: 0x0c-0x0d). 14 | LayoutMicrosoft 15 | // LayoutFuture represents the Future layout: Reserved for future definition. (Values: 0x0e-0x0f). 16 | LayoutFuture 17 | ) 18 | 19 | // SetLayout sets the layout for uuid. 20 | // This is intended to be called from the New function in packages that implement uuid generating functions. 21 | func SetLayout(uuid []byte, layout Layout) { 22 | switch layout { 23 | case LayoutNCS: 24 | uuid[8] = (uuid[8] | 0x00) & 0x0f // Msb0=0 25 | case LayoutRFC4122: 26 | uuid[8] = (uuid[8] | 0x80) & 0x8f // Msb0=1, Msb1=0 27 | case LayoutMicrosoft: 28 | uuid[8] = (uuid[8] | 0xc0) & 0xcf // Msb0=1, Msb1=1, Msb2=0 29 | case LayoutFuture: 30 | uuid[8] = (uuid[8] | 0xe0) & 0xef // Msb0=1, Msb1=1, Msb2=1 31 | default: 32 | panic("layout is invalid") 33 | } 34 | } 35 | 36 | // GetLayout returns layout of uuid. 37 | func GetLayout(uuid []byte) Layout { 38 | switch { 39 | case (uuid[8] & 0x80) == 0x00: 40 | return LayoutNCS 41 | case (uuid[8] & 0xc0) == 0x80: 42 | return LayoutRFC4122 43 | case (uuid[8] & 0xe0) == 0xc0: 44 | return LayoutMicrosoft 45 | case (uuid[8] & 0xe0) == 0xe0: 46 | return LayoutFuture 47 | } 48 | 49 | return LayoutInvalid 50 | } 51 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "github.com/golang-plus/uuid/internal" 5 | ) 6 | 7 | // Version represents the version of UUID. See page 7 in RFC 4122. 8 | type Version byte 9 | 10 | // Versions. 11 | const ( 12 | // VersionUnknown represents unknown version. 13 | VersionUnknown = Version(internal.VersionUnknown) 14 | // VersionTimeBased represents the time-based version (Version 1). 15 | VersionTimeBased = Version(internal.VersionTimeBased) 16 | // VersionDCESecurity represents the DCE security version, with embedded POSIX UIDs (Version 2). 17 | VersionDCESecurity = Version(internal.VersionDCESecurity) 18 | // VersionNameBasedMD5 represents the name-based version that uses MD5 hashing (Version 3). 19 | VersionNameBasedMD5 = Version(internal.VersionNameBasedMD5) 20 | // VersionRandom represents the randomly or pseudo-randomly generated version (Version 4). 21 | VersionRandom = Version(internal.VersionRandom) 22 | // VersionNameBasedSHA1 represents the name-based version that uses SHA-1 hashing (Version 5). 23 | VersionNameBasedSHA1 = Version(internal.VersionNameBasedSHA1) 24 | ) 25 | 26 | // Short names of versions. 27 | const ( 28 | V1 = VersionTimeBased 29 | V2 = VersionDCESecurity 30 | V3 = VersionNameBasedMD5 31 | V4 = VersionRandom 32 | V5 = VersionNameBasedSHA1 33 | ) 34 | 35 | // String returns English description of Version. 36 | func (v Version) String() string { 37 | switch v { 38 | case VersionTimeBased: 39 | return "Version 1: Time-Based" 40 | case VersionDCESecurity: 41 | return "Version 2: DCE Security With Embedded POSIX UIDs" 42 | case VersionNameBasedMD5: 43 | return "Version 3: Name-Based (MD5)" 44 | case VersionRandom: 45 | return "Version 4: Randomly OR Pseudo-Randomly Generated" 46 | case VersionNameBasedSHA1: 47 | return "Version 5: Name-Based (SHA-1)" 48 | default: 49 | return "Version: Unknwon" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /parse_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | testing2 "github.com/golang-plus/testing" 8 | ) 9 | 10 | func TestParse(t *testing.T) { 11 | uuids := []string{ 12 | "00000000-0000-0000-0000-000000000000", // nil 13 | "945f6800-b463-11e4-854a-0002a5d5c51b", // v1: time based 14 | "000001f5-b465-21e4-8ffe-98fe945016ea", // v2: dce security 15 | "6fa459ea-ee8a-3ca4-894e-db77e160355e", // v3: name based md5 16 | "0f8fad5b-d9cb-469f-a165-70867728950e", // v4: random 17 | "886313e1-3b8a-5372-9b90-0c9aee199e5d", // v5: name based sha1 18 | } 19 | for i, v := range uuids { 20 | u, err := Parse(v) 21 | if err != nil { 22 | t.Fatalf("%s: %s", v, err) 23 | } 24 | 25 | switch i { 26 | case 0: 27 | testing2.ExpectEqual(t, u, Nil) 28 | case 1: 29 | testing2.ExpectEqual(t, u.Version(), VersionTimeBased) 30 | case 2: 31 | testing2.ExpectEqual(t, u.Version(), VersionDCESecurity) 32 | case 3: 33 | testing2.ExpectEqual(t, u.Version(), VersionNameBasedMD5) 34 | case 4: 35 | testing2.ExpectEqual(t, u.Version(), VersionRandom) 36 | case 5: 37 | testing2.ExpectEqual(t, u.Version(), VersionNameBasedSHA1) 38 | } 39 | 40 | fmt.Printf("%s, %s, %s\n", u.String(), u.Version(), u.Layout()) 41 | } 42 | } 43 | 44 | func TestParseErrors(t *testing.T) { 45 | uuids := map[string]bool{ 46 | "00000000-0000-0000-0000-000000000000": true, // "is an correct pattern", 47 | "00000000-00000000-0000-0000-00000000": false, // "is an incorrect pattern", 48 | "945f6800-b463-11e4-854a-0002a5d5c51b2a": false, // "is too long", 49 | "000001f5-b465-21h4-8ffe-98fe945016ea": false, // "contains an invalid character", 50 | "0f8fad5bd9cb469fa1657086772895": false, // "is too short", 51 | } 52 | 53 | i := 0 54 | for k, v := range uuids { 55 | _, err := Parse(k) 56 | testing2.ExpectEqualL(t, err == nil, v, i) 57 | i++ 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /internal/timebased/timebased.go: -------------------------------------------------------------------------------- 1 | package timebased 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "net" 7 | "sync" 8 | "time" 9 | 10 | "github.com/golang-plus/uuid/internal" 11 | 12 | "github.com/golang-plus/errors" 13 | ) 14 | 15 | const ( 16 | // Intervals bewteen 1/1/1970 and 15/10/1582 (Julain days of 1 Jan 1970 - Julain days of 15 Oct 1582) * 100-Nanoseconds Per Day 17 | intervals = (2440587 - 2299160) * 86400 * 10000000 18 | ) 19 | 20 | var ( 21 | lastGenerated time.Time // last generated time 22 | clockSequence uint16 // clock sequence for same tick 23 | nodeID []byte // node id (MAC Address) 24 | locker sync.Mutex // global lock 25 | ) 26 | 27 | // NewUUID returns a new time-based uuid. 28 | func NewUUID() ([]byte, error) { 29 | // Get and release a global lock 30 | locker.Lock() 31 | defer locker.Unlock() 32 | 33 | uuid := make([]byte, 16) 34 | 35 | // get timestamp 36 | now := time.Now().UTC() 37 | timestamp := uint64(now.UnixNano()/100) + intervals // get timestamp 38 | if !now.After(lastGenerated) { 39 | clockSequence++ // last generated time known, then just increment clock sequence 40 | } else { 41 | b := make([]byte, 2) 42 | _, err := rand.Read(b) 43 | if err != nil { 44 | return nil, errors.Wrap(err, "could not generate clock sequence") 45 | } 46 | clockSequence = uint16(int(b[0])<<8 | int(b[1])) // set to a random value (network byte order) 47 | } 48 | 49 | lastGenerated = now // remember the last generated time 50 | 51 | timeLow := uint32(timestamp & 0xffffffff) 52 | timeMiddle := uint16((timestamp >> 32) & 0xffff) 53 | timeHigh := uint16((timestamp >> 48) & 0xfff) 54 | 55 | // network byte order(BigEndian) 56 | binary.BigEndian.PutUint32(uuid[0:], timeLow) 57 | binary.BigEndian.PutUint16(uuid[4:], timeMiddle) 58 | binary.BigEndian.PutUint16(uuid[6:], timeHigh) 59 | binary.BigEndian.PutUint16(uuid[8:], clockSequence) 60 | 61 | // get node id(mac address) 62 | if nodeID == nil { 63 | interfaces, err := net.Interfaces() 64 | if err != nil { 65 | return nil, errors.Wrap(err, "could not get network interfaces") 66 | } 67 | 68 | for _, i := range interfaces { 69 | if len(i.HardwareAddr) >= 6 { 70 | nodeID = make([]byte, 6) 71 | copy(nodeID, i.HardwareAddr) 72 | break 73 | } 74 | } 75 | 76 | if nodeID == nil { 77 | nodeID = make([]byte, 6) 78 | _, err := rand.Read(nodeID) 79 | if err != nil { 80 | return nil, errors.Wrap(err, "could not generate node id") 81 | } 82 | } 83 | } 84 | 85 | copy(uuid[10:], nodeID) 86 | 87 | // set version(v1) 88 | internal.SetVersion(uuid, internal.VersionTimeBased) 89 | // set layout(RFC4122) 90 | internal.SetLayout(uuid, internal.LayoutRFC4122) 91 | 92 | return uuid, nil 93 | } 94 | -------------------------------------------------------------------------------- /uuid.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/golang-plus/uuid/internal" 8 | "github.com/golang-plus/uuid/internal/dcesecurity" 9 | "github.com/golang-plus/uuid/internal/namebased/md5" 10 | "github.com/golang-plus/uuid/internal/namebased/sha1" 11 | "github.com/golang-plus/uuid/internal/random" 12 | "github.com/golang-plus/uuid/internal/timebased" 13 | ) 14 | 15 | // UUID respresents an UUID type compliant with specification in RFC 4122. 16 | type UUID [16]byte 17 | 18 | // Layout returns layout of UUID. 19 | func (u UUID) Layout() Layout { 20 | return Layout(internal.GetLayout(u[:])) 21 | } 22 | 23 | // Version returns version of UUID. 24 | func (u UUID) Version() Version { 25 | return Version(internal.GetVersion(u[:])) 26 | } 27 | 28 | // Equal returns true if current uuid equal to passed uuid. 29 | func (u UUID) Equal(another UUID) bool { 30 | return bytes.EqualFold(u[:], another[:]) 31 | } 32 | 33 | // Format returns the formatted string of UUID. 34 | func (u UUID) Format(style Style) string { 35 | switch style { 36 | case StyleWithoutDash: 37 | return fmt.Sprintf("%x", u[:]) 38 | //case StyleStandard: 39 | default: 40 | return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", u[:4], u[4:6], u[6:8], u[8:10], u[10:]) 41 | } 42 | } 43 | 44 | // String returns the string of UUID with standard style(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 8-4-4-4-12). 45 | func (u UUID) String() string { 46 | return u.Format(StyleStandard) 47 | } 48 | 49 | var ( 50 | // Nil represents the Nil UUID (00000000-0000-0000-0000-000000000000). 51 | Nil = UUID{} 52 | ) 53 | 54 | // NewTimeBased returns a new time based UUID (version 1). 55 | func NewTimeBased() (UUID, error) { 56 | u, err := timebased.NewUUID() 57 | if err != nil { 58 | return Nil, err 59 | } 60 | 61 | uuid := UUID{} 62 | copy(uuid[:], u) 63 | 64 | return uuid, nil 65 | } 66 | 67 | // NewV1 is short of NewTimeBased. 68 | func NewV1() (UUID, error) { 69 | return NewTimeBased() 70 | } 71 | 72 | // NewDCESecurity returns a new DCE security UUID (version 2). 73 | func NewDCESecurity(domain Domain) (UUID, error) { 74 | u, err := dcesecurity.NewUUID(dcesecurity.Domain(domain)) 75 | if err != nil { 76 | return Nil, err 77 | } 78 | 79 | uuid := UUID{} 80 | copy(uuid[:], u) 81 | 82 | return uuid, nil 83 | } 84 | 85 | // NewV2 is short of NewDCESecurity. 86 | func NewV2(domain Domain) (UUID, error) { 87 | return NewDCESecurity(domain) 88 | } 89 | 90 | // NewNameBasedMD5 returns a new name based UUID with MD5 hash (version 3). 91 | func NewNameBasedMD5(namespace, name string) (UUID, error) { 92 | u, err := md5.NewUUID(namespace, name) 93 | if err != nil { 94 | return Nil, err 95 | } 96 | 97 | uuid := UUID{} 98 | copy(uuid[:], u) 99 | 100 | return uuid, nil 101 | } 102 | 103 | // NewV3 is short of NewNameBasedMD5. 104 | func NewV3(namespace, name string) (UUID, error) { 105 | return NewNameBasedMD5(namespace, name) 106 | } 107 | 108 | // NewRandom returns a new random UUID (version 4). 109 | func NewRandom() (UUID, error) { 110 | u, err := random.NewUUID() 111 | if err != nil { 112 | return Nil, err 113 | } 114 | 115 | uuid := UUID{} 116 | copy(uuid[:], u) 117 | 118 | return uuid, nil 119 | } 120 | 121 | // NewV4 is short of NewRandom. 122 | func NewV4() (UUID, error) { 123 | return NewRandom() 124 | } 125 | 126 | // New is short of NewRandom. 127 | func New() (UUID, error) { 128 | return NewRandom() 129 | } 130 | 131 | // NewNameBasedSHA1 returns a new name based UUID with SHA1 hash (version 5). 132 | func NewNameBasedSHA1(namespace, name string) (UUID, error) { 133 | u, err := sha1.NewUUID(namespace, name) 134 | if err != nil { 135 | return Nil, err 136 | } 137 | 138 | uuid := UUID{} 139 | copy(uuid[:], u) 140 | 141 | return uuid, nil 142 | } 143 | 144 | // NewV5 is short of NewNameBasedSHA1. 145 | func NewV5(namespace, name string) (UUID, error) { 146 | return NewNameBasedSHA1(namespace, name) 147 | } 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Wayne Ho. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------