1 | // Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved. |
2 | // Use of this source code is governed by an MIT license |
3 | // that can be found in the LICENSE file. |
4 | module builtin |
5 | |
6 | // |
7 | // ----- value to string functions ----- |
8 | // |
9 | |
10 | // type u8 = byte |
11 | type byte = u8 |
12 | type i32 = int |
13 | |
14 | // ptr_str returns the address of `ptr` as a `string`. |
15 | pub fn ptr_str(ptr voidptr) string { |
16 | buf1 := u64(ptr).hex() |
17 | return buf1 |
18 | } |
19 | |
20 | // pub fn nil_str(x voidptr) string { |
21 | // return 'nil' |
22 | //} |
23 | |
24 | // str returns string equivalent of x |
25 | pub fn (x isize) str() string { |
26 | return i64(x).str() |
27 | } |
28 | |
29 | // str returns string equivalent of x |
30 | pub fn (x usize) str() string { |
31 | return u64(x).str() |
32 | } |
33 | |
34 | // str returns string equivalent of cptr |
35 | pub fn (cptr &char) str() string { |
36 | return u64(cptr).hex() |
37 | } |
38 | |
39 | const ( |
40 | // digit pairs in reverse order |
41 | digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' |
42 | ) |
43 | |
44 | // This implementation is the quickest with gcc -O2 |
45 | // str_l returns the string representation of the integer nn with max chars. |
46 | [direct_array_access; inline] |
47 | fn (nn int) str_l(max int) string { |
48 | unsafe { |
49 | mut n := i64(nn) |
50 | mut d := 0 |
51 | if n == 0 { |
52 | return '0' |
53 | } |
54 | |
55 | mut is_neg := false |
56 | if n < 0 { |
57 | n = -n |
58 | is_neg = true |
59 | } |
60 | mut index := max |
61 | mut buf := malloc_noscan(max + 1) |
62 | buf[index] = 0 |
63 | index-- |
64 | |
65 | for n > 0 { |
66 | n1 := int(n / 100) |
67 | // calculate the digit_pairs start index |
68 | d = int(u32(int(n) - (n1 * 100)) << 1) |
69 | n = n1 |
70 | buf[index] = digit_pairs.str[d] |
71 | index-- |
72 | d++ |
73 | buf[index] = digit_pairs.str[d] |
74 | index-- |
75 | } |
76 | index++ |
77 | // remove head zero |
78 | if d < 20 { |
79 | index++ |
80 | } |
81 | // Prepend - if it's negative |
82 | if is_neg { |
83 | index-- |
84 | buf[index] = `-` |
85 | } |
86 | diff := max - index |
87 | vmemmove(buf, voidptr(buf + index), diff + 1) |
88 | /* |
89 | // === manual memory move for bare metal === |
90 | mut c:= 0 |
91 | for c < diff { |
92 | buf[c] = buf[c+index] |
93 | c++ |
94 | } |
95 | buf[c] = 0 |
96 | */ |
97 | return tos(buf, diff) |
98 | |
99 | // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) |
100 | } |
101 | } |
102 | |
103 | // str returns the value of the `i8` as a `string`. |
104 | // Example: assert i8(-2).str() == '-2' |
105 | pub fn (n i8) str() string { |
106 | return int(n).str_l(5) |
107 | } |
108 | |
109 | // str returns the value of the `i16` as a `string`. |
110 | // Example: assert i16(-20).str() == '-20' |
111 | pub fn (n i16) str() string { |
112 | return int(n).str_l(7) |
113 | } |
114 | |
115 | // str returns the value of the `u16` as a `string`. |
116 | // Example: assert u16(20).str() == '20' |
117 | pub fn (n u16) str() string { |
118 | return int(n).str_l(7) |
119 | } |
120 | |
121 | // str returns the value of the `int` as a `string`. |
122 | // Example: assert int(-2020).str() == '-2020' |
123 | pub fn (n int) str() string { |
124 | return n.str_l(12) |
125 | } |
126 | |
127 | // str returns the value of the `u32` as a `string`. |
128 | // Example: assert u32(20000).str() == '20000' |
129 | [direct_array_access; inline] |
130 | pub fn (nn u32) str() string { |
131 | unsafe { |
132 | mut n := nn |
133 | mut d := u32(0) |
134 | if n == 0 { |
135 | return '0' |
136 | } |
137 | max := 12 |
138 | mut buf := malloc_noscan(max + 1) |
139 | mut index := max |
140 | buf[index] = 0 |
141 | index-- |
142 | for n > 0 { |
143 | n1 := n / u32(100) |
144 | d = ((n - (n1 * u32(100))) << u32(1)) |
145 | n = n1 |
146 | buf[index] = digit_pairs[d] |
147 | index-- |
148 | d++ |
149 | buf[index] = digit_pairs[d] |
150 | index-- |
151 | } |
152 | index++ |
153 | // remove head zero |
154 | if d < u32(20) { |
155 | index++ |
156 | } |
157 | diff := max - index |
158 | vmemmove(buf, voidptr(buf + index), diff + 1) |
159 | return tos(buf, diff) |
160 | |
161 | // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) |
162 | } |
163 | } |
164 | |
165 | // str returns the value of the `int_literal` as a `string`. |
166 | [inline] |
167 | pub fn (n int_literal) str() string { |
168 | return i64(n).str() |
169 | } |
170 | |
171 | // str returns the value of the `i64` as a `string`. |
172 | // Example: assert i64(-200000).str() == '-200000' |
173 | [direct_array_access; inline] |
174 | pub fn (nn i64) str() string { |
175 | unsafe { |
176 | mut n := nn |
177 | mut d := i64(0) |
178 | if n == 0 { |
179 | return '0' |
180 | } else if n == i64(-9223372036854775807 - 1) { |
181 | // math.min_i64 |
182 | return '-9223372036854775808' |
183 | } |
184 | max := 20 |
185 | mut buf := malloc_noscan(max + 1) |
186 | mut is_neg := false |
187 | if n < 0 { |
188 | n = -n |
189 | is_neg = true |
190 | } |
191 | mut index := max |
192 | buf[index] = 0 |
193 | index-- |
194 | for n > 0 { |
195 | n1 := n / i64(100) |
196 | d = (u32(n - (n1 * i64(100))) << i64(1)) |
197 | n = n1 |
198 | buf[index] = digit_pairs[d] |
199 | index-- |
200 | d++ |
201 | buf[index] = digit_pairs[d] |
202 | index-- |
203 | } |
204 | index++ |
205 | // remove head zero |
206 | if d < i64(20) { |
207 | index++ |
208 | } |
209 | // Prepend - if it's negative |
210 | if is_neg { |
211 | index-- |
212 | buf[index] = `-` |
213 | } |
214 | diff := max - index |
215 | vmemmove(buf, voidptr(buf + index), diff + 1) |
216 | return tos(buf, diff) |
217 | // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) |
218 | } |
219 | } |
220 | |
221 | // str returns the value of the `u64` as a `string`. |
222 | // Example: assert u64(2000000).str() == '2000000' |
223 | [direct_array_access; inline] |
224 | pub fn (nn u64) str() string { |
225 | unsafe { |
226 | mut n := nn |
227 | mut d := u64(0) |
228 | if n == 0 { |
229 | return '0' |
230 | } |
231 | max := 20 |
232 | mut buf := malloc_noscan(max + 1) |
233 | mut index := max |
234 | buf[index] = 0 |
235 | index-- |
236 | for n > 0 { |
237 | n1 := n / 100 |
238 | d = ((n - (n1 * 100)) << 1) |
239 | n = n1 |
240 | buf[index] = digit_pairs[d] |
241 | index-- |
242 | d++ |
243 | buf[index] = digit_pairs[d] |
244 | index-- |
245 | } |
246 | index++ |
247 | // remove head zero |
248 | if d < 20 { |
249 | index++ |
250 | } |
251 | diff := max - index |
252 | vmemmove(buf, voidptr(buf + index), diff + 1) |
253 | return tos(buf, diff) |
254 | // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) |
255 | } |
256 | } |
257 | |
258 | // str returns the value of the `bool` as a `string`. |
259 | // Example: assert (2 > 1).str() == 'true' |
260 | pub fn (b bool) str() string { |
261 | if b { |
262 | return 'true' |
263 | } |
264 | return 'false' |
265 | } |
266 | |
267 | // |
268 | // ----- value to hex string functions ----- |
269 | // |
270 | |
271 | // u64_to_hex converts the number `nn` to a (zero padded if necessary) hexadecimal `string`. |
272 | [direct_array_access; inline] |
273 | fn u64_to_hex(nn u64, len u8) string { |
274 | mut n := nn |
275 | mut buf := [17]u8{} |
276 | buf[len] = 0 |
277 | mut i := 0 |
278 | for i = len - 1; i >= 0; i-- { |
279 | d := u8(n & 0xF) |
280 | buf[i] = if d < 10 { d + `0` } else { d + 87 } |
281 | n = n >> 4 |
282 | } |
283 | return unsafe { tos(memdup(&buf[0], len + 1), len) } |
284 | } |
285 | |
286 | // u64_to_hex_no_leading_zeros converts the number `nn` to hexadecimal `string`. |
287 | [direct_array_access; inline] |
288 | fn u64_to_hex_no_leading_zeros(nn u64, len u8) string { |
289 | mut n := nn |
290 | mut buf := [17]u8{} |
291 | buf[len] = 0 |
292 | mut i := 0 |
293 | for i = len - 1; i >= 0; i-- { |
294 | d := u8(n & 0xF) |
295 | buf[i] = if d < 10 { d + `0` } else { d + 87 } |
296 | n = n >> 4 |
297 | if n == 0 { |
298 | break |
299 | } |
300 | } |
301 | res_len := len - i |
302 | return unsafe { tos(memdup(&buf[i], res_len + 1), res_len) } |
303 | } |
304 | |
305 | // hex returns the value of the `byte` as a hexadecimal `string`. |
306 | // Note that the output is zero padded for values below 16. |
307 | // Example: assert u8(2).hex() == '02' |
308 | // Example: assert u8(15).hex() == '0f' |
309 | // Example: assert u8(255).hex() == 'ff' |
310 | pub fn (nn u8) hex() string { |
311 | if nn == 0 { |
312 | return '00' |
313 | } |
314 | return u64_to_hex(nn, 2) |
315 | } |
316 | |
317 | // hex returns the value of the `i8` as a hexadecimal `string`. |
318 | // Note that the output is zero padded for values below 16. |
319 | // Example: assert i8(8).hex() == '08' |
320 | // Example: assert i8(10).hex() == '0a' |
321 | // Example: assert i8(15).hex() == '0f' |
322 | pub fn (nn i8) hex() string { |
323 | if nn == 0 { |
324 | return '00' |
325 | } |
326 | return u64_to_hex(u64(nn), 2) |
327 | } |
328 | |
329 | // hex returns the value of the `u16` as a hexadecimal `string`. |
330 | // Note that the output is ***not*** zero padded. |
331 | // Example: assert u16(2).hex() == '2' |
332 | // Example: assert u16(200).hex() == 'c8' |
333 | pub fn (nn u16) hex() string { |
334 | if nn == 0 { |
335 | return '0' |
336 | } |
337 | return u64_to_hex_no_leading_zeros(nn, 4) |
338 | } |
339 | |
340 | // hex returns the value of the `i16` as a hexadecimal `string`. |
341 | // Note that the output is ***not*** zero padded. |
342 | // Example: assert i16(2).hex() == '2' |
343 | // Example: assert i16(200).hex() == 'c8' |
344 | pub fn (nn i16) hex() string { |
345 | return u16(nn).hex() |
346 | } |
347 | |
348 | // hex returns the value of the `u32` as a hexadecimal `string`. |
349 | // Note that the output is ***not*** zero padded. |
350 | // Example: assert u32(2).hex() == '2' |
351 | // Example: assert u32(200).hex() == 'c8' |
352 | pub fn (nn u32) hex() string { |
353 | if nn == 0 { |
354 | return '0' |
355 | } |
356 | return u64_to_hex_no_leading_zeros(nn, 8) |
357 | } |
358 | |
359 | // hex returns the value of the `int` as a hexadecimal `string`. |
360 | // Note that the output is ***not*** zero padded. |
361 | // Example: assert int(2).hex() == '2' |
362 | // Example: assert int(200).hex() == 'c8' |
363 | pub fn (nn int) hex() string { |
364 | return u32(nn).hex() |
365 | } |
366 | |
367 | // hex2 returns the value of the `int` as a `0x`-prefixed hexadecimal `string`. |
368 | // Note that the output after `0x` is ***not*** zero padded. |
369 | // Example: assert int(8).hex2() == '0x8' |
370 | // Example: assert int(15).hex2() == '0xf' |
371 | // Example: assert int(18).hex2() == '0x12' |
372 | pub fn (n int) hex2() string { |
373 | return '0x' + n.hex() |
374 | } |
375 | |
376 | // hex returns the value of the `u64` as a hexadecimal `string`. |
377 | // Note that the output is ***not*** zero padded. |
378 | // Example: assert u64(2).hex() == '2' |
379 | // Example: assert u64(2000).hex() == '7d0' |
380 | pub fn (nn u64) hex() string { |
381 | if nn == 0 { |
382 | return '0' |
383 | } |
384 | return u64_to_hex_no_leading_zeros(nn, 16) |
385 | } |
386 | |
387 | // hex returns the value of the `i64` as a hexadecimal `string`. |
388 | // Note that the output is ***not*** zero padded. |
389 | // Example: assert i64(2).hex() == '2' |
390 | // Example: assert i64(-200).hex() == 'ffffffffffffff38' |
391 | // Example: assert i64(2021).hex() == '7e5' |
392 | pub fn (nn i64) hex() string { |
393 | return u64(nn).hex() |
394 | } |
395 | |
396 | // hex returns the value of the `int_literal` as a hexadecimal `string`. |
397 | // Note that the output is ***not*** zero padded. |
398 | pub fn (nn int_literal) hex() string { |
399 | return u64(nn).hex() |
400 | } |
401 | |
402 | // hex returns the value of the `voidptr` as a hexadecimal `string`. |
403 | // Note that the output is ***not*** zero padded. |
404 | pub fn (nn voidptr) str() string { |
405 | return '0x' + u64(nn).hex() |
406 | } |
407 | |
408 | // hex returns the value of the `byteptr` as a hexadecimal `string`. |
409 | // Note that the output is ***not*** zero padded. |
410 | // pub fn (nn byteptr) str() string { |
411 | pub fn (nn byteptr) str() string { |
412 | return '0x' + u64(nn).hex() |
413 | } |
414 | |
415 | pub fn (nn charptr) str() string { |
416 | return '0x' + u64(nn).hex() |
417 | } |
418 | |
419 | pub fn (nn u8) hex_full() string { |
420 | return u64_to_hex(u64(nn), 2) |
421 | } |
422 | |
423 | pub fn (nn i8) hex_full() string { |
424 | return u64_to_hex(u64(nn), 2) |
425 | } |
426 | |
427 | pub fn (nn u16) hex_full() string { |
428 | return u64_to_hex(u64(nn), 4) |
429 | } |
430 | |
431 | pub fn (nn i16) hex_full() string { |
432 | return u64_to_hex(u64(nn), 4) |
433 | } |
434 | |
435 | pub fn (nn u32) hex_full() string { |
436 | return u64_to_hex(u64(nn), 8) |
437 | } |
438 | |
439 | pub fn (nn int) hex_full() string { |
440 | return u64_to_hex(u64(nn), 8) |
441 | } |
442 | |
443 | pub fn (nn i64) hex_full() string { |
444 | return u64_to_hex(u64(nn), 16) |
445 | } |
446 | |
447 | pub fn (nn voidptr) hex_full() string { |
448 | return u64_to_hex(u64(nn), 16) |
449 | } |
450 | |
451 | pub fn (nn int_literal) hex_full() string { |
452 | return u64_to_hex(u64(nn), 16) |
453 | } |
454 | |
455 | // hex_full returns the value of the `u64` as a *full* 16-digit hexadecimal `string`. |
456 | // Example: assert u64(2).hex_full() == '0000000000000002' |
457 | // Example: assert u64(255).hex_full() == '00000000000000ff' |
458 | pub fn (nn u64) hex_full() string { |
459 | return u64_to_hex(nn, 16) |
460 | } |
461 | |
462 | // str returns the contents of `byte` as a zero terminated `string`. |
463 | // See also: [`byte.ascii_str`](#byte.ascii_str) |
464 | // Example: assert u8(111).str() == '111' |
465 | pub fn (b u8) str() string { |
466 | return int(b).str_l(7) |
467 | } |
468 | |
469 | // ascii_str returns the contents of `byte` as a zero terminated ASCII `string` character. |
470 | // Example: assert u8(97).ascii_str() == 'a' |
471 | pub fn (b u8) ascii_str() string { |
472 | mut str := string{ |
473 | str: unsafe { malloc_noscan(2) } |
474 | len: 1 |
475 | } |
476 | unsafe { |
477 | str.str[0] = b |
478 | str.str[1] = 0 |
479 | } |
480 | // println(str) |
481 | return str |
482 | } |
483 | |
484 | // str_escaped returns the contents of `byte` as an escaped `string`. |
485 | // Example: assert u8(0).str_escaped() == r'`\0`' |
486 | [manualfree] |
487 | pub fn (b u8) str_escaped() string { |
488 | str := match b { |
489 | 0 { |
490 | r'`\0`' |
491 | } |
492 | 7 { |
493 | r'`\a`' |
494 | } |
495 | 8 { |
496 | r'`\b`' |
497 | } |
498 | 9 { |
499 | r'`\t`' |
500 | } |
501 | 10 { |
502 | r'`\n`' |
503 | } |
504 | 11 { |
505 | r'`\v`' |
506 | } |
507 | 12 { |
508 | r'`\f`' |
509 | } |
510 | 13 { |
511 | r'`\r`' |
512 | } |
513 | 27 { |
514 | r'`\e`' |
515 | } |
516 | 32...126 { |
517 | b.ascii_str() |
518 | } |
519 | else { |
520 | xx := b.hex() |
521 | yy := '0x' + xx |
522 | unsafe { xx.free() } |
523 | yy |
524 | } |
525 | } |
526 | return str |
527 | } |
528 | |
529 | // is_capital returns `true`, if the byte is a Latin capital letter. |
530 | // Example: assert `H`.is_capital() == true |
531 | // Example: assert `h`.is_capital() == false |
532 | [inline] |
533 | pub fn (c u8) is_capital() bool { |
534 | return c >= `A` && c <= `Z` |
535 | } |
536 | |
537 | // clone clones the byte array, and returns the newly created copy. |
538 | pub fn (b []u8) clone() []u8 { |
539 | mut res := []u8{len: b.len} |
540 | // mut res := make([]u8, {repeat:b.len}) |
541 | for i in 0 .. b.len { |
542 | res[i] = b[i] |
543 | } |
544 | return res |
545 | } |
546 | |
547 | // bytestr produces a string from *all* the bytes in the array. |
548 | // Note: the returned string will have .len equal to the array.len, |
549 | // even when some of the array bytes were `0`. |
550 | // If you want to get a V string, that contains only the bytes till |
551 | // the first `0` byte, use `tos_clone(&u8(array.data))` instead. |
552 | pub fn (b []u8) bytestr() string { |
553 | unsafe { |
554 | buf := malloc_noscan(b.len + 1) |
555 | vmemcpy(buf, b.data, b.len) |
556 | buf[b.len] = 0 |
557 | return tos(buf, b.len) |
558 | } |
559 | } |
560 | |
561 | // byterune attempts to decode a sequence of bytes |
562 | // from utf8 to utf32 and return the result as a rune |
563 | // it will produce an error if there are more than |
564 | // four bytes in the array. |
565 | pub fn (b []u8) byterune() !rune { |
566 | r := b.utf8_to_utf32()! |
567 | return rune(r) |
568 | } |
569 | |
570 | // repeat returns a new string with `count` number of copies of the byte it was called on. |
571 | pub fn (b u8) repeat(count int) string { |
572 | if count < 0 { |
573 | panic('byte.repeat: count is negative: ${count}') |
574 | } else if count == 0 { |
575 | return '' |
576 | } else if count == 1 { |
577 | return b.ascii_str() |
578 | } |
579 | mut ret := unsafe { malloc_noscan(count + 1) } |
580 | for i in 0 .. count { |
581 | unsafe { |
582 | ret[i] = b |
583 | } |
584 | } |
585 | new_len := count |
586 | unsafe { |
587 | ret[new_len] = 0 |
588 | } |
589 | return unsafe { ret.vstring_with_len(new_len) } |
590 | } |
591 | |
592 | // for atomic ints, internal |
593 | fn _Atomic__int_str(x int) string { |
594 | return x.str() |
595 | } |