// Copyright 2016, 2018 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 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 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) }