From 963644c19058829f74bd7d19a484c0786d6777cd Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 27 Dec 2016 21:59:17 -0700 Subject: Documentation improvements. Also, I renamed a couple of the sd_id128 functions for better clarity. But since I haven't pushed since originally implementing them, I'm not labeling it as a breaking change. --- sd_id128/types.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 sd_id128/types.go (limited to 'sd_id128/types.go') 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) +} -- cgit v1.2.3-2-g168b