You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

134 lines
3.1 KiB

// Copyright 2015 Red Hat, Inc.
//
// 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/hex"
"errors"
"fmt"
"io"
"strings"
)
// UUID is 128bits = 16bytes
type UUID [16]byte
func (uuid UUID) String() string {
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
}
func (uuid UUID) IsZero() bool {
var zeroUuid UUID
return Equal(zeroUuid, uuid)
}
func (uuid UUID) MarshalJSON() ([]byte, error) {
return []byte(`"` + uuid.String() + `"`), nil
}
func (uuid *UUID) UnmarshalJSON(b []byte) error {
if u, err := Parse(string(b)); err != nil {
return err
} else {
copy(uuid[:], u[:])
return nil
}
}
func New() (*UUID, error) {
uuid := new(UUID)
n, err := io.ReadFull(rand.Reader, uuid[:])
if err != nil {
return nil, err
} else if n != len(uuid) {
return nil, errors.New(fmt.Sprintf("insufficient random data (expected: %d, read: %d)", len(uuid), n))
} else {
// variant bits; for more info
// see https://www.ietf.org/rfc/rfc4122.txt section 4.1.1
uuid[8] = uuid[8]&0x3f | 0x80
// version 4 (pseudo-random); for more info
// see https://www.ietf.org/rfc/rfc4122.txt section 4.1.3
uuid[6] = uuid[6]&0x0f | 0x40
}
return uuid, nil
}
func Equal(uuid1 UUID, uuid2 UUID) bool {
for i, v := range uuid1 {
if v != uuid2[i] {
return false
}
}
return true
}
func Parse(s string) (*UUID, error) {
// the string format should be either in
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (or)
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// If the uuid is marshaled by us we add " " around the uuid.
// while parsing this, we have to remove the " " around the
// uuid. So we check if uuid has " " around it, if yes we remove
// it.
if strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") {
s = s[1 : len(s)-1]
}
uuid := new(UUID)
if len(s) == 36 {
if ba, err := hex.DecodeString(s[0:8]); err == nil {
copy(uuid[:4], ba)
} else {
return nil, err
}
if ba, err := hex.DecodeString(s[9:13]); err == nil {
copy(uuid[4:], ba)
} else {
return nil, err
}
if ba, err := hex.DecodeString(s[14:18]); err == nil {
copy(uuid[6:], ba)
} else {
return nil, err
}
if ba, err := hex.DecodeString(s[19:23]); err == nil {
copy(uuid[8:], ba)
} else {
return nil, err
}
if ba, err := hex.DecodeString(s[24:]); err == nil {
copy(uuid[10:], ba)
} else {
return nil, err
}
} else if len(s) == 32 {
if ba, err := hex.DecodeString(s); err == nil {
copy(uuid[:], ba)
} else {
return nil, err
}
} else {
return nil, errors.New("unknown UUID string " + s)
}
return uuid, nil
}