github.com/spi-ca/misc@v1.0.1/types/bytes16.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/subtle"
     6  	"database/sql/driver"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  )
    11  
    12  // Bytes16 is an alias for [16]byte,
    13  // Bytes16 implements Marshal and Unmarshal.
    14  type Bytes16 [16]byte
    15  
    16  // Marshal obj into your JSON variable.
    17  func (b Bytes16) Marshal() ([]byte, error) {
    18  	return b[:], nil
    19  }
    20  
    21  func (b Bytes16) MarshalTo(buf []byte) (n int, err error) {
    22  	copy(buf, b[:])
    23  	return len(b), nil
    24  }
    25  
    26  // Unmarshal your JSON variable into dest.
    27  func (b *Bytes16) Unmarshal(buf []byte) error {
    28  	if len(buf) != 16 {
    29  		return fmt.Errorf("invalid bytes16 (got %d bytes)", len(buf))
    30  	}
    31  	copy(b[:], buf)
    32  	return nil
    33  }
    34  
    35  func (b Bytes16) Compare(other Bytes16) int {
    36  	return bytes.Compare(b[:], other[:])
    37  }
    38  
    39  func (b Bytes16) Equal(other Bytes16) bool {
    40  	return subtle.ConstantTimeCompare(b[:], other[:]) == 1
    41  }
    42  
    43  // ParseBytes is like Parse, except it parses a byte slice instead of a string.
    44  func ParseBytes16(buf []byte) (b Bytes16, err error) {
    45  	if len(buf) != hex.EncodedLen(len(b)) {
    46  		err = fmt.Errorf("invalid bytes16 length: %d", len(buf))
    47  	}
    48  	_, err = hex.Decode(b[:], buf)
    49  	return
    50  }
    51  
    52  func (b *Bytes16) UnmarshalJSON(from []byte) error {
    53  	quote := []byte("\"")
    54  	quoteSize := len(quote)
    55  
    56  	if len(from) < quoteSize*2 {
    57  		return errors.New("invalid quote notation")
    58  	}
    59  
    60  	if !bytes.HasPrefix(from, quote) || !bytes.HasSuffix(from, quote) {
    61  		return errors.New("invalid quote notation")
    62  	} else if parsed, err := ParseBytes16(from[quoteSize : len(from)-quoteSize]); err == nil {
    63  		*b = parsed
    64  	}
    65  	return nil
    66  }
    67  
    68  func (b Bytes16) MarshalJSON() ([]byte, error) {
    69  	var buffer bytes.Buffer
    70  	buffer.WriteRune('"')
    71  	buffer.WriteString(hex.EncodeToString(b[:]))
    72  	buffer.WriteRune('"')
    73  	return buffer.Bytes(), nil
    74  }
    75  
    76  func (b *Bytes16) Size() int {
    77  	if b == nil {
    78  		return 0
    79  	}
    80  	return 16
    81  }
    82  
    83  func (b *Bytes16) FromHexString(buf []byte) error {
    84  	hexBuf := make([]byte, hex.DecodedLen(len(buf)))
    85  	if n, err := hex.Decode(hexBuf, buf); err != nil {
    86  		return err
    87  	} else {
    88  		hexBuf = hexBuf[:n]
    89  	}
    90  	if err := b.Unmarshal(hexBuf); err != nil {
    91  		return err
    92  	}
    93  	return nil
    94  }
    95  
    96  func (b Bytes16) ToHexString() string {
    97  	return hex.EncodeToString(b[:])
    98  }
    99  
   100  // Scan implements the Scanner interface.
   101  func (b *Bytes16) Scan(src any) error {
   102  	switch src.(type) {
   103  	case string:
   104  		b.FromHexString([]byte(src.(string)))
   105  	default:
   106  		return errors.New("Incompatible type")
   107  	}
   108  	return nil
   109  }
   110  
   111  // Value implements the driver Valuer interface.
   112  func (b Bytes16) Value() (driver.Value, error) {
   113  	return b.ToHexString(), nil
   114  }