├── go.mod ├── .gitignore ├── uuid.go ├── LICENSE ├── README.md ├── uuid_base.go ├── helpers.go ├── bit_setter.go ├── uuid_test.go └── uuid_v7.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/uuid6/uuid6go-proto 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /uuid.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | type UUIDv6 uuidBase 4 | 5 | type UUIDv8 uuidBase 6 | 7 | // UUIDv6FromBytes creates a new UUIDv6 from a slice of bytes and returns an error, if an array length does not equal 16. 8 | func UUIDv6FromBytes(b []byte) (uuid UUIDv6, err error) { 9 | var utmp uuidBase 10 | err = utmp.UnmarshalBinary(b) 11 | return UUIDv6(utmp), err 12 | } 13 | 14 | // UUIDv8FromBytes creates a new UUIDv8 from a slice of bytes and returns an error, if an array length does not equal 16. 15 | func UUIDv8FromBytes(b []byte) (uuid UUIDv8, err error) { 16 | var utmp uuidBase 17 | err = utmp.UnmarshalBinary(b) 18 | return UUIDv8(utmp), err 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 uuid6 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UUIDv6, 7 and 8 2 | 3 | Welcome new contestors, UUIDv6, v7 and v8 in Golang. Nice, awesome, machine-sortable and absolutely unique! 4 | 5 | - UUIDv6 - a highly compatible guy. 6 | - UUIDv7 - all brand new and shinny. Very awesome. 7 | - UUIDv8 - strange and implementration specific, to fulfill everyone's dreams and requirements. 8 | 9 | For more information visit https://datatracker.ietf.org/doc/draft-peabody-dispatch-new-uuid-format/ 10 | 11 | ## Usage 12 | 13 | ```go 14 | var gen uuid.UUIDv7Generator 15 | 16 | //Sets how many bits of nano-second precision you would like to see in your UUID. Don't go under 12, don't exceed 48 17 | gen.Precision = 12 18 | 19 | id := gen.Next() 20 | 21 | 22 | fmt.Println(id.ToString()) 23 | //Output: 24 | //060f1cb1ce-8c70-e7b9-f7e4-4b50e3353e 25 | 26 | fmt.Println(id.ToBinaryString()) 27 | //Output: 28 | //00000110 00001111 00011100 10110001 11001110 10001100 01110000 11100111 10111001 11110111 11100100 01001011 01010000 11100011 00110101 00111110 29 | fmt.Println(id.ToMicrosoftString()) 30 | //Output: 31 | //{060F1CB1CE-8C70-E7B9-F7E4-4B50E3353E} 32 | 33 | fmt.Println(id.Time()) 34 | //Ouput: 35 | //2021-07-16 11:08:28 -0700 PDT 36 | 37 | fmt.Println(id.Timestamp()) 38 | //Output: 39 | //1626458908 40 | ``` -------------------------------------------------------------------------------- /uuid_base.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import "fmt" 4 | 5 | type uuidBase [16]byte 6 | 7 | // UnmarshalBinary implements encoding.BinaryUnmarshaler. 8 | func (uuid *uuidBase) UnmarshalBinary(data []byte) error { 9 | if len(data) != 16 { 10 | return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) 11 | } 12 | copy(uuid[:], data) 13 | return nil 14 | } 15 | 16 | // ToString returns UUID as a string in the following format 17 | // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 18 | func (u uuidBase) ToString() string { 19 | return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:5], u[5:7], u[7:9], u[9:11], u[11:16]) 20 | } 21 | 22 | // ToMicrosoftString returns UUID as a string in the following format 23 | // {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 24 | func (u uuidBase) ToMicrosoftString() string { 25 | return fmt.Sprintf("{%X-%X-%X-%X-%X}", u[0:5], u[5:7], u[7:9], u[9:11], u[11:16]) 26 | } 27 | 28 | func (u uuidBase) ToBitArray() []bool { 29 | ret := make([]bool, 128) 30 | for i := 0; i < 128; i++ { 31 | ret[i] = getBit(u[:], i) 32 | } 33 | return ret 34 | } 35 | 36 | // ToBinaryString returns UUID as a string of binary digits grouped in 8 37 | func (u uuidBase) ToBinaryString() string { 38 | var ret string 39 | for _, n := range u { 40 | ret += fmt.Sprintf("%0*b ", 8, n) // prints 00000000 11111101 41 | } 42 | return ret 43 | } 44 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "time" 7 | ) 8 | 9 | // encodeDecimal takes nanoseconds and converts them to the binary-encoded arbitrary-precision 10 | // byte array. 11 | func encodeDecimal(sec float64, bits int) (val []byte, err error) { 12 | len := int(math.Log10(sec)) + 1 13 | sec = sec / math.Pow10(len) 14 | num := math.Pow(2, float64(bits)) 15 | var part uint64 = uint64(sec * float64(num)) 16 | val = make([]byte, 8) 17 | binary.BigEndian.PutUint64(val, part) 18 | return val, nil 19 | } 20 | 21 | // decodeDecimal decodes an array of bits into a decimal 22 | func decodeDecimal(val []byte, bits int) (sec float64, err error) { 23 | num := math.Pow(2, float64(bits)) 24 | ss := float64(toUint64(val)) / float64(num) 25 | return ss, nil 26 | } 27 | 28 | // toUint64 converts []byte to uint64 29 | func toUint64(data []byte) uint64 { 30 | var arr [8]byte 31 | copy(arr[len(arr)-len(data):], data) 32 | return binary.BigEndian.Uint64(arr[:]) 33 | } 34 | 35 | // toUint64 converts []byte to uint64 36 | func toBytes(data uint64) []byte { 37 | b := make([]byte, 8) 38 | binary.BigEndian.PutUint64(b, uint64(data)) 39 | return b 40 | } 41 | 42 | func timeToBytes(t time.Time) []byte { 43 | tsBytes := make([]byte, 8) 44 | binary.BigEndian.PutUint64(tsBytes, uint64(t.Unix())) 45 | return tsBytes 46 | } 47 | -------------------------------------------------------------------------------- /bit_setter.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | // getBit returns the value of a bit at a specified positon in UUID 4 | func (b uuidBase) getBit(index int) bool { 5 | pos := index / 8 6 | j := uint(index % 8) 7 | j = 7 - j 8 | return (b[pos] & (uint8(1) << j)) != 0 9 | } 10 | 11 | // getBit returns the value of a bit at a specified positon in UUIDv7 12 | func (b UUIDv7) getBit(index int) bool { 13 | return uuidBase(b).getBit(index) 14 | } 15 | 16 | // getBit returns the value of a bit at a specified positon in given byte array 17 | func getBit(b []byte, index int) bool { 18 | pos := index / 8 19 | j := uint(index % 8) 20 | j = 7 - j 21 | return (b[pos] & (uint8(1) << j)) != 0 22 | } 23 | 24 | // setBit sets the value of a bit at a specified positon in []byte 25 | func setBit(b []byte, index int, value bool) { 26 | pos := index / 8 27 | j := uint(index % 8) 28 | j = 7 - j 29 | if value { 30 | b[pos] |= (uint8(1) << j) 31 | } else { 32 | b[pos] &= ^(uint8(1) << j) 33 | } 34 | } 35 | 36 | // setBit sets the value of a bit at a specified positon in UUID 37 | func (b uuidBase) setBit(index int, value bool) uuidBase { 38 | pos := index / 8 39 | j := uint(index % 8) 40 | j = 7 - j 41 | if value { 42 | b[pos] |= (uint8(1) << j) 43 | } else { 44 | b[pos] &= ^(uint8(1) << j) 45 | } 46 | return b 47 | } 48 | 49 | // setBit sets the value of a bit at a specified positon in UUIDv7 50 | func (b UUIDv7) setBit(index int, value bool) UUIDv7 { 51 | return UUIDv7(uuidBase(b).setBit(index, value)) 52 | } 53 | 54 | // indexer returns updated index of a bit in the array of bits. It skips bits 48-51 and 64,65 55 | // for those containt information about Version and Variant and can't be populated by the 56 | // precision bits. It also omits first 36 bits of timestamp at the beginning of the GUID 57 | func indexer(input int) int { 58 | out := 35 + input //Skip the TS block and start counting right after ts block 59 | if input > 11 { //If we are bumbing into a ver block, skip it 60 | out += 4 61 | } 62 | 63 | if input > 23 { //If we are bumping into a var block 64 | out += 2 65 | } 66 | return out 67 | } 68 | 69 | // absoluteIndexer returns updated index of a bit in the array of bits. It skips bits 48-51 and 64,65 70 | // for those containt information about Version and Variant and can't be populated by the 71 | // precision bits. It DOES NOT omit first 36 bits of timestamp at the beginning of the GUID 72 | func absoluteIndexer(input int) int { 73 | out := input //Skip the TS block and start counting right after ts block 74 | if input > 35+11 { //If we are bumbing into a ver block, skip it 75 | out += 4 76 | } 77 | if input > 35+23 { //If we are bumping into a var block 78 | out += 2 79 | } 80 | return out 81 | } 82 | 83 | // stack adds a chunk of bits, encoded as []byte at the selected started position, with respect to the timestamp, version and variant values. 84 | func (b UUIDv7) stack(startingPosition int, value []byte, length int) (UUIDv7, int) { 85 | rettype, retval := (uuidBase(b)).stack(startingPosition, value, length) 86 | return UUIDv7(rettype), retval 87 | } 88 | 89 | // stack adds a chunk of bits, encoded as []byte at the selected started position, with respect to the timestamp, version and variant values. 90 | func (b uuidBase) stack(startingPosition int, value []byte, length int) (uuidBase, int) { 91 | cnt := 0 92 | for i := startingPosition; i < startingPosition+length; i++ { 93 | bit := getBit(value, (len(value)*8-1)-cnt) 94 | b = b.setBit(absoluteIndexer(i), bit) 95 | cnt++ 96 | } 97 | return b, startingPosition + length 98 | } 99 | -------------------------------------------------------------------------------- /uuid_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestHelloName calls greetings.Hello with a name, checking 8 | // for a valid return value. 9 | 10 | func TestUUIDv7FromBytes(t *testing.T) { 11 | _, err := UUIDv7FromBytes([]byte{0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16}) 12 | if err != nil { 13 | t.Fatal("Did not expet an error during a conversion", err) 14 | } 15 | 16 | _, err = UUIDv7FromBytes([]byte{0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16}) 17 | if err == nil { 18 | t.Fatal("Expeted a conversion to error", err) 19 | } 20 | 21 | temp, _ := UUIDv7FromBytes([]byte{0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16}) 22 | 23 | var byteArray [16]byte = [16]byte(temp) 24 | if byteArray != [16]byte{0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16} { 25 | t.Fatal("Somehow byte array is changed after a marshaling") 26 | } 27 | } 28 | 29 | func TestUUIDv7Ver(t *testing.T) { 30 | test, _ := UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 31 | 32 | if test.Ver() != 15 { 33 | t.Fatal("Expected Ver to be 16, got ", test.Ver(), " instead") 34 | } 35 | 36 | test, _ = UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 37 | 38 | if test.Ver() != 0 { 39 | t.Fatal("Expected Ver to be 0, got ", test.Ver(), " instead") 40 | } 41 | 42 | test, _ = UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 43 | 44 | if test.Ver() != 7 { 45 | t.Fatal("Expected Ver to be 7, got ", test.Ver(), " instead") 46 | } 47 | } 48 | 49 | func TestUUIDv7Var(t *testing.T) { 50 | test, _ := UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 51 | 52 | if test.Var() != 3 { 53 | t.Fatal("Expected Var to be 3, got ", test.Var(), " instead") 54 | } 55 | 56 | test, _ = UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 57 | 58 | if test.Var() != 0 { 59 | t.Fatal("Expected Var to be 0, got ", test.Var(), " instead") 60 | } 61 | 62 | test, _ = UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 63 | 64 | if test.Var() != 2 { 65 | t.Fatal("Expected Var to be 2, got ", test.Var(), " instead") 66 | } 67 | } 68 | 69 | func TestUUIDv7Timestamp(t *testing.T) { 70 | test, _ := UUIDv7FromBytes([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 71 | 72 | if test.Timestamp() != 68719476735 { 73 | t.Fatal("Expected UnixTS to be 68719476735, got ", test.Timestamp(), " instead") 74 | } 75 | 76 | test, _ = UUIDv7FromBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 77 | 78 | if test.Timestamp() != 0 { 79 | t.Fatal("Expected UnixTS to be 0, got ", test.Timestamp(), " instead") 80 | } 81 | 82 | test, _ = UUIDv7FromBytes([]byte{0x01, 0xE5, 0x30, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) 83 | 84 | if test.Timestamp() != 508759567 { 85 | t.Fatal("Expected UnixTS to be 508759567, got ", test.Timestamp(), " instead") 86 | } 87 | } 88 | 89 | func TestUUIDv7Generation(t *testing.T) { 90 | var test UUIDv7Generator 91 | test.SubsecondPrecisionLength = 16 92 | test.NodePrecisionLength = 4 93 | test.Node = 15 94 | test.CounterPrecisionLength = 8 95 | guid := test.Next() 96 | test.Parse(&guid) 97 | //ps, c, n := test.Parse(&guid) 98 | // fmt.Println("binary", guid.ToBinaryString()) 99 | // fmt.Println("guid", guid.ToString()) 100 | // fmt.Print(ps, c, n) 101 | } 102 | 103 | func TestConverters(t *testing.T) { 104 | // out, _ := encodeDecimal(209302800, 64) 105 | // put, _ := decodeDecimal(out, 64) 106 | // fmt.Print(out, put) 107 | } 108 | -------------------------------------------------------------------------------- /uuid_v7.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "time" 7 | ) 8 | 9 | type UUIDv7 uuidBase 10 | 11 | // Generator is a primary structure that allows you to create new UUIDv7 12 | // SubsecondPrecisionLength is a number of bits to carry sub-second information. On a systems with a high-resulution sub-second precision (like x64 Linux/Windows/MAC) it can go up to 48 bits 13 | // NodePrecisionBits is a number of bits for node information. If this is not set to 0 [default], a Node variable must be set to a non-zero value 14 | // Node information about the node generating UUIDs 15 | // CounterPrecisionBits how many bits are dedicated to a counter. If two UUIDs were generated at the same time an internal counter would increase, distinguishing those UUIDs 16 | type UUIDv7Generator struct { 17 | SubsecondPrecisionLength int 18 | 19 | NodePrecisionLength int 20 | Node uint64 21 | 22 | CounterPrecisionLength int 23 | 24 | //Internal constants used during the generation process. 25 | currentTs []byte 26 | counter uint64 27 | currentPosition int 28 | } 29 | 30 | // UUIDv7FromBytes creates a new UUIDv7 from a slice of bytes and returns an error, if an array length does not equal 16. 31 | func UUIDv7FromBytes(b []byte) (uuid UUIDv7, err error) { 32 | var utmp uuidBase 33 | err = utmp.UnmarshalBinary(b) 34 | return UUIDv7(utmp), err 35 | } 36 | 37 | // Timestamp returns unix epoch stored in the struct without millisecond precision 38 | func (u UUIDv7) Timestamp() uint64 { 39 | bytes := [16]byte(u) 40 | tmp := toUint64(bytes[0:5]) 41 | tmp = tmp >> 4 //We are off by 4 last bits of the byte there. 42 | return tmp 43 | } 44 | 45 | // Timestamp returns unix epoch stored in the struct without millisecond precision 46 | func (u UUIDv7) Time() time.Time { 47 | bytes := [16]byte(u) 48 | tmp := toUint64(bytes[0:5]) 49 | tmp = tmp >> 4 //We are off by 4 last bits of the byte there. 50 | return time.Unix(int64(tmp), 0) 51 | } 52 | 53 | // Ver returns a version of UUID, 07 in this case 54 | func (u UUIDv7) Ver() uint16 { 55 | bytes := [16]byte(u) 56 | var tmp uint16 = uint16(bytes[6:7][0]) 57 | tmp = tmp >> 4 //We are off by 4 last bits of the byte there. 58 | return tmp 59 | } 60 | 61 | // Var doing something described in the draft, but I don't know what 62 | func (u UUIDv7) Var() uint16 { 63 | bytes := [16]byte(u) 64 | var tmp uint16 = uint16(bytes[8:9][0]) 65 | tmp = tmp >> 6 //We are off by 4 last bits of the byte there. 66 | return tmp 67 | } 68 | 69 | /* 70 | bytes 0 1 2 3 71 | 0 1 2 3 72 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 73 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | | unixts | 75 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 | 77 | bytes 4 5 6 7 78 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 79 | |unixts | subsec_a | ver | subsec_b | 80 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 81 | 82 | bytes 8 9 10 11 83 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 84 | |var| subsec_seq_node | 85 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 86 | 87 | bytes 12 13 14 15 88 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 89 | | subsec_seq_node | 90 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 91 | 92 | */ 93 | 94 | // UUIDv7FromBytes creates a new UUIDv7 from a slice of bytes and returns an error, if an array length does not equal 16. 95 | func (u *UUIDv7Generator) Next() (uuid UUIDv7) { 96 | var retval UUIDv7 97 | //Getting current time 98 | then := time.Now() 99 | tsBytes := timeToBytes(then) 100 | 101 | //Copy unix timestamp where it suppose to go, bits 0 - 36 102 | for i := 0; i < 36; i++ { 103 | retval = retval.setBit(35-i, getBit(tsBytes, 63-i)) 104 | } 105 | u.currentPosition = 36 106 | 107 | //Getting nanoseconds 108 | var precisitonBytes []byte 109 | //Use the below to test your counter 110 | // precisitonBytes := []byte{0xff, 0xff} 111 | if u.SubsecondPrecisionLength != 0 { 112 | // fmt.Println("Nanos:", then.Nanosecond()) 113 | precisitonBytes, _ = encodeDecimal(float64(then.Nanosecond()), u.SubsecondPrecisionLength) 114 | } 115 | 116 | //Adding sub-second precision length 117 | retval, u.currentPosition = retval.stack(u.currentPosition, precisitonBytes, u.SubsecondPrecisionLength) 118 | 119 | //Checks if we are going to use counter at all, or we don't need it 120 | 121 | //If we are using precision and bytes on the last tick equal current bytes, 122 | //use counter and append it. 123 | //But do this only if we are using precision 124 | if u.SubsecondPrecisionLength != 0 { 125 | if bytes.Equal(u.currentTs, precisitonBytes) { 126 | u.counter++ 127 | } else { 128 | u.counter = 0 129 | } 130 | u.currentTs = precisitonBytes 131 | } 132 | 133 | //If we are using the counter, it goes right after precision bytes 134 | if u.CounterPrecisionLength != 0 { 135 | //counter bits 136 | retval, u.currentPosition = retval.stack(u.currentPosition, toBytes(u.counter), u.CounterPrecisionLength) 137 | 138 | } 139 | 140 | //Adding node data after bytes 141 | if u.NodePrecisionLength != 0 { 142 | retval, u.currentPosition = retval.stack(u.currentPosition, toBytes(u.Node), u.NodePrecisionLength) 143 | } 144 | 145 | //Create some random crypto data for the tail end 146 | rnd := make([]byte, 16) 147 | rand.Read(rnd) 148 | 149 | //Copy crypto data from the array to the end of the GUID 150 | cnt := 0 151 | limit := absoluteIndexer(u.currentPosition) 152 | for i := 127; i > limit; i-- { 153 | //Ommiting bits 48-51 and 64, 65. Those contain version and variant information 154 | if i == 48 || i == 49 || i == 50 || i == 51 || i == 64 || i == 65 { 155 | continue 156 | } 157 | bit := getBit(rnd, cnt) 158 | cnt++ 159 | retval = retval.setBit(i, bit) 160 | } 161 | 162 | //Adding version data [0111 = 7] 163 | retval = retval.setBit(48, false) 164 | retval = retval.setBit(49, true) 165 | retval = retval.setBit(50, true) 166 | retval = retval.setBit(51, true) 167 | 168 | //Adding variant data [10] 169 | retval = retval.setBit(64, true) 170 | retval = retval.setBit(65, false) 171 | 172 | return UUIDv7(retval) 173 | } 174 | 175 | //Parse finds the values for precisionSeconds, counter and node for a specified UUID on a specified generator. 176 | func (u *UUIDv7Generator) Parse(id *UUIDv7) (precisionSeconds float64, counter uint64, node uint64) { 177 | ps := make([]byte, 8) 178 | c := make([]byte, 8) 179 | n := make([]byte, 8) 180 | 181 | var j = 0 182 | for i := 35 + u.SubsecondPrecisionLength; i > 35; i-- { 183 | // fmt.Println("Subsecond precision:", j, absoluteIndexer(i), getBit(id[:], absoluteIndexer(i))) 184 | setBit(ps, 63-j, getBit(id[:], absoluteIndexer(i))) 185 | j++ 186 | } 187 | 188 | j = 0 189 | for i := 35 + u.SubsecondPrecisionLength + u.CounterPrecisionLength; i > 35+u.SubsecondPrecisionLength; i-- { 190 | // fmt.Println("Counter precision:", j, absoluteIndexer(i), getBit(id[:], absoluteIndexer(i))) 191 | setBit(c, 63-j, getBit(id[:], absoluteIndexer(i))) 192 | j++ 193 | 194 | } 195 | 196 | j = 0 197 | for i := 35 + u.SubsecondPrecisionLength + u.CounterPrecisionLength + u.NodePrecisionLength; i > 35+u.SubsecondPrecisionLength+u.CounterPrecisionLength; i-- { 198 | // fmt.Println("Node precision:", j, absoluteIndexer(i), getBit(id[:], absoluteIndexer(i))) 199 | setBit(n, 63-j, getBit(id[:], absoluteIndexer(i))) 200 | j++ 201 | } 202 | 203 | precision, _ := decodeDecimal(ps[:], u.SubsecondPrecisionLength) 204 | return precision, toUint64(c[:]), toUint64(n[:]) 205 | } 206 | 207 | func (u UUIDv7) ToString() string { 208 | return uuidBase(u).ToString() 209 | } 210 | 211 | func (u UUIDv7) ToMicrosoftString() string { 212 | return uuidBase(u).ToMicrosoftString() 213 | } 214 | 215 | func (u UUIDv7) ToBinaryString() string { 216 | return uuidBase(u).ToBinaryString() 217 | } 218 | 219 | func (u UUIDv7) ToBitArray() []bool { 220 | return uuidBase(u).ToBitArray() 221 | } 222 | --------------------------------------------------------------------------------