|
@@ -1,13 +1,19 @@
|
|
|
package xorm
|
|
package xorm
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "bytes"
|
|
|
"crypto/md5"
|
|
"crypto/md5"
|
|
|
"crypto/rand"
|
|
"crypto/rand"
|
|
|
"crypto/sha1"
|
|
"crypto/sha1"
|
|
|
"encoding/binary"
|
|
"encoding/binary"
|
|
|
|
|
+ "encoding/hex"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"hash"
|
|
"hash"
|
|
|
|
|
+ "math"
|
|
|
|
|
+ "math/big"
|
|
|
"net"
|
|
"net"
|
|
|
|
|
+ "sort"
|
|
|
|
|
+ "strings"
|
|
|
"time"
|
|
"time"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
@@ -176,3 +182,241 @@ func NewV5(namespaceUUID *UUID, name []byte) *UUID {
|
|
|
func NewNamespaceUUID(namespace string) *UUID {
|
|
func NewNamespaceUUID(namespace string) *UUID {
|
|
|
return NewV5(NIL, []byte(namespace))
|
|
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
|
|
|
|
|
+}
|