github.com/linuxboot/fiano@v1.2.0/pkg/guid/guid.go (about)

     1  // Copyright 2018 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package guid implements the mixed-endian GUID as implemented by Microsoft.
     6  package guid
     7  
     8  import (
     9  	"encoding/hex"
    10  	"encoding/json"
    11  	"fmt"
    12  	"strings"
    13  
    14  	"github.com/linuxboot/fiano/pkg/log"
    15  )
    16  
    17  const (
    18  	// Size represents number of bytes in a GUID
    19  	Size = 16
    20  	// UExample is a example of a string GUID
    21  	UExample  = "01234567-89AB-CDEF-0123-456789ABCDEF"
    22  	strFormat = "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X"
    23  )
    24  
    25  var (
    26  	fields = [...]int{4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1}
    27  )
    28  
    29  // GUID represents a unique identifier.
    30  type GUID [Size]byte
    31  
    32  func reverse(b []byte) {
    33  	for i := 0; i < len(b)/2; i++ {
    34  		other := len(b) - i - 1
    35  		b[other], b[i] = b[i], b[other]
    36  	}
    37  }
    38  
    39  // Parse parses a guid string.
    40  func Parse(s string) (*GUID, error) {
    41  	// remove all hyphens to make it easier to parse.
    42  	stripped := strings.Replace(s, "-", "", -1)
    43  	decoded, err := hex.DecodeString(stripped)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("guid string not correct, need string of the format \n%v\n, got \n%v",
    46  			UExample, s)
    47  	}
    48  
    49  	if len(decoded) != Size {
    50  		return nil, fmt.Errorf("guid string has incorrect length, need string of the format \n%v\n, got \n%v",
    51  			UExample, s)
    52  	}
    53  
    54  	u := GUID{}
    55  	i := 0
    56  	copy(u[:], decoded[:])
    57  	// Correct for endianness.
    58  	for _, fieldlen := range fields {
    59  		reverse(u[i : i+fieldlen])
    60  		i += fieldlen
    61  	}
    62  	return &u, nil
    63  }
    64  
    65  // MustParse parses a guid string or panics.
    66  func MustParse(s string) *GUID {
    67  	guid, err := Parse(s)
    68  	if err != nil {
    69  		log.Fatalf("%v", err)
    70  	}
    71  	return guid
    72  }
    73  
    74  func (u GUID) String() string {
    75  	// Not a pointer receiver so we don't have to manually copy.
    76  	i := 0
    77  	// reverse endianness.
    78  	for _, fieldlen := range fields {
    79  		reverse(u[i : i+fieldlen])
    80  		i += fieldlen
    81  	}
    82  	// Convert to []interface{} for easy printing.
    83  	b := make([]interface{}, Size)
    84  	for i := range u[:] {
    85  		b[i] = u[i]
    86  	}
    87  	return fmt.Sprintf(strFormat, b...)
    88  }
    89  
    90  // MarshalJSON implements the marshaller interface.
    91  // This allows us to actually read and edit the json file
    92  func (u *GUID) MarshalJSON() ([]byte, error) {
    93  	return []byte(`{"GUID" : "` + u.String() + `"}`), nil
    94  }
    95  
    96  // UnmarshalJSON implements the unmarshaller interface.
    97  // This allows us to actually read and edit the json file
    98  func (u *GUID) UnmarshalJSON(b []byte) error {
    99  	j := make(map[string]string)
   100  
   101  	if err := json.Unmarshal(b, &j); err != nil {
   102  		return err
   103  	}
   104  	g, err := Parse(j["GUID"])
   105  	if err != nil {
   106  		return err
   107  	}
   108  	copy(u[:], g[:])
   109  	return nil
   110  }