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 30 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
)
130 changes: 130 additions & 0 deletions examples/gno.land/p/demo/uuid/uuid.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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 42-bit.
// 42 it's the maximum value that can be returned "2^42", It's represent 139 years (2^42 secondes)
// SequenBits allows for 4096 unique IDs to be generated per millisecond.

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

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

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

const (
TimestampBits uint8 = 42
MachineIDBits uint64 = 10
SequenceBits uint64 = 12
MaxSequence uint16 = 1<<SequenceBits - 1
MaxTimestamp uint64 = 1<<TimestampBits - 1
MaxMachineID uint64 = 1<<MachineIDBits - 1

machineIDMoveLength = SequenceBits
timestampMoveLength = MachineIDBits + SequenceBits

startTime uint64 = 1226354400000 // 10 Nov 2008 23:00:00 UTC in milliseconds (Snowflake epoch)
)

// UUID struct for generating unique identifiers.
type UUID struct {
machineID uint64
sequence uint16
startTime uint64
lastTimestamp uint64
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.getTimenow()

if current == u.lastTimestamp {
u.incrementSequence()
} else {
u.sequence = 0
u.lastTimestamp = current
}

elapsedTime := uint64(current-u.startTime) % MaxTimestamp

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

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

func (u *UUID) getTimenow() uint64 {
return uint64(time.Now().UnixMilli())
}

// 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 UUIDIntToUUIDString(id uint64) string {
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
}
60 changes: 60 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,60 @@
package uuid

import (
"testing"

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

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

uassert.NotEqual(t, uuid1, uuid2)

idMap := make(map[uint64]bool)
uuidMap := make(map[string]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 := UUIDIntToUUIDString(id)
if _, exists := uuidMap[uuidStr]; exists {
t.Errorf("Duplicate UUID found: %s", uuidStr)
}
uuidMap[uuidStr] = true
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())
}

// Output:
// Generated UUID 0: 0007a63f-a542-4310-807a-63fa54253102
// Generated UUID 1: 0007a63f-a542-43fe-807a-63fa54253fe1
// Generated UUID 2: 0007a63f-a542-43e3-807a-63fa54253e34
// Generated UUID 3: 0007a63f-a542-432f-807a-63fa542532fb
// Generated UUID 4: 0007a63f-a542-4353-807a-63fa54253536
// Generated UUID 5: 0007a63f-a542-43be-807a-63fa54253be5
// Generated UUID 6: 0007a63f-a542-43e0-807a-63fa54253e08
// Generated UUID 7: 0007a63f-a542-4329-807a-63fa5425329f
// Generated UUID 8: 0007a63f-a542-430a-807a-63fa542530aa
// Generated UUID 9: 0007a63f-a542-43f2-807a-63fa54253f29
// Generated UUID 10: 0007a63f-a542-4351-807a-63fa5425351c
// .......
Loading