/*============================================================================= Copyright (c) 2019-2023 Dario Deledda. All rights reserved. Use of this source code is governed by an MIT license that can be found in the LICENSE file. This file contains string interpolation V functions =============================================================================*/ module strconv import strings // format_str_sb is a `strings.Builder` version of `format_str`. pub fn format_str_sb(s string, p BF_param, mut sb strings.Builder) { if p.len0 <= 0 { sb.write_string(s) return } dif := p.len0 - utf8_str_visible_length(s) if dif <= 0 { sb.write_string(s) return } if p.allign == .right { for i1 := 0; i1 < dif; i1++ { sb.write_u8(p.pad_ch) } } sb.write_string(s) if p.allign == .left { for i1 := 0; i1 < dif; i1++ { sb.write_u8(p.pad_ch) } } } const ( 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 // digit pairs in reverse order digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' ) // format_dec_sb formats an u64 using a `strings.Builder`. [direct_array_access] pub fn format_dec_sb(d u64, p BF_param, mut res strings.Builder) { mut n_char := dec_digits(d) sign_len := if !p.positive || p.sign_flag { 1 } else { 0 } number_len := sign_len + n_char dif := p.len0 - number_len mut sign_written := false if p.allign == .right { if p.pad_ch == `0` { if p.positive { if p.sign_flag { res.write_u8(`+`) sign_written = true } } else { res.write_u8(`-`) sign_written = true } } // write the pad chars for i1 := 0; i1 < dif; i1++ { res.write_u8(p.pad_ch) } } if !sign_written { // no pad char, write the sign before the number if p.positive { if p.sign_flag { res.write_u8(`+`) } } else { res.write_u8(`-`) } } /* // Legacy version // max u64 18446744073709551615 => 20 byte mut buf := [32]u8{} mut i := 20 mut d1 := d for i >= (21 - n_char) { buf[i] = u8(d1 % 10) + `0` d1 = d1 / 10 i-- } i++ */ //=========================================== // Speed version // max u64 18446744073709551615 => 20 byte mut buf := [32]u8{} mut i := 20 mut n := d mut d_i := u64(0) if n > 0 { for n > 0 { n1 := n / 100 // calculate the digit_pairs start index d_i = (n - (n1 * 100)) << 1 n = n1 unsafe { buf[i] = strconv.digit_pairs.str[d_i] } i-- d_i++ unsafe { buf[i] = strconv.digit_pairs.str[d_i] } i-- } i++ // remove head zero if d_i < 20 { i++ } unsafe { res.write_ptr(&buf[i], n_char) } } else { // we have a zero no need of more code! res.write_u8(`0`) } //=========================================== if p.allign == .left { for i1 := 0; i1 < dif; i1++ { res.write_u8(p.pad_ch) } } return } // f64_to_str_lnd1 formats a f64 to a `string` with `dec_digit` digits after the dot. [direct_array_access; manualfree] pub fn f64_to_str_lnd1(f f64, dec_digit int) string { unsafe { // we add the rounding value s := f64_to_str(f + dec_round[dec_digit], 18) // check for +inf -inf Nan if s.len > 2 && (s[0] == `n` || s[1] == `i`) { return s } m_sgn_flag := false mut sgn := 1 mut b := [26]u8{} mut d_pos := 1 mut i := 0 mut i1 := 0 mut exp := 0 mut exp_sgn := 1 mut dot_res_sp := -1 // get sign and decimal parts for c in s { match c { `-` { sgn = -1 i++ } `+` { sgn = 1 i++ } `0`...`9` { b[i1] = c i1++ i++ } `.` { if sgn > 0 { d_pos = i } else { d_pos = i - 1 } i++ } `e` { i++ break } else { s.free() return '[Float conversion error!!]' } } } b[i1] = 0 // get exponent if s[i] == `-` { exp_sgn = -1 i++ } else if s[i] == `+` { exp_sgn = 1 i++ } mut c := i for c < s.len { exp = exp * 10 + int(s[c] - `0`) c++ } // allocate exp+32 chars for the return string // mut res := []u8{len:exp+32,init:`0`} mut res := []u8{len: exp + 32, init: 0} mut r_i := 0 // result string buffer index // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") // s no more needed s.free() if sgn == 1 { if m_sgn_flag { res[r_i] = `+` r_i++ } } else { res[r_i] = `-` r_i++ } i = 0 if exp_sgn >= 0 { for b[i] != 0 { res[r_i] = b[i] r_i++ i++ if i >= d_pos && exp >= 0 { if exp == 0 { dot_res_sp = r_i res[r_i] = `.` r_i++ } exp-- } } for exp >= 0 { res[r_i] = `0` r_i++ exp-- } // println("exp: $exp $r_i $dot_res_sp") } else { mut dot_p := true for exp > 0 { res[r_i] = `0` r_i++ exp-- if dot_p { dot_res_sp = r_i res[r_i] = `.` r_i++ dot_p = false } } for b[i] != 0 { res[r_i] = b[i] r_i++ i++ } } // no more digits needed, stop here if dec_digit <= 0 { // 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) if dot_res_sp < 0 { dot_res_sp = i + 1 } tmp_res := tos(res.data, dot_res_sp).clone() res.free() return tmp_res } // println("r_i-d_pos: ${r_i - d_pos}") if dot_res_sp >= 0 { r_i = dot_res_sp + dec_digit + 1 res[r_i] = 0 for c1 in 1 .. dec_digit + 1 { if res[r_i - c1] == 0 { res[r_i - c1] = `0` } } // println("result: [${tos(&res[0],r_i)}]") tmp_res := tos(res.data, r_i).clone() res.free() return tmp_res } else { if dec_digit > 0 { mut c1 := 0 res[r_i] = `.` r_i++ for c1 < dec_digit { res[r_i] = `0` r_i++ c1++ } res[r_i] = 0 } tmp_res := tos(res.data, r_i).clone() res.free() return tmp_res } } } // format_fl is a `strings.Builder` version of format_fl. [direct_array_access; manualfree] pub fn format_fl(f f64, p BF_param) string { unsafe { mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1) // error!! if fs[0] == `[` { return fs } if p.rm_tail_zero { tmp := fs fs = remove_tail_zeros(fs) tmp.free() } mut buf := [strconv.max_size_f64_char]u8{} // write temp float buffer in stack mut out := [strconv.max_size_f64_char]u8{} // out buffer mut buf_i := 0 // index temporary string mut out_i := 0 // index output string mut sign_len_diff := 0 if p.pad_ch == `0` { if p.positive { if p.sign_flag { out[out_i] = `+` out_i++ sign_len_diff = -1 } } else { out[out_i] = `-` out_i++ sign_len_diff = -1 } } else { if p.positive { if p.sign_flag { buf[buf_i] = `+` buf_i++ } } else { buf[buf_i] = `-` buf_i++ } } // copy the float vmemcpy(&buf[buf_i], fs.str, fs.len) buf_i += fs.len // make the padding if needed dif := p.len0 - buf_i + sign_len_diff if p.allign == .right { for i1 := 0; i1 < dif; i1++ { out[out_i] = p.pad_ch out_i++ } } vmemcpy(&out[out_i], &buf[0], buf_i) out_i += buf_i if p.allign == .left { for i1 := 0; i1 < dif; i1++ { out[out_i] = p.pad_ch out_i++ } } out[out_i] = 0 // return and free tmp := fs fs = tos_clone(&out[0]) tmp.free() return fs } } // format_es returns a f64 as a `string` formatted according to the options set in `p`. [direct_array_access; manualfree] pub fn format_es(f f64, p BF_param) string { unsafe { mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1) if p.rm_tail_zero { tmp := fs fs = remove_tail_zeros(fs) tmp.free() } mut buf := [strconv.max_size_f64_char]u8{} // write temp float buffer in stack mut out := [strconv.max_size_f64_char]u8{} // out buffer mut buf_i := 0 // index temporary string mut out_i := 0 // index output string mut sign_len_diff := 0 if p.pad_ch == `0` { if p.positive { if p.sign_flag { out[out_i] = `+` out_i++ sign_len_diff = -1 } } else { out[out_i] = `-` out_i++ sign_len_diff = -1 } } else { if p.positive { if p.sign_flag { buf[buf_i] = `+` buf_i++ } } else { buf[buf_i] = `-` buf_i++ } } // copy the float vmemcpy(&buf[buf_i], fs.str, fs.len) buf_i += fs.len // make the padding if needed dif := p.len0 - buf_i + sign_len_diff if p.allign == .right { for i1 := 0; i1 < dif; i1++ { out[out_i] = p.pad_ch out_i++ } } vmemcpy(&out[out_i], &buf[0], buf_i) out_i += buf_i if p.allign == .left { for i1 := 0; i1 < dif; i1++ { out[out_i] = p.pad_ch out_i++ } } out[out_i] = 0 // return and free tmp := fs fs = tos_clone(&out[0]) tmp.free() return fs } } // remove_tail_zeros strips traling zeros from `s` and return the resulting `string`. [direct_array_access] pub fn remove_tail_zeros(s string) string { unsafe { mut buf := malloc_noscan(s.len + 1) mut i_d := 0 mut i_s := 0 // skip spaces for i_s < s.len && s[i_s] !in [`-`, `+`] && (s[i_s] > `9` || s[i_s] < `0`) { buf[i_d] = s[i_s] i_s++ i_d++ } // sign if i_s < s.len && s[i_s] in [`-`, `+`] { buf[i_d] = s[i_s] i_s++ i_d++ } // integer part for i_s < s.len && s[i_s] >= `0` && s[i_s] <= `9` { buf[i_d] = s[i_s] i_s++ i_d++ } // check decimals if i_s < s.len && s[i_s] == `.` { mut i_s1 := i_s + 1 mut sum := 0 for i_s1 < s.len && s[i_s1] >= `0` && s[i_s1] <= `9` { sum += s[i_s1] - u8(`0`) i_s1++ } // decimal part must be copied if sum > 0 { for c_i in i_s .. i_s1 { buf[i_d] = s[c_i] i_d++ } } i_s = i_s1 } if i_s < s.len && s[i_s] != `.` { // check exponent for { buf[i_d] = s[i_s] i_s++ i_d++ if i_s >= s.len { break } } } buf[i_d] = 0 return tos(buf, i_d) } }