v / vlib / strconv
Raw file | 524 loc (480 sloc) | 9.86 KB | Latest commit hash 928dafeb6
1/*=============================================================================
2Copyright (c) 2019-2023 Dario Deledda. All rights reserved.
3Use of this source code is governed by an MIT license
4that can be found in the LICENSE file.
5
6This file contains string interpolation V functions
7=============================================================================*/
8module strconv
9
10import strings
11
12// format_str_sb is a `strings.Builder` version of `format_str`.
13pub fn format_str_sb(s string, p BF_param, mut sb strings.Builder) {
14 if p.len0 <= 0 {
15 sb.write_string(s)
16 return
17 }
18 dif := p.len0 - utf8_str_visible_length(s)
19 if dif <= 0 {
20 sb.write_string(s)
21 return
22 }
23
24 if p.allign == .right {
25 for i1 := 0; i1 < dif; i1++ {
26 sb.write_u8(p.pad_ch)
27 }
28 }
29 sb.write_string(s)
30 if p.allign == .left {
31 for i1 := 0; i1 < dif; i1++ {
32 sb.write_u8(p.pad_ch)
33 }
34 }
35}
36
37const (
38 max_size_f64_char = 32 // the f64 max representation is -36,028,797,018,963,968e1023, 21 chars, 32 is faster for the memory manger
39 // digit pairs in reverse order
40 digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999'
41)
42
43// format_dec_sb formats an u64 using a `strings.Builder`.
44[direct_array_access]
45pub fn format_dec_sb(d u64, p BF_param, mut res strings.Builder) {
46 mut n_char := dec_digits(d)
47 sign_len := if !p.positive || p.sign_flag { 1 } else { 0 }
48 number_len := sign_len + n_char
49 dif := p.len0 - number_len
50 mut sign_written := false
51
52 if p.allign == .right {
53 if p.pad_ch == `0` {
54 if p.positive {
55 if p.sign_flag {
56 res.write_u8(`+`)
57 sign_written = true
58 }
59 } else {
60 res.write_u8(`-`)
61 sign_written = true
62 }
63 }
64 // write the pad chars
65 for i1 := 0; i1 < dif; i1++ {
66 res.write_u8(p.pad_ch)
67 }
68 }
69
70 if !sign_written {
71 // no pad char, write the sign before the number
72 if p.positive {
73 if p.sign_flag {
74 res.write_u8(`+`)
75 }
76 } else {
77 res.write_u8(`-`)
78 }
79 }
80
81 /*
82 // Legacy version
83 // max u64 18446744073709551615 => 20 byte
84 mut buf := [32]u8{}
85 mut i := 20
86 mut d1 := d
87 for i >= (21 - n_char) {
88 buf[i] = u8(d1 % 10) + `0`
89 d1 = d1 / 10
90 i--
91 }
92 i++
93 */
94
95 //===========================================
96 // Speed version
97 // max u64 18446744073709551615 => 20 byte
98 mut buf := [32]u8{}
99 mut i := 20
100 mut n := d
101 mut d_i := u64(0)
102 if n > 0 {
103 for n > 0 {
104 n1 := n / 100
105 // calculate the digit_pairs start index
106 d_i = (n - (n1 * 100)) << 1
107 n = n1
108 unsafe {
109 buf[i] = strconv.digit_pairs.str[d_i]
110 }
111 i--
112 d_i++
113 unsafe {
114 buf[i] = strconv.digit_pairs.str[d_i]
115 }
116 i--
117 }
118 i++
119 // remove head zero
120 if d_i < 20 {
121 i++
122 }
123 unsafe { res.write_ptr(&buf[i], n_char) }
124 } else {
125 // we have a zero no need of more code!
126 res.write_u8(`0`)
127 }
128 //===========================================
129
130 if p.allign == .left {
131 for i1 := 0; i1 < dif; i1++ {
132 res.write_u8(p.pad_ch)
133 }
134 }
135 return
136}
137
138// f64_to_str_lnd1 formats a f64 to a `string` with `dec_digit` digits after the dot.
139[direct_array_access; manualfree]
140pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
141 unsafe {
142 // we add the rounding value
143 s := f64_to_str(f + dec_round[dec_digit], 18)
144 // check for +inf -inf Nan
145 if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
146 return s
147 }
148
149 m_sgn_flag := false
150 mut sgn := 1
151 mut b := [26]u8{}
152 mut d_pos := 1
153 mut i := 0
154 mut i1 := 0
155 mut exp := 0
156 mut exp_sgn := 1
157
158 mut dot_res_sp := -1
159
160 // get sign and decimal parts
161 for c in s {
162 match c {
163 `-` {
164 sgn = -1
165 i++
166 }
167 `+` {
168 sgn = 1
169 i++
170 }
171 `0`...`9` {
172 b[i1] = c
173 i1++
174 i++
175 }
176 `.` {
177 if sgn > 0 {
178 d_pos = i
179 } else {
180 d_pos = i - 1
181 }
182 i++
183 }
184 `e` {
185 i++
186 break
187 }
188 else {
189 s.free()
190 return '[Float conversion error!!]'
191 }
192 }
193 }
194 b[i1] = 0
195
196 // get exponent
197 if s[i] == `-` {
198 exp_sgn = -1
199 i++
200 } else if s[i] == `+` {
201 exp_sgn = 1
202 i++
203 }
204
205 mut c := i
206 for c < s.len {
207 exp = exp * 10 + int(s[c] - `0`)
208 c++
209 }
210
211 // allocate exp+32 chars for the return string
212 // mut res := []u8{len:exp+32,init:`0`}
213 mut res := []u8{len: exp + 32, init: 0}
214 mut r_i := 0 // result string buffer index
215
216 // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
217
218 // s no more needed
219 s.free()
220
221 if sgn == 1 {
222 if m_sgn_flag {
223 res[r_i] = `+`
224 r_i++
225 }
226 } else {
227 res[r_i] = `-`
228 r_i++
229 }
230
231 i = 0
232 if exp_sgn >= 0 {
233 for b[i] != 0 {
234 res[r_i] = b[i]
235 r_i++
236 i++
237 if i >= d_pos && exp >= 0 {
238 if exp == 0 {
239 dot_res_sp = r_i
240 res[r_i] = `.`
241 r_i++
242 }
243 exp--
244 }
245 }
246 for exp >= 0 {
247 res[r_i] = `0`
248 r_i++
249 exp--
250 }
251 // println("exp: $exp $r_i $dot_res_sp")
252 } else {
253 mut dot_p := true
254 for exp > 0 {
255 res[r_i] = `0`
256 r_i++
257 exp--
258 if dot_p {
259 dot_res_sp = r_i
260 res[r_i] = `.`
261 r_i++
262 dot_p = false
263 }
264 }
265 for b[i] != 0 {
266 res[r_i] = b[i]
267 r_i++
268 i++
269 }
270 }
271
272 // no more digits needed, stop here
273 if dec_digit <= 0 {
274 // C.printf(c'f: %f, i: %d, res.data: %p | dot_res_sp: %d | *(res.data): %s \n', f, i, res.data, dot_res_sp, res.data)
275 if dot_res_sp < 0 {
276 dot_res_sp = i + 1
277 }
278 tmp_res := tos(res.data, dot_res_sp).clone()
279 res.free()
280 return tmp_res
281 }
282
283 // println("r_i-d_pos: ${r_i - d_pos}")
284 if dot_res_sp >= 0 {
285 r_i = dot_res_sp + dec_digit + 1
286 res[r_i] = 0
287 for c1 in 1 .. dec_digit + 1 {
288 if res[r_i - c1] == 0 {
289 res[r_i - c1] = `0`
290 }
291 }
292 // println("result: [${tos(&res[0],r_i)}]")
293 tmp_res := tos(res.data, r_i).clone()
294 res.free()
295 return tmp_res
296 } else {
297 if dec_digit > 0 {
298 mut c1 := 0
299 res[r_i] = `.`
300 r_i++
301 for c1 < dec_digit {
302 res[r_i] = `0`
303 r_i++
304 c1++
305 }
306 res[r_i] = 0
307 }
308 tmp_res := tos(res.data, r_i).clone()
309 res.free()
310 return tmp_res
311 }
312 }
313}
314
315// format_fl is a `strings.Builder` version of format_fl.
316[direct_array_access; manualfree]
317pub fn format_fl(f f64, p BF_param) string {
318 unsafe {
319 mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1)
320
321 // error!!
322 if fs[0] == `[` {
323 return fs
324 }
325
326 if p.rm_tail_zero {
327 tmp := fs
328 fs = remove_tail_zeros(fs)
329 tmp.free()
330 }
331
332 mut buf := [strconv.max_size_f64_char]u8{} // write temp float buffer in stack
333 mut out := [strconv.max_size_f64_char]u8{} // out buffer
334 mut buf_i := 0 // index temporary string
335 mut out_i := 0 // index output string
336
337 mut sign_len_diff := 0
338 if p.pad_ch == `0` {
339 if p.positive {
340 if p.sign_flag {
341 out[out_i] = `+`
342 out_i++
343 sign_len_diff = -1
344 }
345 } else {
346 out[out_i] = `-`
347 out_i++
348 sign_len_diff = -1
349 }
350 } else {
351 if p.positive {
352 if p.sign_flag {
353 buf[buf_i] = `+`
354 buf_i++
355 }
356 } else {
357 buf[buf_i] = `-`
358 buf_i++
359 }
360 }
361
362 // copy the float
363 vmemcpy(&buf[buf_i], fs.str, fs.len)
364 buf_i += fs.len
365
366 // make the padding if needed
367 dif := p.len0 - buf_i + sign_len_diff
368 if p.allign == .right {
369 for i1 := 0; i1 < dif; i1++ {
370 out[out_i] = p.pad_ch
371 out_i++
372 }
373 }
374 vmemcpy(&out[out_i], &buf[0], buf_i)
375 out_i += buf_i
376 if p.allign == .left {
377 for i1 := 0; i1 < dif; i1++ {
378 out[out_i] = p.pad_ch
379 out_i++
380 }
381 }
382 out[out_i] = 0
383
384 // return and free
385 tmp := fs
386 fs = tos_clone(&out[0])
387 tmp.free()
388 return fs
389 }
390}
391
392// format_es returns a f64 as a `string` formatted according to the options set in `p`.
393[direct_array_access; manualfree]
394pub fn format_es(f f64, p BF_param) string {
395 unsafe {
396 mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1)
397 if p.rm_tail_zero {
398 tmp := fs
399 fs = remove_tail_zeros(fs)
400 tmp.free()
401 }
402
403 mut buf := [strconv.max_size_f64_char]u8{} // write temp float buffer in stack
404 mut out := [strconv.max_size_f64_char]u8{} // out buffer
405 mut buf_i := 0 // index temporary string
406 mut out_i := 0 // index output string
407
408 mut sign_len_diff := 0
409 if p.pad_ch == `0` {
410 if p.positive {
411 if p.sign_flag {
412 out[out_i] = `+`
413 out_i++
414 sign_len_diff = -1
415 }
416 } else {
417 out[out_i] = `-`
418 out_i++
419 sign_len_diff = -1
420 }
421 } else {
422 if p.positive {
423 if p.sign_flag {
424 buf[buf_i] = `+`
425 buf_i++
426 }
427 } else {
428 buf[buf_i] = `-`
429 buf_i++
430 }
431 }
432
433 // copy the float
434 vmemcpy(&buf[buf_i], fs.str, fs.len)
435 buf_i += fs.len
436
437 // make the padding if needed
438 dif := p.len0 - buf_i + sign_len_diff
439 if p.allign == .right {
440 for i1 := 0; i1 < dif; i1++ {
441 out[out_i] = p.pad_ch
442 out_i++
443 }
444 }
445 vmemcpy(&out[out_i], &buf[0], buf_i)
446 out_i += buf_i
447 if p.allign == .left {
448 for i1 := 0; i1 < dif; i1++ {
449 out[out_i] = p.pad_ch
450 out_i++
451 }
452 }
453 out[out_i] = 0
454
455 // return and free
456 tmp := fs
457 fs = tos_clone(&out[0])
458 tmp.free()
459 return fs
460 }
461}
462
463// remove_tail_zeros strips traling zeros from `s` and return the resulting `string`.
464[direct_array_access]
465pub fn remove_tail_zeros(s string) string {
466 unsafe {
467 mut buf := malloc_noscan(s.len + 1)
468 mut i_d := 0
469 mut i_s := 0
470
471 // skip spaces
472 for i_s < s.len && s[i_s] !in [`-`, `+`] && (s[i_s] > `9` || s[i_s] < `0`) {
473 buf[i_d] = s[i_s]
474 i_s++
475 i_d++
476 }
477 // sign
478 if i_s < s.len && s[i_s] in [`-`, `+`] {
479 buf[i_d] = s[i_s]
480 i_s++
481 i_d++
482 }
483
484 // integer part
485 for i_s < s.len && s[i_s] >= `0` && s[i_s] <= `9` {
486 buf[i_d] = s[i_s]
487 i_s++
488 i_d++
489 }
490
491 // check decimals
492 if i_s < s.len && s[i_s] == `.` {
493 mut i_s1 := i_s + 1
494 mut sum := 0
495 for i_s1 < s.len && s[i_s1] >= `0` && s[i_s1] <= `9` {
496 sum += s[i_s1] - u8(`0`)
497 i_s1++
498 }
499 // decimal part must be copied
500 if sum > 0 {
501 for c_i in i_s .. i_s1 {
502 buf[i_d] = s[c_i]
503 i_d++
504 }
505 }
506 i_s = i_s1
507 }
508
509 if i_s < s.len && s[i_s] != `.` {
510 // check exponent
511 for {
512 buf[i_d] = s[i_s]
513 i_s++
514 i_d++
515 if i_s >= s.len {
516 break
517 }
518 }
519 }
520
521 buf[i_d] = 0
522 return tos(buf, i_d)
523 }
524}