summaryrefslogtreecommitdiff
path: root/sd_id128/types.go
diff options
context:
space:
mode:
Diffstat (limited to 'sd_id128/types.go')
-rw-r--r--sd_id128/types.go184
1 files changed, 184 insertions, 0 deletions
diff --git a/sd_id128/types.go b/sd_id128/types.go
new file mode 100644
index 0000000..3b5228c
--- /dev/null
+++ b/sd_id128/types.go
@@ -0,0 +1,184 @@
+// Copyright 2016 Luke Shumaker
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package sd_id128 reads, generates, and processes 128-bit ID values.
+//
+// These 128-bit IDs are a generalization of DCE UUIDs (of which
+// Microsoft GUIDs are one implementation), but use a simpler string
+// format. This package imposes no structure on the used IDs, unlike
+// UUIDs, but are fully compatible with those types of IDs.
+//
+// DCE UUID: https://tools.ietf.org/html/rfc4122
+//
+// Microsoft GUID: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373931(v=vs.85).aspx
+//
+// The UUID string representation is a (zero-padded) 32-digit
+// hexadecimal number with a hyphen after the 8th, 12th, 16th, and
+// 20th digits (for a total length of 36 glyphs). This package
+// supports this format, but the primary ("plain") string
+// representation is simply a (zero-padded) 32-digit hexadecimal
+// number.
+//
+// For example:
+//
+// Plain: 6ba7b8109dad11d180b400c04fd430c8
+// UUID : 6ba7b810-9dad-11d1-80b4-00c04fd430c8
+//
+// Several restrictions are imposed on the structure of UUIDs. Most
+// notably, the most significant 1-3 bits of the 8th octet specify the
+// UUID type ("variant"); unless dealing with legacy UUIDs, the most
+// significant 2 bits of the octet will be "10", specifying a RFC 4122
+// type UUID. Assuming RFC 4122 type, the most significant 4 bits of
+// the 6th octet then specify the sub-type ("version"), which could
+// impose complicated requirements on the rest of the UUID.
+//
+// This package doesn't really care about all of these UUID structure
+// requirements. Nothing in this package ever inspects an ID's
+// structure. However, because it is anticipated that the IDs
+// generated by this package might be used in a context where a UUID
+// is required, many of the functions that emit an ID ensure that the
+// ID emitted is structured to be a valid UUID.
+package sd_id128
+
+import (
+ "errors"
+)
+
+var (
+ ErrInvalid = errors.New("Invalid ID")
+ ErrNone = errors.New("No Invocation ID set")
+)
+
+type ID128 [16]byte
+
+// String formats the ID as a plain zero-padded 32-digit hexadecimal
+// number.
+func (id ID128) String() string {
+ var s [32]byte
+ for n := range id {
+ s[n*2] = hexchar(id[n] >> 4)
+ s[n*2+1] = hexchar(id[n] & 0xF)
+ }
+ return string(s[:])
+}
+
+// UUID formats the ID as a UUID string.
+func (id ID128) UUID() string {
+ var s [36]byte
+ k := 0
+ for n := range id {
+ switch n {
+ case 4, 6, 8, 10:
+ s[k] = '-'
+ k++
+ }
+ s[k] = hexchar(id[n] >> 4)
+ k++
+ s[k] = hexchar(id[n] & 0xF)
+ k++
+ }
+ return string(s[:])
+}
+
+// Parse parses a 128-bit ID represented either in the plain string
+// format, or in the UUID string format.
+//
+// If you need to validate that the string is in one format or the
+// other, then you should use the ParsePlain or ParseUUID functions.
+//
+// If the input string does not match either of these formats, then
+// ErrInvalid is returned.
+func Parse(s string) (ID128, error) {
+ ret := ID128{}
+ isGuid := false
+
+ i := 0
+ n := 0
+ for n < 16 {
+ if s[i] == '-' {
+ if i == 8 {
+ isGuid = true
+ } else if i == 13 || i == 18 || i == 23 {
+ if !isGuid {
+ return ID128{}, ErrInvalid
+ }
+ } else {
+ return ID128{}, ErrInvalid
+ }
+ i++
+ } else {
+ if i+2 >= len(s) {
+ return ID128{}, ErrInvalid
+ }
+
+ a, err := unhexchar(s[i])
+ i++
+ if err != nil {
+ return ID128{}, err
+ }
+
+ b, err := unhexchar(s[i])
+ i++
+ if err != nil {
+ return ID128{}, err
+ }
+
+ ret[n] = (a << 4) | b
+ n++
+ }
+ }
+
+ if !((i == 36 && isGuid) || (i == 32 && !isGuid)) {
+ return ID128{}, ErrInvalid
+ }
+ if i != len(s) {
+ return ID128{}, ErrInvalid
+ }
+
+ return ret, nil
+}
+
+// ParsePlain parses a 128-bit ID represented in the plain string
+// format.
+//
+// If you would like more flexibility, and would like to be accept
+// either the plain string format or the UUID string format, then you
+// should use the Parse function.
+//
+// If the input string is not a 32-digit hexadecimal number, then
+// ErrInvalid is returned.
+func ParsePlain(s string) (ID128, error) {
+ if len(s) != 32 {
+ return ID128{}, ErrInvalid
+ }
+ return Parse(s)
+}
+
+// ParsePlain parses a 128-bit ID represented in the UUID string
+// format.
+//
+// If you would like more flexibility, and would like to be accept
+// either the plain string format or the UUID string format, then you
+// should use the Parse function.
+//
+// If the input string is not a 36-character UUID string, then
+// ErrInvalid is returned. This function does not validate the
+// variant or version bit fields, nor does it validate any structure
+// implied by them.
+func ParseUUID(s string) (ID128, error) {
+ if len(s) != 36 {
+ return ID128{}, ErrInvalid
+ }
+ return Parse(s)
+}