1 | /*============================================================================= |
2 | Copyright (c) 2019-2023 Dario Deledda. All rights reserved. |
3 | Use of this source code is governed by an MIT license |
4 | that can be found in the LICENSE file. |
5 | |
6 | This file contains string interpolation V functions |
7 | =============================================================================*/ |
8 | module strconv |
9 | |
10 | import strings |
11 | |
12 | // format_str_sb is a `strings.Builder` version of `format_str`. |
13 | pub 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 | |
37 | const ( |
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] |
45 | pub 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] |
140 | pub 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] |
317 | pub 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] |
394 | pub 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] |
465 | pub 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 | } |