Skip to content

Commit 85161fb

Browse files
committed
✨ feat: add unify functions uuid #5
1 parent 83e18dd commit 85161fb

File tree

2 files changed

+212
-1
lines changed

2 files changed

+212
-1
lines changed

test/uuid_test.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package example_test
2+
3+
import (
4+
"sync"
5+
"testing"
6+
7+
"github.com/sivaosorg/unify4g"
8+
)
9+
10+
func TestGenerateUUID(t *testing.T) {
11+
var wg sync.WaitGroup
12+
numTests := 100
13+
uniqueIDs := make(map[string]struct{})
14+
mu := sync.Mutex{}
15+
16+
for i := 0; i < numTests; i++ {
17+
wg.Add(1)
18+
go func() {
19+
defer wg.Done()
20+
id, _ := unify4g.GenerateUUID()
21+
mu.Lock()
22+
if _, exists := uniqueIDs[id]; exists {
23+
t.Errorf("Duplicate ID generated: %s", id)
24+
}
25+
uniqueIDs[id] = struct{}{}
26+
mu.Unlock()
27+
}()
28+
}
29+
30+
wg.Wait()
31+
if len(uniqueIDs) != numTests {
32+
t.Errorf("Expected %d unique IDs but got %d", numTests, len(uniqueIDs))
33+
}
34+
}
35+
36+
func TestGenerateTimestampID(t *testing.T) {
37+
var wg sync.WaitGroup
38+
numTests := 100
39+
uniqueIDs := make(map[string]struct{})
40+
mu := sync.Mutex{}
41+
42+
for i := 0; i < numTests; i++ {
43+
wg.Add(1)
44+
go func() {
45+
defer wg.Done()
46+
id := unify4g.GenerateTimestampID()
47+
mu.Lock()
48+
if _, exists := uniqueIDs[id]; exists {
49+
t.Errorf("Duplicate ID generated: %s", id)
50+
}
51+
uniqueIDs[id] = struct{}{}
52+
mu.Unlock()
53+
}()
54+
}
55+
56+
wg.Wait()
57+
if len(uniqueIDs) != numTests {
58+
t.Errorf("Expected %d unique IDs but got %d", numTests, len(uniqueIDs))
59+
}
60+
}
61+
62+
// Recommended
63+
func TestGenerateCryptoID(t *testing.T) {
64+
var wg sync.WaitGroup
65+
numTests := 100
66+
uniqueIDs := make(map[string]struct{})
67+
mu := sync.Mutex{}
68+
69+
for i := 0; i < numTests; i++ {
70+
wg.Add(1)
71+
go func() {
72+
defer wg.Done()
73+
id := unify4g.GenerateCryptoID()
74+
mu.Lock()
75+
if _, exists := uniqueIDs[id]; exists {
76+
t.Errorf("Duplicate ID generated: %s", id)
77+
}
78+
uniqueIDs[id] = struct{}{}
79+
mu.Unlock()
80+
}()
81+
}
82+
83+
wg.Wait()
84+
if len(uniqueIDs) != numTests {
85+
t.Errorf("Expected %d unique IDs but got %d", numTests, len(uniqueIDs))
86+
}
87+
}

uuid.go

+125-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package unify4g
22

33
import (
4+
cr "crypto/rand"
5+
"encoding/hex"
46
"fmt"
7+
"log"
8+
"math/rand"
59
"os"
10+
"time"
611
)
712

