mirror of
https://github.com/rclone/rclone.git
synced 2024-12-12 02:02:02 +01:00
142 lines
3.6 KiB
Go
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
|
|
}
|