mirror of
https://github.com/vlang/v.git
synced 2025-08-03 09:47:15 -04:00
224 lines
5.4 KiB
V
224 lines
5.4 KiB
V
module rand
|
|
|
|
import time
|
|
|
|
// uuid_v4 generates a random (v4) UUID.
|
|
// See https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
|
|
// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4
|
|
pub fn uuid_v4() string {
|
|
rand_1 := default_rng.u64()
|
|
rand_2 := default_rng.u64()
|
|
return internal_uuid(4, rand_1, rand_2)
|
|
}
|
|
|
|
@[direct_array_access; inline]
|
|
fn internal_uuid(version u8, rand_1 u64, rand_2 u64) string {
|
|
// 0 1 2 3
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | rand_1 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | rand_1 | ver | rand_1 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// |var| rand_2 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | rand_2 |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
mut parts := [8]u16{}
|
|
parts[0] = u16(rand_1 >> 48)
|
|
parts[1] = u16(rand_1 >> 32)
|
|
parts[2] = u16(rand_1 >> 16)
|
|
parts[3] = u16(rand_1)
|
|
parts[4] = u16(rand_2 >> 48)
|
|
parts[5] = u16(rand_2 >> 32)
|
|
parts[6] = u16(rand_2 >> 16)
|
|
parts[7] = u16(rand_2)
|
|
|
|
parts[3] = (parts[3] & 0x0FFF) | (u16(version) << 12) // set version
|
|
parts[4] = (parts[4] & 0x3FFF) | 0x8000 // set variant = 0b10
|
|
|
|
mut buf := unsafe { malloc_noscan(37) }
|
|
mut start := 0
|
|
unsafe {
|
|
for i in 0 .. 8 {
|
|
val := parts[i]
|
|
buf[start] = hex_chars[(val >> 12) & 0xF]
|
|
buf[start + 1] = hex_chars[(val >> 8) & 0xF]
|
|
buf[start + 2] = hex_chars[(val >> 4) & 0xF]
|
|
buf[start + 3] = hex_chars[val & 0xF]
|
|
start += 4
|
|
// insert `-` at specified locations
|
|
if start in [8, 13, 18, 23]! {
|
|
buf[start] = `-`
|
|
start++
|
|
}
|
|
}
|
|
buf[36] = 0
|
|
return buf.vstring_with_len(36)
|
|
}
|
|
}
|
|
|
|
// uuid_v7 generates a time-ordered (v7) UUID.
|
|
// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7
|
|
pub fn uuid_v7() string {
|
|
timestamp_48 := u64(time.now().unix_milli()) << 16
|
|
rand_1 := timestamp_48 | default_rng.u16()
|
|
rand_2 := default_rng.u64()
|
|
return internal_uuid(7, rand_1, rand_2)
|
|
}
|
|
|
|
pub struct UUIDSession {
|
|
mut:
|
|
counter u8 // 6 bits session counter
|
|
}
|
|
|
|
// new_uuid_v7_session create a new session for generating uuid_v7.
|
|
// The 12 bits `rand_a` in the RFC 9652, is replaced by 6 bits
|
|
// sub-millisecond timestamp + 6 bits session counter.
|
|
// See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=78c5e141e9c139fc2ff36a220334e4aa25e1b0eb
|
|
pub fn new_uuid_v7_session() UUIDSession {
|
|
return UUIDSession{}
|
|
}
|
|
|
|
// next get a new uuid_v7 from current session.
|
|
pub fn (mut u UUIDSession) next() string {
|
|
timestamp := u64(time.now().unix_nano())
|
|
// make place for holding 4 bits `version`
|
|
timestamp_shift_4bits := (timestamp & 0xFFFF_FFFF_FFFF_0000) | ((timestamp & 0x0000_0000_0000_FFFF) >> 4)
|
|
rand_1 := (timestamp_shift_4bits & 0xFFFF_FFFF_FFFF_FFC0) | u64(u.counter & 0x3F) // 6 bits session counter
|
|
rand_2 := default_rng.u64()
|
|
|
|
u.counter++
|
|
|
|
return internal_uuid(7, rand_1, rand_2)
|
|
}
|
|
|
|
const ulid_encoding = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
|
|
|
@[direct_array_access]
|
|
fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string {
|
|
buflen := 26
|
|
mut buf := unsafe { malloc_noscan(27) }
|
|
mut t := unix_time_milli
|
|
mut i := 9
|
|
for i >= 0 {
|
|
unsafe {
|
|
buf[i] = ulid_encoding[t & 0x1F]
|
|
}
|
|
t = t >> 5
|
|
i--
|
|
}
|
|
// first rand set
|
|
mut x := rng.u64()
|
|
i = 10
|
|
for i < 19 {
|
|
unsafe {
|
|
buf[i] = ulid_encoding[x & 0x1F]
|
|
}
|
|
x = x >> 5
|
|
i++
|
|
}
|
|
// second rand set
|
|
x = rng.u64()
|
|
for i < 26 {
|
|
unsafe {
|
|
buf[i] = ulid_encoding[x & 0x1F]
|
|
}
|
|
x = x >> 5
|
|
i++
|
|
}
|
|
unsafe {
|
|
buf[26] = 0
|
|
return buf.vstring_with_len(buflen)
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn internal_string_from_set(mut rng PRNG, charset string, len int) string {
|
|
if len == 0 {
|
|
return ''
|
|
}
|
|
mut buf := unsafe { malloc_noscan(len + 1) }
|
|
for i in 0 .. len {
|
|
unsafe {
|
|
buf[i] = charset[rng.u32() % u32(charset.len)]
|
|
}
|
|
}
|
|
unsafe {
|
|
buf[len] = 0
|
|
}
|
|
return unsafe { buf.vstring_with_len(len) }
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn internal_fill_buffer_from_set(mut rng PRNG, charset string, mut buf []u8) {
|
|
if buf.len == 0 {
|
|
return
|
|
}
|
|
blen := buf.len
|
|
for i in 0 .. blen {
|
|
unsafe {
|
|
buf[i] = charset[rng.u32() % u32(charset.len)]
|
|
}
|
|
}
|
|
}
|
|
|
|
fn deinit() {
|
|
unsafe {
|
|
default_rng.free() // free the implementation
|
|
free(default_rng) // free the interface wrapper itself
|
|
}
|
|
}
|
|
|
|
// init initializes the default RNG.
|
|
fn init() {
|
|
default_rng = new_default()
|
|
at_exit(deinit) or {}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn read_32(mut rng PRNG, mut buf []u8) {
|
|
p32 := unsafe { &u32(buf.data) }
|
|
u32s := buf.len / 4
|
|
for i in 0 .. u32s {
|
|
unsafe {
|
|
*(p32 + i) = rng.u32()
|
|
}
|
|
}
|
|
for i in u32s * 4 .. buf.len {
|
|
buf[i] = rng.u8()
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn read_64(mut rng PRNG, mut buf []u8) {
|
|
p64 := unsafe { &u64(buf.data) }
|
|
u64s := buf.len / 8
|
|
for i in 0 .. u64s {
|
|
unsafe {
|
|
*(p64 + i) = rng.u64()
|
|
}
|
|
}
|
|
for i in u64s * 8 .. buf.len {
|
|
buf[i] = rng.u8()
|
|
}
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn read_internal(mut rng PRNG, mut buf []u8) {
|
|
match rng.block_size() {
|
|
32 {
|
|
read_32(mut rng, mut buf)
|
|
}
|
|
64 {
|
|
read_64(mut rng, mut buf)
|
|
}
|
|
else {
|
|
for i in 0 .. buf.len {
|
|
buf[i] = rng.u8()
|
|
}
|
|
}
|
|
}
|
|
}
|