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 22 package uuid 23 24 import ( 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. 32 func 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. 40 func 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. 50 func 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. 58 func 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. 68 func (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 // 106 func (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". 123 func (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". 148 func (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}". 159 func (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". 172 func (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". 187 func (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. 199 func (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. 205 func (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