Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add p/uuid #2076

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c59a3ba
add package UUID
May 12, 2024
dbcacc9
make fmt imports
May 12, 2024
bdb8b96
change uint16 to uint64
May 12, 2024
c0b93f0
delete a var and change comment
May 13, 2024
db92344
add a simple sequence resolver and test generation uuid
Jun 3, 2024
2f1bc74
corrective after review
Jun 13, 2024
ecbf534
convert MaxTimestamp
Jun 13, 2024
32ec061
Merge branch 'master' into dev/thox/uuid
DIGIX666 Jun 13, 2024
21d9f13
deleting an import in the test
Jun 13, 2024
bfd6d20
Merge branch 'master' into dev/thox/uuid
DIGIX666 Jun 15, 2024
791c33b
Merge branch 'master' into dev/thox/uuid
DIGIX666 Jul 9, 2024
2f4dd8b
change operation uint8 to uint64
DIGIX666 Jul 9, 2024
eb2c14c
change correction for lint and fmt
DIGIX666 Jul 9, 2024
1e9bc87
Merge branch 'gnolang:master' into dev/thox/uuid
DIGIX666 Aug 12, 2024
4ede6f4
feat : add p/demo/entropy
DIGIX666 Aug 13, 2024
10731af
fixup gno.mod
DIGIX666 Aug 13, 2024
8537844
gno mod tidy
DIGIX666 Aug 13, 2024
91cdfe8
update for use Unix and hex.EncodeToString()
DIGIX666 Aug 20, 2024
68efeff
make tidy
DIGIX666 Aug 20, 2024
e9a5953
change uint64 for uint32
DIGIX666 Aug 21, 2024
1611c05
Merge branch 'master' into dev/thox/uuid
DIGIX666 Aug 21, 2024
a8184d0
Merge branch 'master' into dev/thox/uuid
thehowl Sep 13, 2024
8ea2368
Apply suggestions from code review
DIGIX666 Sep 14, 2024
859cae9
change for 42 bits
DIGIX666 Sep 17, 2024
cc38c09
correct the owerflow for elaspedTime
DIGIX666 Sep 17, 2024
353beb1
change startTime second to ms
DIGIX666 Sep 17, 2024
335c5f2
Merge branch 'master' into dev/thox/uuid
DIGIX666 Sep 17, 2024
485ce8d
add time.Now
DIGIX666 Sep 17, 2024
0169b68
add output comment in test
DIGIX666 Sep 18, 2024
ad62314
Merge branch 'master' into dev/thox/uuid
DIGIX666 Nov 3, 2024
3a18918
change name to snowflake
DIGIX666 Nov 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/gno.land/p/demo/uuid/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/p/demo/uuid

require (
gno.land/p/demo/entropy v0.0.0-latest
gno.land/p/demo/uassert v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
)
125 changes: 125 additions & 0 deletions examples/gno.land/p/demo/uuid/uuid.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Package uuid provides utility functions for generating unique identifiers
// (UUIDs) based on a custom implementation inspired by the Snowflake algorithm (https://pkg.go.dev/github.com/godruoyi/go-snowflake#section-sourcefiles).
// The UUIDs generated by this package ensure uniqueness and are suitable for
// distributed systems. The package offers a mechanism to convert these IDs
// to standard UUID format strings, making them compatible with systems
// requiring UUIDs.
// Add import "gno.land/p/demo/entropy" to use the entropy package. For of the pseudo-random number generator.
// TimestampLength is 32-bit because entropy.Value() returns a uint32.
// 32 it's the maximum value that can be returned "4 294 967 295", It's represent 136 years (4 294 967 295 secondes)

DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
package uuid

import (
"encoding/binary"
"encoding/hex"
"std"

"gno.land/p/demo/entropy"
"gno.land/p/demo/ufmt"
)

