Skip to content

Commit

Permalink
add byte encode/decode
Browse files Browse the repository at this point in the history
  • Loading branch information
jackspirou committed Sep 18, 2024
1 parent 9cec742 commit 7244e58
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 35 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ Package uuidkey encodes UUIDs to a readable Key format via the Base32\-Crockford
- [Constants](<#constants>)
- [type Key](<#Key>)
- [func Encode\(uuid string\) \(Key, error\)](<#Encode>)
- [func EncodeBytes\(uuid \[16\]byte\) \(Key, error\)](<#EncodeBytes>)
- [func Parse\(key string\) \(Key, error\)](<#Parse>)
- [func \(k Key\) Bytes\(\) \(\[16\]byte, error\)](<#Key.Bytes>)
- [func \(k Key\) Decode\(\) \(string, error\)](<#Key.Decode>)
- [func \(k Key\) String\(\) string](<#Key.String>)
- [func \(k Key\) UUID\(\) \(string, error\)](<#Key.UUID>)
Expand Down Expand Up @@ -149,6 +151,15 @@ func Encode(uuid string) (Key, error)
Encode will encode a given UUID string into a Key with basic length validation.
<a name="EncodeBytes"></a>
### func [EncodeBytes](<https://github.com/agentstation/uuidkey/blob/master/codec.go#L62>)
```go
func EncodeBytes(uuid [16]byte) (Key, error)
```
EncodeBytes encodes a \[16\]byte UUID into a Key.
<a name="Parse"></a>
### func [Parse](<https://github.com/agentstation/uuidkey/blob/master/uuidkey.go#L33>)
Expand All @@ -158,8 +169,17 @@ func Parse(key string) (Key, error)
Parse converts a Key formatted string into a Key type.
<a name="Key.Bytes"></a>
### func \(Key\) [Bytes](<https://github.com/agentstation/uuidkey/blob/master/codec.go#L111>)
```go
func (k Key) Bytes() ([16]byte, error)
```
Bytes converts a Key to a \[16\]byte UUID.
<a name="Key.Decode"></a>
### func \(Key\) [Decode](<https://github.com/agentstation/uuidkey/blob/master/codec.go#L62>)
### func \(Key\) [Decode](<https://github.com/agentstation/uuidkey/blob/master/codec.go#L82>)
```go
func (k Key) Decode() (string, error)
Expand Down
49 changes: 49 additions & 0 deletions codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ func Encode(uuid string) (Key, error) {
return Key(e1 + "-" + e2 + "-" + e3 + "-" + e4), nil
}

// EncodeBytes encodes a [16]byte UUID into a Key.
func EncodeBytes(uuid [16]byte) (Key, error) {
// Convert byte groups directly to uint64
// Each group of 4 bytes is combined into a single uint64
n1 := uint64(uuid[0])<<24 | uint64(uuid[1])<<16 | uint64(uuid[2])<<8 | uint64(uuid[3])
n2 := uint64(uuid[4])<<24 | uint64(uuid[5])<<16 | uint64(uuid[6])<<8 | uint64(uuid[7])
n3 := uint64(uuid[8])<<24 | uint64(uuid[9])<<16 | uint64(uuid[10])<<8 | uint64(uuid[11])
n4 := uint64(uuid[12])<<24 | uint64(uuid[13])<<16 | uint64(uuid[14])<<8 | uint64(uuid[15])

// Encode each uint64 into base32 crockford encoding format
e1 := encode(n1) // Encodes bytes 0-3
e2 := encode(n2) // Encodes bytes 4-7
e3 := encode(n3) // Encodes bytes 8-11
e4 := encode(n4) // Encodes bytes 12-15

// Build and return key
// The key is constructed by joining the encoded parts with hyphens
return Key(e1 + "-" + e2 + "-" + e3 + "-" + e4), nil
}

// Decode will decode a given Key into a UUID string with basic length validation.
func (k Key) Decode() (string, error) {
if len(k) != KeyLength { // basic length validation to ensure we can decode
Expand Down Expand Up @@ -86,3 +106,32 @@ func (k Key) Decode() (string, error) {
// build and return UUID string
return (n1 + "-" + n2a + "-" + n2b + "-" + n3a + "-" + n3b + n4), nil
}

// Bytes converts a Key to a [16]byte UUID.
func (k Key) Bytes() ([16]byte, error) {
keyStr := string(k)

// Check the length of the Key
if len(keyStr) != KeyLength {
return [16]byte{}, fmt.Errorf("invalid Key length: expected %d characters, got %d", KeyLength, len(keyStr))
}

var uuid [16]byte
var err error
var n uint64

// Process each part of the key
for i, part := range [4]string{keyStr[:7], keyStr[8:15], keyStr[16:23], keyStr[24:]} {
if n, err = crock32.Decode(strings.ToLower(part)); err != nil {
return [16]byte{}, fmt.Errorf("failed to decode Key part: %v", err)
}

// Write 4 bytes for each part
uuid[i*4] = byte(n >> 24)
uuid[i*4+1] = byte(n >> 16)
uuid[i*4+2] = byte(n >> 8)
uuid[i*4+3] = byte(n)
}

return uuid, nil
}
64 changes: 36 additions & 28 deletions uuid_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,82 +9,90 @@ import (
"github.com/agentstation/uuidkey"
)

const (
validKey = "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X"
invalidKey = "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0" // Invalid key
validUUID = "d1756360-5da0-40df-9926-a76abff5601d"
)

func BenchmarkValidate(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
key := uuidkey.Key(validKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = key.Valid()
}
}

func BenchmarkParse(b *testing.B) {
s := "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X"
func BenchmarkValidateInvalid(b *testing.B) {
key := uuidkey.Key(invalidKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = uuidkey.Parse(s)
_ = key.Valid()
}
}

func BenchmarkFromKey(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
func BenchmarkParse(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = key.UUID()
_, _ = uuidkey.Parse(validKey)
}
}

func BenchmarkEncode(b *testing.B) {
uuid := "d1756360-5da0-40df-9926-a76abff5601d"
func BenchmarkParseInvalid(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = uuidkey.Encode(uuid)
_, _ = uuidkey.Parse(invalidKey)
}
}

func BenchmarkDecode(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
func BenchmarkUUID(b *testing.B) {
key := uuidkey.Key(validKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = key.Decode()
_, _ = key.UUID()
}
}

func BenchmarkValidateInvalid(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0") // Invalid key
func BenchmarkUUIDInvalid(b *testing.B) {
key := uuidkey.Key(invalidKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = key.Valid()
_, _ = key.UUID()
}
}

func BenchmarkParseValid(b *testing.B) {
s := "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X"
func BenchmarkEncode(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = uuidkey.Parse(s)
_, _ = uuidkey.Encode(validUUID)
}
}

func BenchmarkParseInvalid(b *testing.B) {
s := "38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0" // Invalid key
func BenchmarkDecode(b *testing.B) {
key := uuidkey.Key(validKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = uuidkey.Parse(s)
_, _ = key.Decode()
}
}

func BenchmarkUUIDValid(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0X")
func BenchmarkBytes(b *testing.B) {
key := uuidkey.Key(validKey)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = key.UUID()
_, _ = key.Bytes()
}
}

func BenchmarkUUIDInvalid(b *testing.B) {
key := uuidkey.Key("38QARV0-1ET0G6Z-2CJD9VA-2ZZAR0") // Invalid key
func BenchmarkEncodeBytes(b *testing.B) {
uuid := [16]byte{
0xd1, 0x75, 0x63, 0x60,
0x5d, 0xa0, 0x40, 0xdf,
0x99, 0x26, 0xa7, 0x6a,
0xbf, 0xf5, 0x60, 0x1d,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = key.UUID()
_, _ = uuidkey.EncodeBytes(uuid)
}
}
Loading

0 comments on commit 7244e58

Please sign in to comment.