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  }