blob: 3aee25d1c5b4d84d18c00c738134c4b8ac14dc27 [file] [log] [blame]
//Copyright 2017 Huawei Technologies Co., Ltd
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
package uuid
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"net"
"sync"
"time"
)
// UUID layout variants.
const (
LAYOUT_NCS = iota
LAYOUT_RFC4122
LAYOUT_MICROSOFT
LAYOUT_FUTURE
)
// Difference in 100-nanosecond intervals between
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
const EPOCH_START_IN_NS = 122192928000000000
const DASH byte = '-'
var (
mux sync.Mutex
once sync.Once
epochFunc = unixTimeFunc
random uint16
last uint64
hardwareAddr [6]byte
)
func initRandom() {
buf := make([]byte, 2)
doSafeRandom(buf)
random = binary.BigEndian.Uint16(buf)
}
func initHardwareAddr() {
interfaces, err := net.Interfaces()
if err == nil {
for _, iface := range interfaces {
if len(iface.HardwareAddr) >= 6 {
copy(hardwareAddr[:], iface.HardwareAddr)
return
}
}
}
// Initialize hardwareAddr randomly in case
// of real network interfaces absence
doSafeRandom(hardwareAddr[:])
// Set multicast bit as recommended in RFC 4122
hardwareAddr[0] |= 0x01
}
func initialize() {
initRandom()
initHardwareAddr()
}
func doSafeRandom(dest []byte) {
if _, err := rand.Read(dest); err != nil {
panic(err)
}
}
func unixTimeFunc() uint64 {
return EPOCH_START_IN_NS + uint64(time.Now().UnixNano()/100)
}
type UUID [16]byte
func (u UUID) Bytes() []byte {
return u[:]
}
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
func (u UUID) String() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = DASH
hex.Encode(buf[9:13], u[4:6])
buf[13] = DASH
hex.Encode(buf[14:18], u[6:8])
buf[18] = DASH
hex.Encode(buf[19:23], u[8:10])
buf[23] = DASH
hex.Encode(buf[24:], u[10:])
return string(buf)
}
func (u *UUID) SetVersion(v byte) {
u[6] = (u[6] & 0x0f) | (v << 4)
}
func (u UUID) Version() uint {
return uint(u[6] >> 4)
}
func (u *UUID) SetLayout() {
u[8] = (u[8] & 0xbf) | 0x80 // LAYOUT_RFC4122
}
func (u UUID) Layout() uint {
switch {
case (u[8] & 0x80) == 0x00:
return LAYOUT_NCS
case (u[8]&0xc0)|0x80 == 0x80:
return LAYOUT_RFC4122
case (u[8]&0xe0)|0xc0 == 0xc0:
return LAYOUT_MICROSOFT
}
return LAYOUT_FUTURE
}
func doInit() (uint64, uint16, []byte) {
once.Do(initialize)
mux.Lock()
now := epochFunc()
if now <= last {
random++
}
last = now
mux.Unlock()
return now, random, hardwareAddr[:]
}
func NewV1() UUID {
u := UUID{}
timeNow, clockSeq, hardwareAddr := doInit()
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
binary.BigEndian.PutUint16(u[8:], clockSeq)
copy(u[10:], hardwareAddr)
u.SetVersion(1)
u.SetLayout()
return u
}