1// Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 2// 3// Permission is hereby granted, free of charge, to any person obtaining 4// a copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to 8// permit persons to whom the Software is furnished to do so, subject to 9// the following conditions: 10// 11// The above copyright notice and this permission notice shall be 12// included in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22package uuid 23 24import ( 25 "bytes" 26 "encoding/hex" 27 "fmt" 28) 29 30// FromBytes returns a UUID generated from the raw byte slice input. 31// It will return an error if the slice isn't 16 bytes long. 32func FromBytes(input []byte) (UUID, error) { 33 u := UUID{} 34 err := u.UnmarshalBinary(input) 35 return u, err 36} 37 38// FromBytesOrNil returns a UUID generated from the raw byte slice input. 39// Same behavior as FromBytes(), but returns uuid.Nil instead of an error. 40func FromBytesOrNil(input []byte) UUID { 41 uuid, err := FromBytes(input) 42 if err != nil { 43 return Nil 44 } 45 return uuid 46} 47 48// FromString returns a UUID parsed from the input string. 49// Input is expected in a form accepted by UnmarshalText. 50func FromString(input string) (UUID, error) { 51 u := UUID{} 52 err := u.UnmarshalText([]byte(input)) 53 return u, err 54} 55 56// FromStringOrNil returns a UUID parsed from the input string. 57// Same behavior as FromString(), but returns uuid.Nil instead of an error. 58func FromStringOrNil(input string) UUID { 59 uuid, err := FromString(input) 60 if err != nil { 61 return Nil 62 } 63 return uuid 64} 65 66// MarshalText implements the encoding.TextMarshaler interface. 67// The encoding is the same as returned by the String() method. 68func (u UUID) MarshalText() ([]byte, error) { 69 return []byte(u.String()), nil 70} 71 72// UnmarshalText implements the encoding.TextUnmarshaler interface. 73// Following formats are supported: 74// 75// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 76// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 77// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" 78// "6ba7b8109dad11d180b400c04fd430c8" 79// "{6ba7b8109dad11d180b400c04fd430c8}", 80// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8" 81// 82// ABNF for supported UUID text representation follows: 83// 84// URN := 'urn' 85// UUID-NID := 'uuid' 86// 87// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 88// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 89// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' 90// 91// hexoct := hexdig hexdig 92// 2hexoct := hexoct hexoct 93// 4hexoct := 2hexoct 2hexoct 94// 6hexoct := 4hexoct 2hexoct 95// 12hexoct := 6hexoct 6hexoct 96// 97// hashlike := 12hexoct 98// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct 99// 100// plain := canonical | hashlike 101// uuid := canonical | hashlike | braced | urn 102// 103// braced := '{' plain '}' | '{' hashlike '}' 104// urn := URN ':' UUID-NID ':' plain 105// 106func (u *UUID) UnmarshalText(text []byte) error { 107 switch len(text) { 108 case 32: 109 return u.decodeHashLike(text) 110 case 34, 38: 111 return u.decodeBraced(text) 112 case 36: 113 return u.decodeCanonical(text) 114 case 41, 45: 115 return u.decodeURN(text) 116 default: 117 return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(text), text) 118 } 119} 120 121// decodeCanonical decodes UUID strings that are formatted as defined in RFC-4122 (section 3): 122// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". 123func (u *UUID) decodeCanonical(t []byte) error { 124 if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { 125 return fmt.Errorf("uuid: incorrect UUID format in string %q", t) 126 } 127 128 src := t 129 dst := u[:] 130 131 for i, byteGroup := range byteGroups { 132 if i > 0 { 133 src = src[1:] // skip dash 134 } 135 _, err := hex.Decode(dst[:byteGroup/2], src[:byteGroup]) 136 if err != nil { 137 return err 138 } 139 src = src[byteGroup:] 140 dst = dst[byteGroup/2:] 141 } 142 143 return nil 144} 145 146// decodeHashLike decodes UUID strings that are using the following format: 147// "6ba7b8109dad11d180b400c04fd430c8". 148func (u *UUID) decodeHashLike(t []byte) error { 149 src := t[:] 150 dst := u[:] 151 152 _, err := hex.Decode(dst, src) 153 return err 154} 155 156// decodeBraced decodes UUID strings that are using the following formats: 157// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" 158// "{6ba7b8109dad11d180b400c04fd430c8}". 159func (u *UUID) decodeBraced(t []byte) error { 160 l := len(t) 161 162 if t[0] != '{' || t[l-1] != '}' { 163 return fmt.Errorf("uuid: incorrect UUID format in string %q", t) 164 } 165 166 return u.decodePlain(t[1 : l-1]) 167} 168 169// decodeURN decodes UUID strings that are using the following formats: 170// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" 171// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". 172func (u *UUID) decodeURN(t []byte) error { 173 total := len(t) 174 175 urnUUIDPrefix := t[:9] 176 177 if !bytes.Equal(urnUUIDPrefix, urnPrefix) { 178 return fmt.Errorf("uuid: incorrect UUID format in string %q", t) 179 } 180 181 return u.decodePlain(t[9:total]) 182} 183 184// decodePlain decodes UUID strings that are using the following formats: 185// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format 186// "6ba7b8109dad11d180b400c04fd430c8". 187func (u *UUID) decodePlain(t []byte) error { 188 switch len(t) { 189 case 32: 190 return u.decodeHashLike(t) 191 case 36: 192 return u.decodeCanonical(t) 193 default: 194 return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(t), t) 195 } 196} 197 198// MarshalBinary implements the encoding.BinaryMarshaler interface. 199func (u UUID) MarshalBinary() ([]byte, error) { 200 return u.Bytes(), nil 201} 202 203// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 204// It will return an error if the slice isn't 16 bytes long. 205func (u *UUID) UnmarshalBinary(data []byte) error { 206 if len(data) != Size { 207 return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) 208 } 209 copy(u[:], data) 210 211 return nil 212} 213