github.com/mymmsc/gox@v1.3.33/util/uuid/codec.go (about)

     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 UUID converted from raw byte slice input.
    31  // It will return error if the slice isn't 16 bytes long.
    32  func FromBytes(input []byte) (u UUID, err error) {
    33  	err = u.UnmarshalBinary(input)
    34  	return
    35  }
    36  
    37  // FromBytesOrNil returns UUID converted from raw byte slice input.
    38  // Same behavior as FromBytes, but returns a Nil UUID on error.
    39  func FromBytesOrNil(input []byte) UUID {
    40  	uuid, err := FromBytes(input)
    41  	if err != nil {
    42  		return Nil
    43  	}
    44  	return uuid
    45  }
    46  
    47  // FromString returns UUID parsed from string input.
    48  // Input is expected in a form accepted by UnmarshalText.
    49  func FromString(input string) (u UUID, err error) {
    50  	err = u.UnmarshalText([]byte(input))
    51  	return
    52  }
    53  
    54  // FromStringOrNil returns UUID parsed from string input.
    55  // Same behavior as FromString, but returns a Nil UUID on error.
    56  func FromStringOrNil(input string) UUID {
    57  	uuid, err := FromString(input)
    58  	if err != nil {
    59  		return Nil
    60  	}
    61  	return uuid
    62  }
    63  
    64  // MarshalText implements the encoding.TextMarshaler interface.
    65  // The encoding is the same as returned by String.
    66  func (u UUID) MarshalText() (text []byte, err error) {
    67  	text = []byte(u.String())
    68  	return
    69  }
    70  
    71  // UnmarshalText implements the encoding.TextUnmarshaler interface.
    72  // Following formats are supported:
    73  //   "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    74  //   "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
    75  //   "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
    76  //   "6ba7b8109dad11d180b400c04fd430c8"
    77  // ABNF for supported UUID text representation follows:
    78  //   uuid := canonical | hashlike | braced | urn
    79  //   plain := canonical | hashlike
    80  //   canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct
    81  //   hashlike := 12hexoct
    82  //   braced := '{' plain '}'
    83  //   urn := URN ':' UUID-NID ':' plain
    84  //   URN := 'urn'
    85  //   UUID-NID := 'uuid'
    86  //   12hexoct := 6hexoct 6hexoct
    87  //   6hexoct := 4hexoct 2hexoct
    88  //   4hexoct := 2hexoct 2hexoct
    89  //   2hexoct := hexoct hexoct
    90  //   hexoct := hexdig hexdig
    91  //   hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
    92  //             'a' | 'b' | 'c' | 'd' | 'e' | 'f' |
    93  //             'A' | 'B' | 'C' | 'D' | 'E' | 'F'
    94  func (u *UUID) UnmarshalText(text []byte) (err error) {
    95  	switch len(text) {
    96  	case 32:
    97  		return u.decodeHashLike(text)
    98  	case 36:
    99  		return u.decodeCanonical(text)
   100  	case 38:
   101  		return u.decodeBraced(text)
   102  	case 41:
   103  		fallthrough
   104  	case 45:
   105  		return u.decodeURN(text)
   106  	default:
   107  		return fmt.Errorf("uuid: incorrect UUID length: %s", text)
   108  	}
   109  }
   110  
   111  // decodeCanonical decodes UUID string in format
   112  // "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
   113  func (u *UUID) decodeCanonical(t []byte) (err error) {
   114  	if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' {
   115  		return fmt.Errorf("uuid: incorrect UUID format %s", t)
   116  	}
   117  
   118  	src := t[:]
   119  	dst := u[:]
   120  
   121  	for i, byteGroup := range byteGroups {
   122  		if i > 0 {
   123  			src = src[1:] // skip dash
   124  		}
   125  		_, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup])
   126  		if err != nil {
   127  			return
   128  		}
   129  		src = src[byteGroup:]
   130  		dst = dst[byteGroup/2:]
   131  	}
   132  
   133  	return
   134  }
   135  
   136  // decodeHashLike decodes UUID string in format
   137  // "6ba7b8109dad11d180b400c04fd430c8".
   138  func (u *UUID) decodeHashLike(t []byte) (err error) {
   139  	src := t[:]
   140  	dst := u[:]
   141  
   142  	if _, err = hex.Decode(dst, src); err != nil {
   143  		return err
   144  	}
   145  	return
   146  }
   147  
   148  // decodeBraced decodes UUID string in format
   149  // "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format
   150  // "{6ba7b8109dad11d180b400c04fd430c8}".
   151  func (u *UUID) decodeBraced(t []byte) (err error) {
   152  	l := len(t)
   153  
   154  	if t[0] != '{' || t[l-1] != '}' {
   155  		return fmt.Errorf("uuid: incorrect UUID format %s", t)
   156  	}
   157  
   158  	return u.decodePlain(t[1 : l-1])
   159  }
   160  
   161  // decodeURN decodes UUID string in format
   162  // "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format
   163  // "urn:uuid:6ba7b8109dad11d180b400c04fd430c8".
   164  func (u *UUID) decodeURN(t []byte) (err error) {
   165  	total := len(t)
   166  
   167  	urn_uuid_prefix := t[:9]
   168  
   169  	if !bytes.Equal(urn_uuid_prefix, urnPrefix) {
   170  		return fmt.Errorf("uuid: incorrect UUID format: %s", t)
   171  	}
   172  
   173  	return u.decodePlain(t[9:total])
   174  }
   175  
   176  // decodePlain decodes UUID string in canonical format
   177  // "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format
   178  // "6ba7b8109dad11d180b400c04fd430c8".
   179  func (u *UUID) decodePlain(t []byte) (err error) {
   180  	switch len(t) {
   181  	case 32:
   182  		return u.decodeHashLike(t)
   183  	case 36:
   184  		return u.decodeCanonical(t)
   185  	default:
   186  		return fmt.Errorf("uuid: incorrrect UUID length: %s", t)
   187  	}
   188  }
   189  
   190  // MarshalBinary implements the encoding.BinaryMarshaler interface.
   191  func (u UUID) MarshalBinary() (data []byte, err error) {
   192  	data = u.Bytes()
   193  	return
   194  }
   195  
   196  // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
   197  // It will return error if the slice isn't 16 bytes long.
   198  func (u *UUID) UnmarshalBinary(data []byte) (err error) {
   199  	if len(data) != Size {
   200  		err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
   201  		return
   202  	}
   203  	copy(u[:], data)
   204  
   205  	return
   206  }