Skip to content

Commit

Permalink
Make ID() generate a time/host based ID. Fixes #2
Browse files Browse the repository at this point in the history
  • Loading branch information
leonelquinteros committed Feb 7, 2018
1 parent 771ed2e commit 6e796a4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 19 deletions.
52 changes: 48 additions & 4 deletions id.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
package gorand

// ID is a wrapper for GetHex(64).
// 64 bytes should be enough to use as unique IDs to avoid collisions between generated values.
func ID() (string, error) {
return GetHex(64)
import (
"bytes"
"io"
"time"
)

var localID [9]byte

// Initializes the value for the local process run identifier
func init() {
buf, err := GetBytes(9)
if err != nil {
localID = [9]byte{'D', 'e', 'f', 'a', 'u', 'l', 't', 'I', 'D'}
} else {
_, err = io.ReadFull(bytes.NewBuffer(buf), localID[:])
if err != nil {
localID = [9]byte{'D', 'e', 'f', 'a', 'u', 'l', 't', 'I', 'D'}
}
}
}

// ID generates a [64]byte random value, using time and local identifier into it.
//
// First (most-significative) 15 bytes: time value
// Next 9 bytes: Local process randomly-generated identifier
// Next 40 bytes: Random value
func ID() (id [64]byte, err error) {
var buf []byte

// Time part
now, err := time.Now().MarshalBinary()
if err != nil {
return
}
buf = append(buf, now[:15]...)

// Local ID part
buf = append(buf, localID[:]...)

// Random value
r, err := GetBytes(40)
if err != nil {
return
}
buf = append(buf, r...)

_, err = io.ReadFull(bytes.NewBuffer(buf), id[:])
return
}
25 changes: 23 additions & 2 deletions id_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gorand

import (
"encoding/hex"
"testing"
)

Expand All @@ -10,8 +11,28 @@ func TestID(t *testing.T) {
t.Error(err.Error())
}

if len(id) != 128 {
t.Error("Length of UUID isn't 128")
if len(id) != 64 {
t.Error("Length of ID isn't 64 bytes")
}
}

func TestIDOrder(t *testing.T) {
ids := make([][64]byte, 100000)
for i := 0; i < 100000; i++ {
id, err := ID()
if err != nil {
t.Fatal(err)
}
ids[i] = id
}

for i, v := range ids {
if i == 0 {
continue
}
if hex.EncodeToString(v[:]) <= hex.EncodeToString(ids[i-1][:]) {
t.Fatalf("%v is lesser or equal than %v", hex.EncodeToString(v[:]), hex.EncodeToString(ids[i-1][:]))
}
}
}

Expand Down
29 changes: 16 additions & 13 deletions uuid-v4.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,43 @@ import (
type UUID [16]byte

// UUIDv4 generates a version 4 (randomly generated) UUID
func UUIDv4() (UUID, error) {
var uuid UUID

func UUIDv4() (uuid UUID, err error) {
// Get 16 random bytes
b, err := GetBytes(16)
if err != nil {
return uuid, err
return
}

_, err = io.ReadFull(bytes.NewBuffer(b), uuid[:])
if err != nil {
return uuid, err
return
}

uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil

return
}

// UnmarshalUUID parses a string representation of a UUID and returns its []bytes value.
// It doesn't check for version or varian bits, so it can be used with invalid (non RFC 4122 compilant) values.
func UnmarshalUUID(s string) (uuid UUID, err error) {
if len(s) != 36 {
return uuid, errors.New("Invalid UUID length")
err = errors.New("Invalid UUID length")
return
}
if s[8:9] != "-" || s[13:14] != "-" || s[18:19] != "-" || s[23:24] != "-" {
return uuid, errors.New("Invalid UUID format")
err = errors.New("Invalid UUID format")
return
}

b, err := hex.DecodeString(s[0:8] + s[9:13] + s[14:18] + s[19:23] + s[24:])
if err != nil {
return uuid, err
return
}

_, err = io.ReadFull(bytes.NewBuffer(b), uuid[:])
return uuid, err
return
}

// MarshalUUID converts UUID into its canonical string representation.
Expand All @@ -56,9 +57,11 @@ func MarshalUUID(uuid UUID) (s string, err error) {

_, err = io.ReadFull(bytes.NewBuffer(uuid[:]), b[:])
if err != nil {
return s, err
return
}

s = hex.EncodeToString(b[:])
return s[0:8] + "-" + s[8:12] + "-" + s[12:16] + "-" + s[16:20] + "-" + s[20:], nil
bin := hex.EncodeToString(b[:])
s = bin[0:8] + "-" + bin[8:12] + "-" + bin[12:16] + "-" + bin[16:20] + "-" + bin[20:]

return
}

0 comments on commit 6e796a4

Please sign in to comment.