v / vlib / strconv / format_mem.c.v
530 lines · 485 sloc · 10.18 KB · eeaaff218bbe695c655a6380963e2f82deadcb94
Raw
1/*=============================================================================
2Copyright (c) 2019-2024 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.align == .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.align == .left {
31 for i1 := 0; i1 < dif; i1++ {
32 sb.write_u8(p.pad_ch)
33 }
34 }
35}
36
37const max_size_f64_char = 512 // the f64 max representation is -36,028,797,018,963,968e1023, 21 chars, but alignment padding requires more
38
39// digit pairs in reverse order
40const digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999'
41
42// format_dec_sb formats an u64 using a `strings.Builder`.
43@[direct_array_access]
44pub fn format_dec_sb(d u64, p BF_param, mut res strings.Builder) {
45 mut n_char := dec_digits(d)
46 sign_len := if !p.positive || p.sign_flag { 1 } else { 0 }
47 number_len := sign_len + n_char
48 dif := p.len0 - number_len
49 mut sign_written := false
50
51 if p.align == .right {
52 if p.pad_ch == `0` {
53 if p.positive {
54 if p.sign_flag {
55 res.write_u8(`+`)
56 sign_written = true
57 }
58 } else {
59 res.write_u8(`-`)
60 sign_written = true
61 }
62 }
63 // write the pad chars
64 for i1 := 0; i1 < dif; i1++ {
65 res.write_u8(p.pad_ch)
66 }
67 }
68
69 if !sign_written {
70 // no pad char, write the sign before the number
71 if p.positive {
72 if p.sign_flag {
73 res.write_u8(`+`)
74 }
75 } else {
76 res.write_u8(`-`)
77 }
78 }
79
80 /*
81 // Legacy version
82 // max u64 18446744073709551615 => 20 byte
83 mut buf := [32]u8{}
84 mut i := 20
85 mut d1 := d
86 for i >= (21 - n_char) {
87 buf[i] = u8(d1 % 10) + `0`
88 d1 = d1 / 10
89 i--
90 }
91 i++
92 */
93
94 //===========================================
95 // Speed version
96 // max u64 18446744073709551615 => 20 byte
97 mut buf := [32]u8{}
98 mut i := 20
99 mut n := d
100 mut d_i := u64(0)
101 if n > 0 {
102 for n > 0 {
103 n1 := n / 100
104 // calculate the digit_pairs start index
105 d_i = (n - (n1 * 100)) << 1
106 n = n1
107 unsafe {
108 buf[i] = digit_pairs.str[d_i]
109 }
110 i--
111 d_i++
112 unsafe {
113 buf[i] = digit_pairs.str[d_i]
114 }
115 i--
116 }
117 i++
118 // remove head zero
119 if d_i < 20 {
120 i++
121 }
122 unsafe { res.write_ptr(&buf[i], n_char) }
123 } else {
124 // we have a zero no need of more code!
125 res.write_u8(`0`)
126 }
127 //===========================================
128
129 if p.align == .left {
130 for i1 := 0; i1 < dif; i1++ {
131 res.write_u8(p.pad_ch)
132 }
133 }
134 return
135}
136
137// f64_to_str_lnd1 formats a f64 to a `string` with `dec_digit` digits after the dot.
138@[direct_array_access; manualfree]
139pub fn f64_to_str_lnd1(f f64, dec_digit int) string {
140 unsafe {
141 // we add the rounding value
142 clamped_dec := if dec_digit >= dec_round.len { dec_round.len - 1 } else { dec_digit }
143 s := f64_to_str(f + dec_round[clamped_dec], 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 // Reserve enough space for the current digits, exponent-driven zeros,
212 // requested fractional padding, and the trailing NUL.
213 extra_frac_digits := if dec_digit > 0 { dec_digit } else { 0 }
214 sign_len := if sgn < 0 { 1 } else { 0 }
215 mut res := []u8{len: sign_len + i1 + exp + extra_frac_digits + 4, init: 0}
216 mut r_i := 0 // result string buffer index
217
218 // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
219
220 // s no more needed
221 s.free()
222
223 if sgn == 1 {
224 if m_sgn_flag {
225 res[r_i] = `+`
226 r_i++
227 }
228 } else {
229 res[r_i] = `-`
230 r_i++
231 }
232
233 i = 0
234 if exp_sgn >= 0 {
235 for b[i] != 0 {
236 res[r_i] = b[i]
237 r_i++
238 i++
239 if i >= d_pos && exp >= 0 {
240 if exp == 0 {
241 dot_res_sp = r_i
242 res[r_i] = `.`
243 r_i++
244 }
245 exp--
246 }
247 }
248 for exp >= 0 {
249 res[r_i] = `0`
250 r_i++
251 exp--
252 }
253 // println("exp: ${exp} ${r_i} ${dot_res_sp}")
254 } else {
255 mut dot_p := true
256 for exp > 0 {
257 res[r_i] = `0`
258 r_i++
259 exp--
260 if dot_p {
261 dot_res_sp = r_i
262 res[r_i] = `.`
263 r_i++
264 dot_p = false
265 }
266 }
267 for b[i] != 0 {
268 res[r_i] = b[i]
269 r_i++
270 i++
271 }
272 }
273
274 // no more digits needed, stop here
275 if dec_digit <= 0 {
276 // 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)
277 if dot_res_sp < 0 {
278 dot_res_sp = i + 1
279 }
280 tmp_res := tos(res.data, dot_res_sp).clone()
281 res.free()
282 return tmp_res
283 }
284
285 // println("r_i-d_pos: ${r_i - d_pos}")
286 if dot_res_sp >= 0 {
287 r_i = dot_res_sp + dec_digit + 1
288 res[r_i] = 0
289 for c1 in 1 .. dec_digit + 1 {
290 if res[r_i - c1] == 0 {
291 res[r_i - c1] = `0`
292 }
293 }
294 // println("result: [${tos(&res[0],r_i)}]")
295 tmp_res := tos(res.data, r_i).clone()
296 res.free()
297 return tmp_res
298 } else {
299 if dec_digit > 0 {
300 mut c1 := 0
301 res[r_i] = `.`
302 r_i++
303 for c1 < dec_digit {
304 res[r_i] = `0`
305 r_i++
306 c1++
307 }
308 res[r_i] = 0
309 }
310 tmp_res := tos(res.data, r_i).clone()
311 res.free()
312 return tmp_res
313 }
314 }
315}
316
317// format_fl is a `strings.Builder` version of format_fl.
318@[direct_array_access; manualfree]
319pub fn format_fl(f f64, p BF_param) string {
320 unsafe {
321 mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1)
322
323 // error!!
324 if fs[0] == `[` {
325 return fs
326 }
327
328 if p.rm_tail_zero {
329 tmp := fs
330 fs = remove_tail_zeros(fs)
331 tmp.free()
332 }
333
334 mut buf := [max_size_f64_char]u8{} // write temp float buffer in stack
335 mut out := [max_size_f64_char]u8{} // out buffer
336 mut buf_i := 0 // index temporary string
337 mut out_i := 0 // index output string
338
339 mut sign_len_diff := 0
340 if p.pad_ch == `0` {
341 if p.positive {
342 if p.sign_flag {
343 out[out_i] = `+`
344 out_i++
345 sign_len_diff = -1
346 }
347 } else {
348 out[out_i] = `-`
349 out_i++
350 sign_len_diff = -1
351 }
352 } else {
353 if p.positive {
354 if p.sign_flag {
355 buf[buf_i] = `+`
356 buf_i++
357 }
358 } else {
359 buf[buf_i] = `-`
360 buf_i++
361 }
362 }
363
364 // copy the float
365 vmemcpy(&buf[buf_i], fs.str, fs.len)
366 buf_i += fs.len
367
368 // make the padding if needed
369 dif := p.len0 - buf_i + sign_len_diff
370 if p.align == .right {
371 for i1 := 0; i1 < dif; i1++ {
372 out[out_i] = p.pad_ch
373 out_i++
374 }
375 }
376 vmemcpy(&out[out_i], &buf[0], buf_i)
377 out_i += buf_i
378 if p.align == .left {
379 for i1 := 0; i1 < dif; i1++ {
380 out[out_i] = p.pad_ch
381 out_i++
382 }
383 }
384 out[out_i] = 0
385
386 // return and free
387 tmp := fs
388 fs = tos_clone(&out[0])
389 tmp.free()
390 return fs
391 }
392}
393
394// format_es returns a f64 as a `string` formatted according to the options set in `p`.
395@[direct_array_access; manualfree]
396pub fn format_es(f f64, p BF_param) string {
397 unsafe {
398 mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1)
399 if p.rm_tail_zero {
400 tmp := fs
401 fs = remove_tail_zeros(fs)
402 tmp.free()
403 }
404
405 mut buf := [max_size_f64_char]u8{} // write temp float buffer in stack
406 mut out := [max_size_f64_char]u8{} // out buffer
407 mut buf_i := 0 // index temporary string
408 mut out_i := 0 // index output string
409
410 mut sign_len_diff := 0
411 if p.pad_ch == `0` {
412 if p.positive {
413 if p.sign_flag {
414 out[out_i] = `+`
415 out_i++
416 sign_len_diff = -1
417 }
418 } else {
419 out[out_i] = `-`
420 out_i++
421 sign_len_diff = -1
422 }
423 } else {
424 if p.positive {
425 if p.sign_flag {
426 buf[buf_i] = `+`
427 buf_i++
428 }
429 } else {
430 buf[buf_i] = `-`
431 buf_i++
432 }
433 }
434
435 // copy the float
436 vmemcpy(&buf[buf_i], fs.str, fs.len)
437 buf_i += fs.len
438
439 // make the padding if needed
440 dif := p.len0 - buf_i + sign_len_diff
441 if p.align == .right {
442 for i1 := 0; i1 < dif; i1++ {
443 out[out_i] = p.pad_ch
444 out_i++
445 }
446 }
447 vmemcpy(&out[out_i], &buf[0], buf_i)
448 out_i += buf_i
449 if p.align == .left {
450 for i1 := 0; i1 < dif; i1++ {
451 out[out_i] = p.pad_ch
452 out_i++
453 }
454 }
455 out[out_i] = 0
456
457 // return and free
458 tmp := fs
459 fs = tos_clone(&out[0])
460 tmp.free()
461 return fs
462 }
463}
464
465// remove_tail_zeros strips trailing zeros from `s` and return the resulting `string`.
466@[direct_array_access]
467pub fn remove_tail_zeros(s string) string {
468 unsafe {
469 mut buf := malloc_noscan(s.len + 1)
470 mut i_d := 0
471 mut i_s := 0
472
473 // skip spaces
474 for i_s < s.len && s[i_s] !in [`-`, `+`] && (s[i_s] > `9` || s[i_s] < `0`) {
475 buf[i_d] = s[i_s]
476 i_s++
477 i_d++
478 }
479 // sign
480 if i_s < s.len && s[i_s] in [`-`, `+`] {
481 buf[i_d] = s[i_s]
482 i_s++
483 i_d++
484 }
485
486 // integer part
487 for i_s < s.len && s[i_s] >= `0` && s[i_s] <= `9` {
488 buf[i_d] = s[i_s]
489 i_s++
490 i_d++
491 }
492
493 // check decimals
494 if i_s < s.len && s[i_s] == `.` {
495 mut i_s1 := i_s + 1
496 mut sum := 0
497 mut i_s2 := i_s1 // last non-zero index after `.`
498 for i_s1 < s.len && s[i_s1] >= `0` && s[i_s1] <= `9` {
499 sum += s[i_s1] - u8(`0`)
500 if s[i_s1] != `0` {
501 i_s2 = i_s1
502 }
503 i_s1++
504 }
505 // decimal part must be copied
506 if sum > 0 {
507 for c_i in i_s .. i_s2 + 1 {
508 buf[i_d] = s[c_i]
509 i_d++
510 }
511 }
512 i_s = i_s1
513 }
514
515 if i_s < s.len && s[i_s] != `.` {
516 // check exponent
517 for {
518 buf[i_d] = s[i_s]
519 i_s++
520 i_d++
521 if i_s >= s.len {
522 break
523 }
524 }
525 }
526
527 buf[i_d] = 0
528 return tos(buf, i_d)
529 }
530}
531