xormplus 9 years ago
parent
commit
5c20469d88
1 changed files with 174 additions and 0 deletions
  1. 174 0
      uuid.go

+ 174 - 0
uuid.go

@@ -0,0 +1,174 @@
+package xorm
+
+import (
+	"crypto/md5"
+	"crypto/rand"
+	"crypto/sha1"
+	"encoding/binary"
+	"fmt"
+	"hash"
+	"net"
+	"time"
+)
+
+// The UUID represents Universally Unique IDentifier (which is 128 bit long).
+type UUID [16]byte
+
+var (
+	// NIL is defined in RFC 4122 section 4.1.7.
+	// The nil UUID is special form of UUID that is specified to have all 128 bits set to zero.
+	NIL = &UUID{
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	}
+	// NameSpaceDNS assume name to be a fully-qualified domain name.
+	// Declared in RFC 4122 Appendix C.
+	NameSpaceDNS = &UUID{
+		0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
+		0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
+	}
+	// NameSpaceURL assume name to be a URL.
+	// Declared in RFC 4122 Appendix C.
+	NameSpaceURL = &UUID{
+		0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
+		0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
+	}
+	// NameSpaceOID assume name to be an ISO OID.
+	// Declared in RFC 4122 Appendix C.
+	NameSpaceOID = &UUID{
+		0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
+		0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
+	}
+	// NameSpaceX500 assume name to be a X.500 DN (in DER or a text output format).
+	// Declared in RFC 4122 Appendix C.
+	NameSpaceX500 = &UUID{
+		0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
+		0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
+	}
+)
+
+// Version of the UUID represents a kind of subtype specifier.
+func (u *UUID) Version() int {
+	return int(binary.BigEndian.Uint16(u[6:8]) >> 12)
+}
+
+// String returns the human readable form of the UUID.
+func (u *UUID) String() string {
+	return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
+}
+
+func (u *UUID) variantRFC4122() {
+	u[8] = (u[8] & 0x3f) | 0x80
+}
+
+// NewV3 creates a new UUID with variant 3 as described in RFC 4122.
+// Variant 3 based namespace-uuid and name and MD-5 hash calculation.
+func NewV3(namespace *UUID, name []byte) *UUID {
+	uuid := newByHash(md5.New(), namespace, name)
+	uuid[6] = (uuid[6] & 0x0f) | 0x30
+	return uuid
+}
+
+func newByHash(hash hash.Hash, namespace *UUID, name []byte) *UUID {
+	hash.Write(namespace[:])
+	hash.Write(name[:])
+
+	var uuid UUID
+	copy(uuid[:], hash.Sum(nil)[:16])
+	uuid.variantRFC4122()
+	return &uuid
+}
+
+type stamp [10]byte
+
+var (
+	mac      []byte
+	requests chan bool
+	answers  chan stamp
+)
+
+const gregorianUnix = 122192928000000000 // nanoseconds between gregorion zero and unix zero
+
+func init() {
+	mac = make([]byte, 6)
+	rand.Read(mac)
+	requests = make(chan bool)
+	answers = make(chan stamp)
+	go unique()
+	i, err := net.Interfaces()
+	if err != nil {
+		return
+	}
+	for _, d := range i {
+		if len(d.HardwareAddr) == 6 {
+			mac = d.HardwareAddr[:6]
+			return
+		}
+	}
+}
+
+// NewV1 creates a new UUID with variant 1 as described in RFC 4122.
+// Variant 1 is based on hosts MAC address and actual timestamp (as count of 100-nanosecond intervals since
+// 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
+func NewV1() *UUID {
+	var uuid UUID
+	requests <- true
+	s := <-answers
+	copy(uuid[:4], s[4:])
+	copy(uuid[4:6], s[2:4])
+	copy(uuid[6:8], s[:2])
+	uuid[6] = (uuid[6] & 0x0f) | 0x10
+	copy(uuid[8:10], s[8:])
+	copy(uuid[10:], mac)
+	uuid.variantRFC4122()
+	return &uuid
+}
+
+func unique() {
+	var (
+		lastNanoTicks uint64
+		clockSequence [2]byte
+	)
+	rand.Read(clockSequence[:])
+
+	for range requests {
+		var s stamp
+		nanoTicks := uint64((time.Now().UTC().UnixNano() / 100) + gregorianUnix)
+		if nanoTicks < lastNanoTicks {
+			lastNanoTicks = nanoTicks
+			rand.Read(clockSequence[:])
+		} else if nanoTicks == lastNanoTicks {
+			lastNanoTicks = nanoTicks + 1
+		} else {
+			lastNanoTicks = nanoTicks
+		}
+		binary.BigEndian.PutUint64(s[:], lastNanoTicks)
+		copy(s[8:], clockSequence[:])
+		answers <- s
+	}
+}
+
+// NewV4 creates a new UUID with variant 4 as described in RFC 4122. Variant 4 based on pure random bytes.
+func NewV4() *UUID {
+	buf := make([]byte, 16)
+	rand.Read(buf)
+	buf[6] = (buf[6] & 0x0f) | 0x40
+	var uuid UUID
+	copy(uuid[:], buf[:])
+	uuid.variantRFC4122()
+	return &uuid
+}
+
+// NewV5 creates a new UUID with variant 5 as described in RFC 4122.
+// Variant 5 based namespace-uuid and name and SHA-1 hash calculation.
+func NewV5(namespaceUUID *UUID, name []byte) *UUID {
+	uuid := newByHash(sha1.New(), namespaceUUID, name)
+	uuid[6] = (uuid[6] & 0x0f) | 0x50
+	return uuid
+}
+
+// NewNamespaceUUID creates a namespace UUID by using the namespace name in the NIL name space.
+// This is a different approach as the 4 "standard" namespace UUIDs which are timebased UUIDs (V1).
+func NewNamespaceUUID(namespace string) *UUID {
+	return NewV5(NIL, []byte(namespace))
+}