v / vlib / rand / rand.c.v
230 lines · 209 sloc · 5.51 KB · 8a36fe3fd82b73666e9d0d9e8294c59cced1b268
Raw
1module rand
2
3import time
4
5// uuid_v4 generates a random (v4) UUID.
6// See https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
7// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-4
8pub fn uuid_v4() string {
9 rand_1 := default_rng.u64()
10 rand_2 := default_rng.u64()
11 return internal_uuid(4, rand_1, rand_2)
12}
13
14@[direct_array_access; inline]
15fn internal_uuid(version u8, rand_1 u64, rand_2 u64) string {
16 // 0 1 2 3
17 // 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
18 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19 // | rand_1 |
20 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21 // | rand_1 | ver | rand_1 |
22 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23 // |var| rand_2 |
24 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25 // | rand_2 |
26 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27
28 mut parts := [8]u16{}
29 parts[0] = u16(rand_1 >> 48)
30 parts[1] = u16(rand_1 >> 32)
31 parts[2] = u16(rand_1 >> 16)
32 parts[3] = u16(rand_1)
33 parts[4] = u16(rand_2 >> 48)
34 parts[5] = u16(rand_2 >> 32)
35 parts[6] = u16(rand_2 >> 16)
36 parts[7] = u16(rand_2)
37
38 parts[3] = (parts[3] & 0x0FFF) | (u16(version) << 12) // set version
39 parts[4] = (parts[4] & 0x3FFF) | 0x8000 // set variant = 0b10
40
41 mut buf := unsafe { malloc_noscan(37) }
42 mut start := 0
43 unsafe {
44 for i in 0 .. 8 {
45 val := parts[i]
46 buf[start] = hex_chars[(val >> 12) & 0xF]
47 buf[start + 1] = hex_chars[(val >> 8) & 0xF]
48 buf[start + 2] = hex_chars[(val >> 4) & 0xF]
49 buf[start + 3] = hex_chars[val & 0xF]
50 start += 4
51 // insert `-` at specified locations
52 if start in [8, 13, 18, 23]! {
53 buf[start] = `-`
54 start++
55 }
56 }
57 buf[36] = 0
58 return buf.vstring_with_len(36)
59 }
60}
61
62// uuid_v7 generates a time-ordered (v7) UUID.
63// See https://datatracker.ietf.org/doc/html/rfc9562#name-uuid-version-7
64pub fn uuid_v7() string {
65 timestamp_48 := u64(time.now().unix_milli()) << 16
66 rand_1 := timestamp_48 | default_rng.u16()
67 rand_2 := default_rng.u64()
68 return internal_uuid(7, rand_1, rand_2)
69}
70
71pub struct UUIDSession {
72mut:
73 counter u8 // 6 bits session counter
74}
75
76// new_uuid_v7_session create a new session for generating uuid_v7.
77// The 12 bits `rand_a` in the RFC 9652, is replaced by 6 bits
78// sub-millisecond timestamp + 6 bits session counter.
79// See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=78c5e141e9c139fc2ff36a220334e4aa25e1b0eb
80pub fn new_uuid_v7_session() UUIDSession {
81 return UUIDSession{}
82}
83
84// next get a new uuid_v7 from current session.
85@[ignore_overflow]
86pub fn (mut u UUIDSession) next() string {
87 timestamp := u64(time.now().unix_nano())
88 // make place for holding 4 bits `version`
89 timestamp_shift_4bits := (timestamp & 0xFFFF_FFFF_FFFF_0000) | ((timestamp & 0x0000_0000_0000_FFFF) >> 4)
90 rand_1 := (timestamp_shift_4bits & 0xFFFF_FFFF_FFFF_FFC0) | u64(u.counter & 0x3F) // 6 bits session counter
91 rand_2 := default_rng.u64()
92
93 u.counter++
94
95 return internal_uuid(7, rand_1, rand_2)
96}
97
98const ulid_encoding = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
99
100@[direct_array_access]
101fn internal_ulid_at_millisecond(mut rng PRNG, unix_time_milli u64) string {
102 buflen := 26
103 mut buf := unsafe { malloc_noscan(27) }
104 mut t := unix_time_milli
105 mut i := 9
106 for i >= 0 {
107 unsafe {
108 buf[i] = ulid_encoding[int(t & 0x1F)]
109 }
110 t = t >> 5
111 i--
112 }
113 // first rand set
114 mut x := rng.u64()
115 i = 10
116 for i < 19 {
117 unsafe {
118 buf[i] = ulid_encoding[int(x & 0x1F)]
119 }
120 x = x >> 5
121 i++
122 }
123 // second rand set
124 x = rng.u64()
125 for i < 26 {
126 unsafe {
127 buf[i] = ulid_encoding[int(x & 0x1F)]
128 }
129 x = x >> 5
130 i++
131 }
132 unsafe {
133 buf[26] = 0
134 return buf.vstring_with_len(buflen)
135 }
136}
137
138@[direct_array_access]
139fn internal_string_from_set(mut rng PRNG, charset string, len int) string {
140 if len == 0 {
141 return ''
142 }
143 mut buf := unsafe { malloc_noscan(len + 1) }
144 for i in 0 .. len {
145 unsafe {
146 buf[i] = charset[rng.u32() % u32(charset.len)]
147 }
148 }
149 unsafe {
150 buf[len] = 0
151 }
152 return unsafe { buf.vstring_with_len(len) }
153}
154
155@[direct_array_access]
156fn internal_fill_buffer_from_set(mut rng PRNG, charset string, mut buf []u8) {
157 if buf.len == 0 {
158 return
159 }
160 blen := buf.len
161 for i in 0 .. blen {
162 unsafe {
163 buf[i] = charset[rng.u32() % u32(charset.len)]
164 }
165 }
166}
167
168fn deinit() {
169 unsafe {
170 default_rng.free() // free the implementation
171 free(default_rng) // free the interface wrapper itself
172 }
173}
174
175// init initializes the default RNG.
176fn init() {
177 default_rng = new_default()
178 at_exit(deinit) or {}
179}
180
181@[direct_array_access]
182fn read_32(mut rng PRNG, mut buf []u8) {
183 p32 := unsafe { &u32(buf.data) }
184 u32s := buf.len / 4
185 for i in 0 .. u32s {
186 unsafe {
187 *(p32 + i) = rng.u32()
188 }
189 }
190 for i in u32s * 4 .. buf.len {
191 buf[i] = rng.u8()
192 }
193}
194
195@[direct_array_access]
196fn read_64(mut rng PRNG, mut buf []u8) {
197 p64 := unsafe { &u64(buf.data) }
198 if u64(p64) & 0xF != 0 {
199 for i in 0 .. buf.len {
200 buf[i] = rng.u8()
201 }
202 return
203 }
204 u64s := buf.len / 8
205 for i in 0 .. u64s {
206 unsafe {
207 *(p64 + i) = rng.u64()
208 }
209 }
210 for i in u64s * 8 .. buf.len {
211 buf[i] = rng.u8()
212 }
213}
214
215@[direct_array_access]
216fn read_internal(mut rng PRNG, mut buf []u8) {
217 match rng.block_size() {
218 32 {
219 read_32(mut rng, mut buf)
220 }
221 64 {
222 read_64(mut rng, mut buf)
223 }
224 else {
225 for i in 0 .. buf.len {
226 buf[i] = rng.u8()
227 }
228 }
229 }
230}
231