nex-go/types/quuid.go

186 lines
4.6 KiB
Go

package types
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"slices"
"strings"
)
// QUUID is an implementation of rdv::qUUID.
// Type alias of []byte.
// Encodes a UUID in little-endian byte order.
type QUUID []byte
// WriteTo writes the QUUID to the given writable
func (qu QUUID) WriteTo(writable Writable) {
writable.Write(qu)
}
// ExtractFrom extracts the QUUID from the given readable
func (qu *QUUID) ExtractFrom(readable Readable) error {
if readable.Remaining() < 16 {
return errors.New("Not enough data left to read qUUID")
}
*qu, _ = readable.Read(16)
return nil
}
// Copy returns a new copied instance of qUUID
func (qu QUUID) Copy() RVType {
return NewQUUID(qu)
}
// Equals checks if the passed Structure contains the same data as the current instance
func (qu QUUID) Equals(o RVType) bool {
if _, ok := o.(QUUID); !ok {
return false
}
return bytes.Equal(qu, o.(QUUID))
}
// CopyRef copies the current value of the QUUID
// and returns a pointer to the new copy
func (qu QUUID) CopyRef() RVTypePtr {
copied := qu.Copy().(QUUID)
return &copied
}
// Deref takes a pointer to the QUUID
// and dereferences it to the raw value.
// Only useful when working with an instance of RVTypePtr
func (qu *QUUID) Deref() RVType {
return *qu
}
// String returns a string representation of the struct
func (qu QUUID) String() string {
return qu.FormatToString(0)
}
// FormatToString pretty-prints the struct data using the provided indentation level
func (qu QUUID) FormatToString(indentationLevel int) string {
indentationValues := strings.Repeat("\t", indentationLevel+1)
indentationEnd := strings.Repeat("\t", indentationLevel)
var b strings.Builder
b.WriteString("qUUID{\n")
b.WriteString(fmt.Sprintf("%sUUID: %s\n", indentationValues, qu.GetStringValue()))
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
return b.String()
}
// GetStringValue returns the UUID encoded in the qUUID
func (qu QUUID) GetStringValue() string {
// * Create copy of the data since slices.Reverse modifies the slice in-line
data := make([]byte, len(qu))
copy(data, qu)
if len(data) != 16 {
// * Default dummy UUID as found in WATCH_DOGS
return "00000000-0000-0000-0000-000000000002"
}
section1 := data[0:4]
section2 := data[4:6]
section3 := data[6:8]
section4 := data[8:10]
section5_1 := data[10:12]
section5_2 := data[12:14]
section5_3 := data[14:16]
slices.Reverse(section1)
slices.Reverse(section2)
slices.Reverse(section3)
slices.Reverse(section4)
slices.Reverse(section5_1)
slices.Reverse(section5_2)
slices.Reverse(section5_3)
var b strings.Builder
b.WriteString(hex.EncodeToString(section1))
b.WriteString("-")
b.WriteString(hex.EncodeToString(section2))
b.WriteString("-")
b.WriteString(hex.EncodeToString(section3))
b.WriteString("-")
b.WriteString(hex.EncodeToString(section4))
b.WriteString("-")
b.WriteString(hex.EncodeToString(section5_1))
b.WriteString(hex.EncodeToString(section5_2))
b.WriteString(hex.EncodeToString(section5_3))
return b.String()
}
// FromString converts a UUID string to a qUUID
func (qu *QUUID) FromString(uuid string) error {
sections := strings.Split(uuid, "-")
if len(sections) != 5 {
return fmt.Errorf("Invalid UUID. Not enough sections. Expected 5, got %d", len(sections))
}
data := make([]byte, 0, 16)
var appendSection = func(section string, expectedSize int) error {
sectionBytes, err := hex.DecodeString(section)
if err != nil {
return err
}
if len(sectionBytes) != expectedSize {
return fmt.Errorf("Unexpected section size. Expected %d, got %d", expectedSize, len(sectionBytes))
}
data = append(data, sectionBytes...)
return nil
}
if err := appendSection(sections[0], 4); err != nil {
return fmt.Errorf("Failed to read UUID section 1. %s", err.Error())
}
if err := appendSection(sections[1], 2); err != nil {
return fmt.Errorf("Failed to read UUID section 2. %s", err.Error())
}
if err := appendSection(sections[2], 2); err != nil {
return fmt.Errorf("Failed to read UUID section 3. %s", err.Error())
}
if err := appendSection(sections[3], 2); err != nil {
return fmt.Errorf("Failed to read UUID section 4. %s", err.Error())
}
if err := appendSection(sections[4], 6); err != nil {
return fmt.Errorf("Failed to read UUID section 5. %s", err.Error())
}
slices.Reverse(data[0:4])
slices.Reverse(data[4:6])
slices.Reverse(data[6:8])
slices.Reverse(data[8:10])
slices.Reverse(data[10:12])
slices.Reverse(data[12:14])
slices.Reverse(data[14:16])
*qu = data
return nil
}
// NewQUUID returns a new qUUID
func NewQUUID(input []byte) QUUID {
qu := make(QUUID, len(input))
copy(qu, input)
return qu
}