813
// GenerateUUID generates a new universally unique identifier (UUID) using random data from /dev/urandom (Unix-based systems).
@@ -30,6 +35,37 @@ import (
3035
// - This function is designed for Unix-based systems. On non-Unix systems, this may not work because /dev/urandom
3136
// may not be available.
3237
func GenerateUUID() (string, error) {
38+
dash := "-"
39+
return GenerateUUIDDelimiter(dash)
40+
}
41+
42+
// GenerateUUIDDelimiter generates a new universally unique identifier (UUID) using random data from /dev/urandom
43+
// (Unix-based systems) with a customizable delimiter.
44+
//
45+
// This function is similar to GenerateUUID but allows the user to specify a custom delimiter to separate
46+
// different sections of the UUID. It opens the special file /dev/urandom to read 16 random bytes,
47+
// which are then used to construct a UUID. The UUID is returned as a string in the format:
48+
// XXXXXXXX<delimiter>XXXX<delimiter>XXXX<delimiter>XXXX<delimiter>XXXXXXXXXXXX, where X is a hexadecimal digit.
49+
//
50+
// Parameters:
51+
// - delimiter: A string used to separate sections of the UUID. Common choices are "-" or "" (no delimiter).
52+
//
53+
// Returns:
54+
// - A string representing the newly generated UUID with the specified delimiter.
55+
// - An error if there is an issue opening or reading from /dev/urandom.
56+
//
57+
// Example:
58+
//
59+
// uuid, err := GenerateUUIDDelimiter("-")
60+
// if err != nil {
61+
// log.Fatalf("Failed to generate UUID: %v", err)
62+
// }
63+
// fmt.Println("Generated UUID:", uuid)
64+
//
65+
// Notes:
66+
// - This function is designed for Unix-based systems. On non-Unix systems, it may not work because /dev/urandom
67+
// may not be available.
68+
func GenerateUUIDDelimiter(delimiter string) (string, error) {
3369
file, err := os.Open("/dev/urandom")
3470
if err != nil {
3571
return "", fmt.Errorf("open /dev/urandom error:[%v]", err)
@@ -44,6 +80,94 @@ func GenerateUUID() (string, error) {
4480
if err != nil {
4581
return "", err
4682
}
47-
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
83+
// Format the bytes as a UUID string with the specified delimiter.
84+
// The UUID is structured as XXXXXXXX<delimiter>XXXX<delimiter>XXXX<delimiter>XXXX<delimiter>XXXXXXXXXXXX.
85+
uuid := fmt.Sprintf("%x%s%x%s%x%s%x%s%x", b[0:4], delimiter, b[4:6], delimiter, b[6:8], delimiter, b[8:10], delimiter, b[10:])
4886
return uuid, nil
4987
}
88+
89+
// GenerateRandomID generates a random alphanumeric string of the specified length.
90+
// This string includes uppercase letters, lowercase letters, and numbers, making it
91+
// suitable for use as unique IDs or tokens.
92+
//
93+
// Parameters:
94+
// - length: The length of the random ID to generate. Must be a positive integer.
95+
//
96+
// Returns:
97+
// - A string of random alphanumeric characters with the specified length.
98+
//
99+
// The function uses a custom random source seeded with the current Unix timestamp
100+
// in nanoseconds to ensure that each call produces a unique sequence.
101+
// This function is intended to generate random strings quickly and is not
102+
// cryptographically secure.
103+
//
104+
// Example:
105+
//
106+
// id := GenerateRandomID(16)
107+
// fmt.Println("Generated Random ID:", id)
108+
//
109+
// Notes:
110+
// - This function is suitable for use cases where simple random IDs are needed.
111+
// However, for cryptographic purposes, consider using more secure random generation.
112+
func GenerateRandomID(length int) string {
113+
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
114+
seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) // Create a seeded random generator for unique results each call
115+
// Allocate a byte slice for the generated ID and populate it with random characters
116+
id := make([]byte, length)
117+
for i := range id {
118+
id[i] = charset[seededRand.Intn(len(charset))]
119+
}
120+
return string(id)
121+
}
122+
123+
// GenerateCryptoID generates a cryptographically secure random ID as a hexadecimal string.
124+
// It uses 16 random bytes, which are then encoded to a hexadecimal string for easy representation.
125+
//
126+
// Returns:
127+
// - A string representing a secure random hexadecimal ID of 32 characters (since 16 bytes are used, and each byte
128+
// is represented by two hexadecimal characters).
129+
//
130+
// The function uses crypto/rand.Read to ensure cryptographic security in the generated ID, making it suitable for
131+
// sensitive use cases such as API keys, session tokens, or any security-critical identifiers.
132+
//
133+
// Example:
134+
//
135+
// id := GenerateCryptoID()
136+
// fmt.Println("Generated Crypto ID:", id)
137+
//
138+
// Notes:
139+
// - This function is suitable for use cases where high security is required in the generated ID.
140+
// - It is not recommended for use cases where deterministic or non-cryptographic IDs are preferred.
141+
func GenerateCryptoID() string {
142+
bytes := make([]byte, 16)
143+
// Use crypto/rand.Read for cryptographically secure random byte generation.
144+
if _, err := cr.Read(bytes); err != nil {
145+
log.Fatalf("Failed to generate secure random bytes: %v", err)
146+
return ""
147+
}
148+
return hex.EncodeToString(bytes)
149+
}
150+
151+
// GenerateTimestampID generates a unique identifier based on the current Unix timestamp in nanoseconds,
152+
// with an additional random integer to enhance uniqueness.
153+
//
154+
// This function captures the current time in nanoseconds since the Unix epoch and appends a random integer
155+
// to ensure additional randomness and uniqueness, even if called in rapid succession. The result is returned
156+
// as a string. This type of ID is well-suited for time-based ordering and can be useful for generating
157+
// unique identifiers for logs, events, or non-cryptographic applications.
158+
//
159+
// Returns:
160+
// - A string representing the current Unix timestamp in nanoseconds, concatenated with a random integer.
161+
//
162+
// Example:
163+
//
164+
// id := GenerateTimestampID()
165+
// fmt.Println("Generated Timestamp ID:", id)
166+
//
167+
// Notes:
168+
// - This function provides a unique, time-ordered identifier, but it is not suitable for cryptographic use.
169+
// - The combination of the current time and a random integer is best suited for applications requiring
170+
// uniqueness and ordering, rather than secure identifiers.
171+
func GenerateTimestampID() string {
172+
return fmt.Sprintf("%d%d", time.Now().UnixNano(), nextInt())
173+
}

0 commit comments

Comments
 (0)