Browse Source

新增短uuid算法

WhiteBatman 9 năm trước cách đây
mục cha
commit
b2b0ffcb8f
1 tập tin đã thay đổi với 244 bổ sung0 xóa
  1. 244 0
      uuid.go

+ 244 - 0
uuid.go

@@ -1,13 +1,19 @@
 package xorm
 
 import (
+	"bytes"
 	"crypto/md5"
 	"crypto/rand"
 	"crypto/sha1"
 	"encoding/binary"
+	"encoding/hex"
 	"fmt"
 	"hash"
+	"math"
+	"math/big"
 	"net"
+	"sort"
+	"strings"
 	"time"
 )
 
@@ -176,3 +182,241 @@ func NewV5(namespaceUUID *UUID, name []byte) *UUID {
 func NewNamespaceUUID(namespace string) *UUID {
 	return NewV5(NIL, []byte(namespace))
 }
+
+// String parse helpers.
+var (
+	urnPrefix  = []byte("urn:uuid:")
+	byteGroups = []int{8, 4, 4, 4, 12}
+)
+
+func (u *UUID) UnmarshalText(text []byte) (err error) {
+	if len(text) < 32 {
+		err = fmt.Errorf("uuid: UUID string too short: %s", text)
+		return
+	}
+
+	t := text[:]
+	braced := false
+
+	if bytes.Equal(t[:9], urnPrefix) {
+		t = t[9:]
+	} else if t[0] == '{' {
+		braced = true
+		t = t[1:]
+	}
+
+	b := u[:]
+
+	for i, byteGroup := range byteGroups {
+		if i > 0 {
+			if t[0] != '-' {
+				err = fmt.Errorf("uuid: invalid string format")
+				return
+			}
+			t = t[1:]
+		}
+
+		if len(t) < byteGroup {
+			err = fmt.Errorf("uuid: UUID string too short: %s", text)
+			return
+		}
+
+		if i == 4 && len(t) > byteGroup &&
+			((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) {
+			err = fmt.Errorf("uuid: UUID string too long: %s", text)
+			return
+		}
+
+		_, err = hex.Decode(b[:byteGroup/2], t[:byteGroup])
+		if err != nil {
+			return
+		}
+
+		t = t[byteGroup:]
+		b = b[byteGroup/2:]
+	}
+
+	return
+}
+
+// FromString returns UUID parsed from string input.
+// Input is expected in a form accepted by UnmarshalText.
+func FromString(input string) (u UUID, err error) {
+	err = u.UnmarshalText([]byte(input))
+	return
+}
+
+type StringSet struct {
+	set    map[string]bool
+	list   []string
+	sorted bool
+}
+
+func NewStringSet() *StringSet {
+	return &StringSet{make(map[string]bool), make([]string, 0), false}
+}
+
+func (set *StringSet) Add(i string) bool {
+	_, found := set.set[i]
+	set.set[i] = true
+	if !found {
+		set.sorted = false
+	}
+	return !found //False if it existed already
+}
+
+func (set *StringSet) Contains(i string) bool {
+	_, found := set.set[i]
+	return found //true if it existed already
+}
+
+func (set *StringSet) Remove(i string) {
+	set.sorted = false
+	delete(set.set, i)
+}
+
+func (set *StringSet) Len() int {
+	return len(set.set)
+}
+
+func (set *StringSet) ItemByIndex(idx int) string {
+	set.Sort()
+	return set.list[idx]
+}
+
+func (set *StringSet) Index(c string) int {
+	for i, s := range set.list {
+		if c == s {
+			return i
+		}
+	}
+	return 0
+}
+
+func (set *StringSet) Sort() {
+	if set.sorted {
+		return
+	}
+	set.list = make([]string, 0)
+	for s, _ := range set.set {
+		set.list = append(set.list, s)
+	}
+	sort.Strings(set.list)
+	set.sorted = true
+}
+
+func (set *StringSet) String() string {
+	set.Sort()
+	return strings.Join(set.list, "")
+}
+
+const (
+	DEFAULT_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+)
+
+type ShortUUID struct {
+	alphabet *StringSet
+}
+
+func NewShortUUID() *ShortUUID {
+	suid := &ShortUUID{}
+	suid.SetAlphabet(DEFAULT_ALPHABET)
+	return suid
+}
+
+func NewShortUUIDWithAlphabet(alphabet string) *ShortUUID {
+
+	suuid := &ShortUUID{}
+	if alphabet == "" {
+		alphabet = DEFAULT_ALPHABET
+	}
+	suuid.SetAlphabet(alphabet)
+	return suuid
+}
+
+func (s *ShortUUID) SetAlphabet(alphabet string) {
+	set := NewStringSet()
+	for _, a := range alphabet {
+		set.Add(string(a))
+	}
+	set.Sort()
+	s.alphabet = set
+}
+
+func (s ShortUUID) String() string {
+	return s.UUID("")
+}
+
+var (
+	NamespaceDNS, _  = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
+	NamespaceURL, _  = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
+	NamespaceOID, _  = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
+	NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
+)
+
+func (s *ShortUUID) UUID(name string) string {
+	var _uuid *UUID
+	if name == "" {
+		_uuid = NewV4()
+	} else if strings.HasPrefix(name, "http") {
+		_uuid = NewV5(&NamespaceDNS, []byte(name))
+	} else {
+		_uuid = NewV5(&NamespaceURL, []byte(name))
+	}
+
+	return s.Encode(_uuid)
+}
+
+// Encodes a UUID into a string (LSB first) according to the alphabet
+// If leftmost (MSB) bits 0, string might be shorter
+func (s *ShortUUID) Encode(uuid *UUID) string {
+	padLen := s.encodeLen(len(uuid))
+	number := uuidToInt(uuid)
+	return s.numToString(number, padLen)
+}
+
+func (s *ShortUUID) Decode(input string) (UUID, error) {
+	_uuid, err := FromString(s.stringToNum(input))
+	return _uuid, err
+}
+
+func (s *ShortUUID) encodeLen(numBytes int) int {
+	factor := math.Log(float64(25)) / math.Log(float64(s.alphabet.Len()))
+	length := math.Ceil(factor * float64(numBytes))
+	return int(length)
+}
+
+//Covert a number to a string, using the given alphabet.
+func (s *ShortUUID) numToString(number *big.Int, padToLen int) string {
+	output := ""
+	var digit *big.Int
+	for number.Uint64() > 0 {
+		number, digit = new(big.Int).DivMod(number, big.NewInt(int64(s.alphabet.Len())), new(big.Int))
+		output += s.alphabet.ItemByIndex(int(digit.Int64()))
+	}
+	if padToLen > 0 {
+		remainer := math.Max(float64(padToLen)-float64(len(output)), 0)
+		output = output + strings.Repeat(s.alphabet.ItemByIndex(0), int(remainer))
+	}
+
+	return output
+}
+
+// Convert a string to a number(based uuid string),using the given alphabet.
+func (s *ShortUUID) stringToNum(input string) string {
+	n := big.NewInt(0)
+	for i := len(input) - 1; i >= 0; i-- {
+		n.Mul(n, big.NewInt(int64(s.alphabet.Len())))
+		n.Add(n, big.NewInt(int64(s.alphabet.Index(string(input[i])))))
+	}
+
+	x := fmt.Sprintf("%x", n)
+	x = x[0:8] + "-" + x[8:12] + "-" + x[12:16] + "-" + x[16:20] + "-" + x[20:32]
+	return x
+}
+
+func uuidToInt(_uuid *UUID) *big.Int {
+	var i big.Int
+	i.SetString(strings.Replace(_uuid.String(), "-", "", 4), 16)
+	return &i
+}