// Package uuids implements RFC 4122's version 4 UUIDs. package uuids import ( "encoding/hex" "strings" "pkg.jfrech.com/brief/internal/crand" ) // UUID represents a universally unique identifier // as layed out in [RFC 4122 4.1.2], // that is bytes are assembed in big-endian to form 128=16*8 bits: // // UUID[ 0] = byte((uint32(TimeLow) >> 24) & 0xff) // UUID[ 1] = byte((uint32(TimeLow) >> 16) & 0xff) // UUID[ 2] = byte((uint32(TimeLow) >> 8) & 0xff) // UUID[ 3] = byte((uint32(TimeLow) >> 0) & 0xff) // UUID[ 4] = byte((uint16(TimeMid) >> 8) & 0xff) // UUID[ 5] = byte((uint16(TimeMid) >> 0) & 0xff) // UUID[ 6] = byte((uint16(TimeHiAndVersion) >> 8) & 0xff) // UUID[ 7] = byte((uint16(TimeHiAndVersion) >> 0) & 0xff) // UUID[ 8] = byte((uint8(ClkSeqHiRes) >> 0) & 0xff) // UUID[ 9] = byte((uint8(ClkSeqLow) >> 0) & 0xff) // UUID[10] = byte((([6]uint8(Node))[0] >> 0) & 0xff) // UUID[11] = byte((([6]uint8(Node))[1] >> 0) & 0xff) // UUID[12] = byte((([6]uint8(Node))[2] >> 0) & 0xff) // UUID[13] = byte((([6]uint8(Node))[3] >> 0) & 0xff) // UUID[14] = byte((([6]uint8(Node))[4] >> 0) & 0xff) // UUID[15] = byte((([6]uint8(Node))[5] >> 0) & 0xff) // // [RFC 4122 4.1.2]: https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.2 type UUID [Size]byte // Size represents the size of a UUID in bytes. const Size = 16 // V4 generates new UUIDv4 as specified by [RFC 4122 4.4], // whose bits are provided by [crypto/rand.Read]. // // When the system's underlying randomness is to be trusted, // V4 generates a token with 128-2-4=122 bits of entropy. // // V4 will panic if the system's random source is dried up // (this may be irrelevant for Go versions >=1.23). // // V4 will never generate a UUID which [IsZero]. // // [RFC 4122 4.4]: https://www.rfc-editor.org/rfc/rfc4122.html#section-4.4 func V4() UUID { return v4(crand.ReadN(len(UUID{}))) } // v4 pads src with infinitely many zeros and takes the first 16 bytes. // // v4 decreases src' entropy by 6 bits. func v4(src []byte) UUID { var uuid UUID copy(uuid[:], src[:min(len(uuid), len(src))]) // Cf. RFC 4122 4.4, first bullet uuid[(32+16+16)/8] &= 0b_00_111111 uuid[(32+16+16)/8] |= 0b_10_000000 // Cf. RFC 4122 4.4, second bullet // Cf. RFC 4122 4.1.3 uuid[(32+16)/8] &= 0b_0000_1111 uuid[(32+16)/8] |= 0b_0100_0000 return uuid } // String returns a hexadecimal representation of UUID, // hyphenated according to the fields in [RFC 4122 4.2.1]: // // uuid = time_low "-" time_mid "-" time_hi_and_version "-" (clk_seq_hi_res clk_seq_low) "-" node // // [RFC 4122 4.2.1]: https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.2 func (uuid UUID) String() string { b := new(strings.Builder) b.WriteString(hex.EncodeToString(uuid[0:4])) b.WriteString("-") b.WriteString(hex.EncodeToString(uuid[4:6])) b.WriteString("-") b.WriteString(hex.EncodeToString(uuid[6:8])) b.WriteString("-") b.WriteString(hex.EncodeToString(uuid[8:10])) b.WriteString("-") b.WriteString(hex.EncodeToString(uuid[10:16])) return b.String() } // IsZero checks whether all bits of uuid are zero. // Due to [RFC 4122 4.1.1]'s variant bits not being all zero, // no conflict with proper UUIDs is possible. // // [RFC 4122 4.1.1]: https://www.rfc-editor.org/rfc/rfc4122.html#section-4.1.1 func (uuid UUID) IsZero() bool { return uuid == UUID{} } // IsRFC4122v4 reports wheter the given UUID reports // its variant and version as RFC 4122 and 4. func (uuid UUID) IsRFC4122v4() bool { return uuid[8] & 0b11_000000 == 0b10_000000 && uuid[6] & 0b1111_0000 == 0b0100_0000 }