From c5294c98c0dfa1d7ff15fac9b5881de7e5d90194 Mon Sep 17 00:00:00 2001 From: Kevin Parsons Date: Wed, 10 Jul 2019 15:03:44 -0700 Subject: [PATCH] guid: Improve version and variant handling Signed-off-by: Kevin Parsons --- pkg/guid/guid.go | 30 ++++++++++++++++-- pkg/guid/guid_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/pkg/guid/guid.go b/pkg/guid/guid.go index d0595f6..06ccb97 100644 --- a/pkg/guid/guid.go +++ b/pkg/guid/guid.go @@ -52,10 +52,11 @@ func NewV4() (GUID, error) { return GUID{}, err } - b[6] = (b[6] & 0x0f) | 0x40 // Version 4 (randomly generated) - b[8] = (b[8] & 0x3f) | 0x80 // RFC4122 variant + g := FromArray(b) + g.setVersion(4) // Version 4 means randomly generated. + g.setVariant(VariantRFC4122) - return FromArray(b), nil + return g, nil } func fromArray(b [16]byte, order binary.ByteOrder) GUID { @@ -150,6 +151,25 @@ func FromString(s string) (GUID, error) { return g, nil } +func (g *GUID) setVariant(v Variant) { + d := g.Data4[0] + switch v { + case VariantNCS: + d = (d & 0x7f) + case VariantRFC4122: + d = (d & 0x3f) | 0x80 + case VariantMicrosoft: + d = (d & 0x1f) | 0xc0 + case VariantFuture: + d = (d & 0x0f) | 0xe0 + case VariantUnknown: + fallthrough + default: + panic(fmt.Sprintf("invalid variant: %d", v)) + } + g.Data4[0] = d +} + // Variant returns the GUID variant, as defined in RFC 4122. func (g GUID) Variant() Variant { b := g.Data4[0] @@ -165,6 +185,10 @@ func (g GUID) Variant() Variant { return VariantUnknown } +func (g *GUID) setVersion(v Version) { + g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12) +} + // Version returns the GUID version, as defined in RFC 4122. func (g GUID) Version() Version { return Version((g.Data3 & 0xF000) >> 12) diff --git a/pkg/guid/guid_test.go b/pkg/guid/guid_test.go index b229cc6..b84bc41 100644 --- a/pkg/guid/guid_test.go +++ b/pkg/guid/guid_test.go @@ -22,6 +22,78 @@ func mustFromString(t *testing.T, s string) GUID { return g } +func Test_Variant(t *testing.T) { + type testCase struct { + g GUID + v Variant + } + testCases := []testCase{ + {mustFromString(t, "f5cbc1a9-4cba-45a0-0fdd-b6761fc7dcc0"), VariantNCS}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-7fdd-b6761fc7dcc0"), VariantNCS}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0"), VariantRFC4122}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-9fdd-b6761fc7dcc0"), VariantRFC4122}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-cfdd-b6761fc7dcc0"), VariantMicrosoft}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-dfdd-b6761fc7dcc0"), VariantMicrosoft}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-efdd-b6761fc7dcc0"), VariantFuture}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-ffdd-b6761fc7dcc0"), VariantFuture}, + } + for _, tc := range testCases { + actualVariant := tc.g.Variant() + if actualVariant != tc.v { + t.Fatalf("Variant is not correct.\nExpected: %d\nActual: %d\nGUID: %s", tc.v, actualVariant, tc.g) + } + } +} + +func Test_SetVariant(t *testing.T) { + testCases := []Variant{ + VariantNCS, + VariantRFC4122, + VariantMicrosoft, + VariantFuture, + } + g := mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0") + for _, tc := range testCases { + t.Logf("Test case: %d", tc) + g.setVariant(tc) + if g.Variant() != tc { + t.Fatalf("Variant is incorrect.\nExpected: %d\nActual: %d", tc, g.Variant()) + } + } +} + +func Test_Version(t *testing.T) { + type testCase struct { + g GUID + v Version + } + testCases := []testCase{ + {mustFromString(t, "f5cbc1a9-4cba-15a0-0fdd-b6761fc7dcc0"), 1}, + {mustFromString(t, "f5cbc1a9-4cba-25a0-0fdd-b6761fc7dcc0"), 2}, + {mustFromString(t, "f5cbc1a9-4cba-35a0-0fdd-b6761fc7dcc0"), 3}, + {mustFromString(t, "f5cbc1a9-4cba-45a0-0fdd-b6761fc7dcc0"), 4}, + {mustFromString(t, "f5cbc1a9-4cba-55a0-0fdd-b6761fc7dcc0"), 5}, + } + for _, tc := range testCases { + actualVersion := tc.g.Version() + if actualVersion != tc.v { + t.Fatalf("Version is not correct.\nExpected: %d\nActual: %d\nGUID: %s", tc.v, actualVersion, tc.g) + } + } +} + +func Test_SetVersion(t *testing.T) { + g := mustFromString(t, "f5cbc1a9-4cba-45a0-bfdd-b6761fc7dcc0") + for tc := 0; tc < 16; tc++ { + t.Logf("Test case: %d", tc) + v := Version(tc) + g.setVersion(v) + if g.Version() != v { + t.Fatalf("Version is incorrect.\nExpected: %d\nActual: %d", v, g.Version()) + } + } +} + func Test_NewV4IsUnique(t *testing.T) { g := mustNewV4(t) g2 := mustNewV4(t)