const (
TimestampLength uint8 = 32
MachineIDLength uint64 = 10
SequenceLength uint64 = 12
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
MaxSequence uint16 = 1<<SequenceLength - 1
MaxTimestamp uint32 = 1<<TimestampLength - 1
MaxMachineID uint64 = 1<<MachineIDLength - 1
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved

machineIDMoveLength = SequenceLength
timestampMoveLength = MachineIDLength + SequenceLength

startTime uint32 = 1226354400 // 10 Nov 2008 23:00:00 UTC in seconds (Snowflake epoch)
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
)

// UUID struct for generating unique identifiers.
type UUID struct {
machineID uint64
sequence uint16
startTime uint32
lastTimestamp uint32
counter int64
entropy *entropy.Instance
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
}

func NewUUID() *UUID {
u := &UUID{
startTime: startTime,
entropy: entropy.New(),
}
u.SetMachineID()
return u
}

// ID generates
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
func (u *UUID) ID() uint64 {
current := u.getEntropy()
elapsedTime := current - u.startTime
u.lastTimestamp = current
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved

if elapsedTime < 0 || elapsedTime > MaxTimestamp {
panic("Exceeding the time limit")
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
}

id := (uint64(elapsedTime) << timestampMoveLength) |
(uint64(u.machineID) << machineIDMoveLength) |
uint64(u.incrementSequence())
return id
}

func (u *UUID) incrementSequence() uint16 {
u.sequence = (u.sequence + 1) & MaxSequence
return u.sequence
}

func (u *UUID) getEntropy() uint32 {
return u.entropy.Value()
}

// SetMachineID sets the machine ID based on the caller’s address.
func (u *UUID) SetMachineID() {
caller := std.PrevRealm().Addr() // Retrieve the caller’s address
machineID := uint64(0)
for _, c := range caller.String() {
machineID += uint64(c)
}
machineID %= MaxMachineID
u.machineID = machineID
}

func IDToUUID(id uint64) string {
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved
bytes := make([]byte, 16)

// Copy transformed ID into the second half of the array
copy(bytes[8:], uint64ToBytes(id))

// Use bits from the Snowflake ID to generate the first half of the UUID
bytes[0] = byte(id >> 60)
bytes[1] = byte(id >> 52)
bytes[2] = byte(id >> 44)
bytes[3] = byte(id >> 36)
bytes[4] = byte(id >> 28)
bytes[5] = byte(id >> 20)
bytes[6] = byte(id >> 12)
bytes[7] = byte(id >> 4)

// set the version and variant bits according to UUID specification.
bytes[6] = (bytes[6] & 0x0f) | 0x40 // version 4 (random)
bytes[8] = (bytes[8] & 0x3f) | 0x80 // variant 1 (RFC 4122)
DIGIX666 marked this conversation as resolved.
Show resolved Hide resolved

hexStr := hex.EncodeToString(bytes)
return ufmt.Sprintf("%s-%s-%s-%s-%s",
hexStr[0:8],
hexStr[8:12],
hexStr[12:16],
hexStr[16:20],
hexStr[20:],
)
}

func uint64ToBytes(i uint64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, i)
return buf
}
40 changes: 40 additions & 0 deletions examples/gno.land/p/demo/uuid/uuid_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package uuid

import (
"testing"

"gno.land/p/demo/uassert"
)

func TestID(t *testing.T) {
u := NewUUID()
id1 := u.ID()
id2 := u.ID()
uuid1 := IDToUUID(id1)
uuid2 := IDToUUID(id2)

uassert.NotEqual(t, uuid1, uuid2)

idMap := make(map[uint64]bool)

for i := 0; i < 101; i++ {
id := u.ID()
if _, exists := idMap[id]; exists {
t.Errorf("Duplicate ID found: %d", id)
}
idMap[id] = true
uuidStr := IDToUUID(id)
t.Logf("Generated UUID %d: %s", i, uuidStr)
}
}

func TestGetEntropy(t *testing.T) {
u := NewUUID()
initialEntropyValue := u.entropy.Value()

for i := 0; i < 10; i++ {
u.ID()
}

uassert.NotEqual(t, initialEntropyValue, u.entropy.Value())
}
Loading