From 2dc6637e2c50c4537f0947928a85d5cfe4cbdc91 Mon Sep 17 00:00:00 2001 From: Kevin Parsons Date: Mon, 13 May 2019 14:28:49 -0700 Subject: [PATCH] pkg/guid: Support big-endian and Windows encodings Signed-off-by: Kevin Parsons --- pkg/guid/guid.go | 56 ++++++++++++++++++++++++++++++++++++------- pkg/guid/guid_test.go | 34 ++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/pkg/guid/guid.go b/pkg/guid/guid.go index 8f58b46..f1add40 100644 --- a/pkg/guid/guid.go +++ b/pkg/guid/guid.go @@ -1,3 +1,8 @@ +// Package guid provides a GUID type. The backing structure for a GUID is +// identical to that used by the golang.org/x/sys/windows GUID type. +// There are two main binary encodings used for a GUID, the big-endian encoding, +// and the Windows (mixed-endian) encoding. See here for details: +// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding package guid import ( @@ -49,15 +54,50 @@ func NewV4() (GUID, error) { return GUID{}, err } - var g GUID - g.Data1 = binary.LittleEndian.Uint32(b[0:4]) - g.Data2 = binary.LittleEndian.Uint16(b[4:6]) - g.Data3 = binary.LittleEndian.Uint16(b[6:8]) - copy(g.Data4[:], b[8:16]) + b[6] = (b[6] & 0x0f) | 0x40 // Version 4 (randomly generated) + b[8] = (b[8] & 0x3f) | 0x80 // RFC4122 variant - g.Data3 = (g.Data3 & 0x0fff) | 0x4000 // Version 4 (randomly generated) - g.Data4[0] = (g.Data4[0] & 0x3f) | 0x80 // RFC4122 variant - return g, nil + return FromArray(b), nil +} + +func fromArray(b [16]byte, order binary.ByteOrder) GUID { + var g GUID + g.Data1 = order.Uint32(b[0:4]) + g.Data2 = order.Uint16(b[4:6]) + g.Data3 = order.Uint16(b[6:8]) + copy(g.Data4[:], b[8:16]) + return g +} + +func (g GUID) toArray(order binary.ByteOrder) [16]byte { + b := [16]byte{} + order.PutUint32(b[0:4], g.Data1) + order.PutUint16(b[4:6], g.Data2) + order.PutUint16(b[6:8], g.Data3) + copy(b[8:16], g.Data4[:]) + return b +} + +// FromArray constructs a GUID from a big-endian encoding array of 16 bytes. +func FromArray(b [16]byte) GUID { + return fromArray(b, binary.BigEndian) +} + +// ToArray returns an array of 16 bytes representing the GUID in big-endian +// encoding. +func (g GUID) ToArray() [16]byte { + return g.toArray(binary.BigEndian) +} + +// FromWindowsArray constructs a GUID from a Windows encoding array of bytes. +func FromWindowsArray(b [16]byte) GUID { + return fromArray(b, binary.LittleEndian) +} + +// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows +// encoding. +func (g GUID) ToWindowsArray() [16]byte { + return g.toArray(binary.LittleEndian) } func (g GUID) String() string { diff --git a/pkg/guid/guid_test.go b/pkg/guid/guid_test.go index 77c06b8..b229cc6 100644 --- a/pkg/guid/guid_test.go +++ b/pkg/guid/guid_test.go @@ -40,6 +40,40 @@ func Test_V4HasCorrectVersionAndVariant(t *testing.T) { } } +func Test_ToArray(t *testing.T) { + g := mustFromString(t, "73c39589-192e-4c64-9acf-6c5d0aa18528") + b := g.ToArray() + expected := [16]byte{0x73, 0xc3, 0x95, 0x89, 0x19, 0x2e, 0x4c, 0x64, 0x9a, 0xcf, 0x6c, 0x5d, 0x0a, 0xa1, 0x85, 0x28} + if b != expected { + t.Fatalf("GUID does not match array form: %x, %x", expected, b) + } +} + +func Test_FromArrayAndBack(t *testing.T) { + b := [16]byte{0x73, 0xc3, 0x95, 0x89, 0x19, 0x2e, 0x4c, 0x64, 0x9a, 0xcf, 0x6c, 0x5d, 0x0a, 0xa1, 0x85, 0x28} + b2 := FromArray(b).ToArray() + if b != b2 { + t.Fatalf("Arrays do not match: %x, %x", b, b2) + } +} + +func Test_ToWindowsArray(t *testing.T) { + g := mustFromString(t, "73c39589-192e-4c64-9acf-6c5d0aa18528") + b := g.ToWindowsArray() + expected := [16]byte{0x89, 0x95, 0xc3, 0x73, 0x2e, 0x19, 0x64, 0x4c, 0x9a, 0xcf, 0x6c, 0x5d, 0x0a, 0xa1, 0x85, 0x28} + if b != expected { + t.Fatalf("GUID does not match array form: %x, %x", expected, b) + } +} + +func Test_FromWindowsArrayAndBack(t *testing.T) { + b := [16]byte{0x73, 0xc3, 0x95, 0x89, 0x19, 0x2e, 0x4c, 0x64, 0x9a, 0xcf, 0x6c, 0x5d, 0x0a, 0xa1, 0x85, 0x28} + b2 := FromWindowsArray(b).ToWindowsArray() + if b != b2 { + t.Fatalf("Arrays do not match: %x, %x", b, b2) + } +} + func Test_FromString(t *testing.T) { orig := "8e35239e-2084-490e-a3db-ab18ee0744cb" g := mustFromString(t, orig)