rclone/vendor/storj.io/common/uuid/uuid.go
2020-05-12 15:56:50 +00:00

142 lines
3.6 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
// Package uuid implements UUID v4 based on RFC4122.
package uuid
import (
"crypto/rand"
"encoding/hex"
"io"
"github.com/zeebo/errs"
)
// Error is the default error class for uuid.
var Error = errs.Class("uuid")
// UUID is big-endian encoded UUID.
//
// UUID can be of any version or variant.
type UUID [16]byte
// New returns a random UUID (version 4 variant 2).
func New() (UUID, error) {
return newRandomFromReader(rand.Reader)
}
// newRandomFromReader returns a random UUID (version 4 variant 2)
// using a custom reader.
func newRandomFromReader(r io.Reader) (UUID, error) {
var uuid UUID
_, err := io.ReadFull(r, uuid[:])
if err != nil {
return uuid, Error.Wrap(err)
}
// version 4, variant 2
uuid[6] = (uuid[6] & 0x0f) | 0x40
uuid[8] = (uuid[8] & 0x3f) | 0x80
return uuid, nil
}
// IsZero returns true when all bytes in uuid are 0.
func (uuid UUID) IsZero() bool { return uuid == UUID{} }
// String returns uuid in "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
func (uuid UUID) String() string {
s := [36]byte{8: '-', 13: '-', 18: '-', 23: '-'}
hex.Encode(s[0:8], uuid[0:4])
hex.Encode(s[9:13], uuid[4:6])
hex.Encode(s[14:18], uuid[6:8])
hex.Encode(s[19:23], uuid[8:10])
hex.Encode(s[24:36], uuid[10:16])
return string(s[:])
}
// FromBytes converts big-endian raw-bytes to an UUID.
//
// FromBytes allows for any version or variant of an UUID.
func FromBytes(bytes []byte) (UUID, error) {
var uuid UUID
if len(uuid) != len(bytes) {
return uuid, Error.New("bytes have wrong length %d expected %d", len(bytes), len(uuid))
}
copy(uuid[:], bytes)
return uuid, nil
}
// FromString parses "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" string form.
//
// FromString allows for any version or variant of an UUID.
func FromString(s string) (UUID, error) {
var uuid UUID
if len(s) != 36 {
return uuid, Error.New("invalid string length %d expected %d", len(s), 36)
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, Error.New("invalid string")
}
var err error
_, err = hex.Decode(uuid[0:4], []byte(s)[0:8])
if err != nil {
return uuid, Error.New("invalid string")
}
_, err = hex.Decode(uuid[4:6], []byte(s)[9:13])
if err != nil {
return uuid, Error.New("invalid string")
}
_, err = hex.Decode(uuid[6:8], []byte(s)[14:18])
if err != nil {
return uuid, Error.New("invalid string")
}
_, err = hex.Decode(uuid[8:10], []byte(s)[19:23])
if err != nil {
return uuid, Error.New("invalid string")
}
_, err = hex.Decode(uuid[10:16], []byte(s)[24:36])
if err != nil {
return uuid, Error.New("invalid string")
}
return uuid, nil
}
// MarshalText marshals UUID in `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` form.
func (uuid UUID) MarshalText() ([]byte, error) {
return []byte(uuid.String()), nil
}
// UnmarshalText unmarshals UUID from `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`.
func (uuid *UUID) UnmarshalText(b []byte) error {
x, err := FromString(string(b))
if err != nil {
return Error.Wrap(err)
}
*uuid = x
return nil
}
// MarshalJSON marshals UUID in `"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"` form.
func (uuid UUID) MarshalJSON() ([]byte, error) {
return []byte(`"` + uuid.String() + `"`), nil
}
// UnmarshalJSON unmarshals UUID from `"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"`.
func (uuid *UUID) UnmarshalJSON(b []byte) error {
if len(b) != 36+2 {
return Error.New("bytes have wrong length %d expected %d", len(b), 36+2)
}
if b[0] != '"' && b[len(b)-1] != '"' {
return Error.New("expected quotes around string")
}
x, err := FromString(string(b[1 : len(b)-1]))
if err != nil {
return Error.Wrap(err)
}
*uuid = x
return nil
}