github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekatyp/uuid.go (about) 1 // Copyright © 2020. All rights reserved. 2 // Refactorer, modifier: Ilya Stroy. 3 // Contacts: iyuryevich@pm.me, https://github.com/qioalice 4 // License: https://opensource.org/licenses/MIT 5 6 // Copyright (C) 2013-2018 by Maxim Bublis <b@codemonkey.ru> 7 // 8 // Permission is hereby granted, free of charge, to any person obtaining 9 // a copy of this software and associated documentation files (the 10 // "Software"), to deal in the Software without restriction, including 11 // without limitation the rights to use, copy, modify, merge, publish, 12 // distribute, sublicense, and/or sell copies of the Software, and to 13 // permit persons to whom the Software is furnished to do so, subject to 14 // the following conditions: 15 // 16 // The above copyright notice and this permission notice shall be 17 // included in all copies or substantial portions of the Software. 18 // 19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 27 package ekatyp 28 29 import ( 30 "bytes" 31 "database/sql/driver" 32 "fmt" 33 ) 34 35 type ( 36 // UUID representation compliant with specification described in RFC 4122. 37 UUID [_UUID_SIZE]byte 38 ) 39 40 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 41 const ( 42 // UUID versions 43 44 UUID_V1 byte = 1 45 UUID_V2 byte = 2 46 UUID_V3 byte = 3 47 UUID_V4 byte = 4 48 UUID_V5 byte = 5 49 50 // UUID layout variants. 51 52 UUID_VARIANT_NCS byte = 0 53 UUID_VARIANT_RFC4122 byte = 1 54 UUID_VARIANT_MICROSOFT byte = 2 55 UUID_VARIANT_FUTURE byte = 3 56 57 // UUID DCE domains. 58 59 UUID_DOMAIN_PERSON = 0 60 UUID_DOMAIN_GROUP = 1 61 UUID_DOMAIN_ORG = 2 62 ) 63 64 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 65 var ( 66 // _UUID_NULL is special form of UUID that is specified to have all 67 // 128 bits set to zero. 68 _UUID_NULL = UUID{} 69 70 // Predefined namespace UUIDs. 71 UUID_NAMESPACE_DNS = UUID_OrPanic(UUID_FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) 72 UUID_NAMESPACE_URL = UUID_OrPanic(UUID_FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) 73 UUID_NAMESPACE_OID = UUID_OrPanic(UUID_FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) 74 UUID_NAMESPACE_X500 = UUID_OrPanic(UUID_FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) 75 ) 76 77 // ---------------------------- UUID COMMON METHODS --------------------------- // 78 // ---------------------------------------------------------------------------- // 79 80 // Equal returns true if u and anotherUuid equals, otherwise returns false. 81 func (u UUID) Equal(anotherUuid UUID) bool { 82 return bytes.Equal(u[:], anotherUuid[:]) 83 } 84 85 // IsNil reports whether u is nil or not. Is the same as u.Equal(_UUID_NULL). 86 func (u UUID) IsNil() bool { 87 return u.Equal(_UUID_NULL) 88 } 89 90 // SetNil sets the current u to _UUID_NIL. Returns modified u. 91 func (u *UUID) SetNil() *UUID { 92 *u = _UUID_NULL 93 return u 94 } 95 96 // Version returns algorithm version used to generate UUID. 97 func (u UUID) Version() byte { 98 return u[6] >> 4 99 } 100 101 // Variant returns UUID layout variant. 102 func (u UUID) Variant() byte { 103 switch { 104 case (u[8] >> 7) == 0x00: 105 return UUID_VARIANT_NCS 106 case (u[8] >> 6) == 0x02: 107 return UUID_VARIANT_RFC4122 108 case (u[8] >> 5) == 0x06: 109 return UUID_VARIANT_MICROSOFT 110 case (u[8] >> 5) == 0x07: 111 fallthrough 112 default: 113 return UUID_VARIANT_FUTURE 114 } 115 } 116 117 // Bytes returns bytes slice representation of UUID. 118 func (u UUID) Bytes() []byte { 119 return u[:] 120 } 121 122 // Returns canonical string representation of UUID: 123 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. 124 func (u UUID) String() string { 125 return string(u.hexEncodeTo(make([]byte, 36))) 126 } 127 128 // SetVersion sets version bits. 129 func (u *UUID) SetVersion(v byte) { 130 u[6] = (u[6] & 0x0f) | (v << 4) 131 } 132 133 // SetVariant sets variant bits. 134 func (u *UUID) SetVariant(v byte) { 135 switch v { 136 case UUID_VARIANT_NCS: 137 u[8] = u[8]&(0xff>>1) | (0x00 << 7) 138 case UUID_VARIANT_RFC4122: 139 u[8] = u[8]&(0xff>>2) | (0x02 << 6) 140 case UUID_VARIANT_MICROSOFT: 141 u[8] = u[8]&(0xff>>3) | (0x06 << 5) 142 case UUID_VARIANT_FUTURE: 143 fallthrough 144 default: 145 u[8] = u[8]&(0xff>>3) | (0x07 << 5) 146 } 147 } 148 149 // --------------------------- UUID CREATION HELPERS -------------------------- // 150 // ---------------------------------------------------------------------------- // 151 152 // UUID_OrPanic is a helper that wraps a call to a function returning (UUID, error) 153 // and panics if the error is non-nil. 154 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 155 func UUID_OrPanic(u UUID, err error) UUID { 156 if err != nil { 157 panic(err) 158 } 159 return u 160 } 161 162 // UUID_OrNil is a helper that wraps a call to a function returning (UUID, error) 163 // and returns _UUID_NULL if the error is non-nil. 164 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 165 func UUID_OrNil(u UUID, err error) UUID { 166 if err != nil { 167 return _UUID_NULL 168 } 169 return u 170 } 171 172 // -------------------------- UUID RFC4122 GENERATORS ------------------------- // 173 // ---------------------------------------------------------------------------- // 174 175 // UUID_NewV1 returns UUID based on current timestamp and MAC address. 176 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 177 func UUID_NewV1() (UUID, error) { 178 return _UUID_RFC4122_Generator.NewV1() 179 } 180 181 // UUID_NewV2 returns DCE Security UUID based on POSIX UID/GID. 182 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 183 func UUID_NewV2(domain byte) (UUID, error) { 184 return _UUID_RFC4122_Generator.NewV2(domain) 185 } 186 187 // UUID_NewV3 returns UUID based on MD5 hash of namespace UUID and name. 188 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 189 func UUID_NewV3(ns UUID, name string) UUID { 190 return _UUID_RFC4122_Generator.NewV3(ns, name) 191 } 192 193 // UUID_NewV4 returns random generated UUID. 194 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 195 func UUID_NewV4() (UUID, error) { 196 return _UUID_RFC4122_Generator.NewV4() 197 } 198 199 // UUID_NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. 200 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 201 func UUID_NewV5(ns UUID, name string) UUID { 202 return _UUID_RFC4122_Generator.NewV5(ns, name) 203 } 204 205 // --------------- UUID RFC4122 GENERATOR'S WRAPPERS OF HELPERS --------------- // 206 // ---------------------------------------------------------------------------- // 207 208 // Next methods are the same as just generators v1/v2/v4 but it panics 209 // if any error occurred while UUID been generated. 210 211 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 212 func UUID_NewV1_OrPanic() UUID { return UUID_OrPanic(UUID_NewV1()) } 213 214 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 215 func UUID_NewV2_OrPanic(domain byte) UUID { return UUID_OrPanic(UUID_NewV2(domain)) } 216 217 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 218 func UUID_NewV4_OrPanic() UUID { return UUID_OrPanic(UUID_NewV4()) } 219 220 // Next methods are the same as just generators v1/v2/v4 but it returns 221 // a zero UUID if any error is occurred while UUID been generated. 222 223 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 224 func UUID_NewV1_OrNil() UUID { return UUID_OrNil(UUID_NewV1()) } 225 226 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 227 func UUID_NewV_2OrNil(domain byte) UUID { return UUID_OrNil(UUID_NewV2(domain)) } 228 229 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 230 func UUID_NewV4_OrNil() UUID { return UUID_OrNil(UUID_NewV4()) } 231 232 // Next methods are the same as just generators v1/v2/v4, but it returns 233 // only one argument - an error and saves generated UUID as output argument 234 // by the address provided by 'dest' arg. 235 // 236 // It's useful when you awaits only one argument for being returned. 237 // For example in if statement to omit else branch. 238 // 239 // WARNING! 240 // No nil check! 'dest' must be not nil, panic otherwise. 241 242 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 243 func UUID_NewV1_To(dest *UUID) (err error) { 244 *dest, err = UUID_NewV1() 245 return 246 } 247 248 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 249 func UUID_NewV2_To(dest *UUID, domain byte) (err error) { 250 *dest, err = UUID_NewV2(domain) 251 return 252 } 253 254 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 255 func UUID_NewV4_To(dest *UUID) (err error) { 256 *dest, err = UUID_NewV4() 257 return 258 } 259 260 // ------------------------------- UUID PARSERS ------------------------------- // 261 // ---------------------------------------------------------------------------- // 262 263 // UUID_FromBytes returns UUID converted from raw byte slice input. 264 // It will return error if the slice isn't 16 bytes long. 265 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 266 func UUID_FromBytes(input []byte) (u UUID, err error) { 267 err = u.UnmarshalBinary(input) 268 return 269 } 270 271 // UUID_FromString returns UUID parsed from string input. 272 // Input is expected in a form accepted by UnmarshalText. 273 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 274 func UUID_FromString(input string) (u UUID, err error) { 275 err = u.UnmarshalText([]byte(input)) 276 return 277 } 278 279 // -------------------- UUID PARSER'S WRAPPERS OF HELPERS --------------------- // 280 // ---------------------------------------------------------------------------- // 281 282 // UUID_FromBytes_OrPanic is the same as UUID_OrPanic(UUID_FromBytes(input)). 283 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 284 func UUID_FromBytes_OrPanic(input []byte) UUID { 285 return UUID_OrPanic(UUID_FromBytes(input)) 286 } 287 288 // UUID_FromString_OrPanic is the same as UUID_OrPanic(UUID_FromString(input)). 289 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 290 func UUID_FromString_OrPanic(input string) UUID { 291 return UUID_OrPanic(UUID_FromString(input)) 292 } 293 294 // UUID_FromBytes_OrNil is the same as UUID_OrNil(UUID_FromBytes(input)). 295 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 296 func UUID_FromBytes_OrNil(input []byte) UUID { 297 return UUID_OrNil(UUID_FromBytes(input)) 298 } 299 300 // UUID_FromString_OrNil is the same as UUID_OrNil(UUID_FromString(input)). 301 // noinspection GoSnakeCaseUsage (Intellij IDEA suppress snake case warning). 302 func UUID_FromString_OrNil(input string) UUID { 303 return UUID_OrNil(UUID_FromString(input)) 304 } 305 306 // ------------------------ UUID TEXT ENCODER/DECODER ------------------------- // 307 // ---------------------------------------------------------------------------- // 308 309 // MarshalText implements the encoding.TextMarshaler interface. 310 // The encoding is the same as returned by String. 311 func (u UUID) MarshalText() (text []byte, err error) { 312 text = []byte(u.String()) 313 return 314 } 315 316 // UnmarshalText implements the encoding.TextUnmarshaler interface. 317 // Following formats are supported: 318 // - "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 319 // - "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", 320 // - "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" 321 // - "6ba7b8109dad11d180b400c04fd430c8". 322 func (u *UUID) UnmarshalText(text []byte) (err error) { 323 switch len(text) { 324 case 32: 325 return u.decodeHashLike(text) 326 case 36: 327 return u.decodeCanonical(text) 328 case 38: 329 return u.decodeBraced(text) 330 case 41: 331 fallthrough 332 case 45: 333 return u.decodeURN(text) 334 default: 335 return fmt.Errorf("uuid: incorrect UUID length: %s", text) 336 } 337 } 338 339 // ------------------------ UUID JSON ENCODER/DECODER ------------------------- // 340 // ---------------------------------------------------------------------------- // 341 342 // MarshalJSON implements the encoding/json.Marshaler interface. 343 // Returns a JSON string with encoded UUID with the followed format: 344 // "6ba7b810-9dad-11d1-80b4-00c04fd430c8". Returns JSON null if u is _UUID_NULL. 345 func (u UUID) MarshalJSON() ([]byte, error) { 346 if u == _UUID_NULL { 347 return _UUID_JSON_NULL, nil 348 } 349 return u.jsonMarshal(), nil 350 } 351 352 // UnmarshalJSON implements the encoding/json.Unmarshaler interface. 353 // Decodes b as encoded JSON UUID string and saves the result to u. 354 // Supports all UUID variants that u.UnmarshalText() does support but also 355 // supports JSON null values. 356 func (u *UUID) UnmarshalJSON(b []byte) error { 357 if len(b) == 0 || bytes.Compare(b, _UUID_JSON_NULL) == 0 { 358 return nil 359 } 360 // JSON contains quotes (") because it's raw JSON data and JSON strings 361 // has quotes 362 if len(b) < 2 { 363 return fmt.Errorf("uuid: incorrect UUID length: %s", string(b)) 364 } 365 return u.UnmarshalText(b[1 : len(b)-1]) 366 } 367 368 // ----------------------- UUID BINARY ENCODER/DECODER ------------------------ // 369 // ---------------------------------------------------------------------------- // 370 371 // MarshalBinary implements the encoding.BinaryMarshaler interface. 372 func (u UUID) MarshalBinary() (data []byte, err error) { 373 data = u.Bytes() 374 return 375 } 376 377 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 378 // It will return error if the slice isn't 16 bytes long. 379 func (u *UUID) UnmarshalBinary(data []byte) (err error) { 380 if len(data) != _UUID_SIZE { 381 err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) 382 return 383 } 384 copy(u[:], data) 385 return 386 } 387 388 // ------------------------- UUID SQL ENCODER/DECODER ------------------------- // 389 // ---------------------------------------------------------------------------- // 390 391 // Value implements the driver.Valuer interface. Supports SQL NULL. 392 func (u UUID) Value() (driver.Value, error) { 393 if u == _UUID_NULL { 394 return nil, nil 395 } 396 return u.String(), nil 397 } 398 399 // Scan implements the sql.Scanner interface. 400 // A 16-byte slice is handled by UnmarshalBinary, while 401 // a longer byte slice or a string is handled by UnmarshalText. Supports SQL NULL. 402 func (u *UUID) Scan(src any) error { 403 switch src := src.(type) { 404 case nil: 405 return nil 406 407 case []byte: 408 if len(src) == _UUID_SIZE { 409 return u.UnmarshalBinary(src) 410 } 411 return u.UnmarshalText(src) 412 413 case string: 414 return u.UnmarshalText([]byte(src)) 415 } 416 417 return fmt.Errorf("uuid: cannot convert %T to UUID", src) 418 }