From 0874e2bf02c5c1ec86a36353a5f9de173879673e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 27 Jun 2026 12:58:44 +0300 Subject: [PATCH] strconv: add buffer decimal writers (#27573) --- vlib/strconv/README.md | 14 ++++++++ vlib/strconv/write_dec.v | 50 ++++++++++++++++++++++++++ vlib/strconv/write_dec_test.v | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 vlib/strconv/write_dec.v create mode 100644 vlib/strconv/write_dec_test.v diff --git a/vlib/strconv/README.md b/vlib/strconv/README.md index bb8f04e50..8445641a3 100644 --- a/vlib/strconv/README.md +++ b/vlib/strconv/README.md @@ -1,3 +1,17 @@ ## Description `strconv` provides functions for converting strings to numbers and numbers to strings. + +## Buffer formatting + +`write_dec` and `write_dec_u` write a decimal integer into a caller-provided `[]u8` +buffer. They return the number of bytes written, or `-1` when the buffer is too small. + +```v +import strconv + +mut buf := []u8{len: 20} +n := strconv.write_dec(-12345, mut buf) +assert n == 6 +assert buf[..n].bytestr() == '-12345' +``` diff --git a/vlib/strconv/write_dec.v b/vlib/strconv/write_dec.v new file mode 100644 index 000000000..b3ecde2cb --- /dev/null +++ b/vlib/strconv/write_dec.v @@ -0,0 +1,50 @@ +module strconv + +// write_dec writes the decimal representation of `n` into `buf`. +// It returns the number of bytes written, or `-1` if `buf` is too small. +@[direct_array_access] +pub fn write_dec(n i64, mut buf []u8) int { + mut mag := u64(n) + if n < 0 { + mag = u64(0) - mag + ndigits := dec_digits(mag) + if buf.len < ndigits + 1 { + return -1 + } + buf[0] = `-` + write_dec_u_digits(mag, mut buf, 1, ndigits) + return ndigits + 1 + } + ndigits := dec_digits(mag) + if buf.len < ndigits { + return -1 + } + write_dec_u_digits(mag, mut buf, 0, ndigits) + return ndigits +} + +// write_dec_u writes the decimal representation of `n` into `buf`. +// It returns the number of bytes written, or `-1` if `buf` is too small. +@[direct_array_access] +pub fn write_dec_u(n u64, mut buf []u8) int { + ndigits := dec_digits(n) + if buf.len < ndigits { + return -1 + } + write_dec_u_digits(n, mut buf, 0, ndigits) + return ndigits +} + +@[direct_array_access] +fn write_dec_u_digits(n u64, mut buf []u8, offset int, ndigits int) { + mut x := n + mut i := offset + ndigits + for { + i-- + buf[i] = u8(x % 10) + `0` + x /= 10 + if x == 0 { + break + } + } +} diff --git a/vlib/strconv/write_dec_test.v b/vlib/strconv/write_dec_test.v new file mode 100644 index 000000000..cf4278a96 --- /dev/null +++ b/vlib/strconv/write_dec_test.v @@ -0,0 +1,68 @@ +import strconv + +fn dec_to_string(n i64) string { + mut buf := []u8{len: 21, init: `x`} + written := strconv.write_dec(n, mut buf) + assert written >= 0 + return buf[..written].bytestr() +} + +fn dec_u_to_string(n u64) string { + mut buf := []u8{len: 20, init: `x`} + written := strconv.write_dec_u(n, mut buf) + assert written >= 0 + return buf[..written].bytestr() +} + +fn test_write_dec_signed_values() { + assert dec_to_string(0) == '0' + assert dec_to_string(1) == '1' + assert dec_to_string(-1) == '-1' + assert dec_to_string(1001) == '1001' + assert dec_to_string(-1001) == '-1001' + assert dec_to_string(1234567890) == '1234567890' + assert dec_to_string(-1234567890) == '-1234567890' + assert dec_to_string(max_i64) == '9223372036854775807' + assert dec_to_string(-9223372036854775807) == '-9223372036854775807' + assert dec_to_string(min_i64) == '-9223372036854775808' + assert dec_to_string('-9223372036854775808'.i64()) == '-9223372036854775808' +} + +fn test_write_dec_unsigned_values() { + assert dec_u_to_string(0) == '0' + assert dec_u_to_string(1) == '1' + assert dec_u_to_string(1001) == '1001' + assert dec_u_to_string(1234567890) == '1234567890' + assert dec_u_to_string(u64(max_i64)) == '9223372036854775807' + assert dec_u_to_string(u64(max_i64) + 1) == '9223372036854775808' + assert dec_u_to_string(max_u64) == '18446744073709551615' +} + +fn test_write_dec_returns_count_and_preserves_tail() { + mut buf := []u8{len: 8, init: `x`} + assert strconv.write_dec(42, mut buf) == 2 + assert buf.bytestr() == '42xxxxxx' + + buf = []u8{len: 8, init: `x`} + assert strconv.write_dec(-42, mut buf) == 3 + assert buf.bytestr() == '-42xxxxx' + + buf = []u8{len: 8, init: `x`} + assert strconv.write_dec_u(42, mut buf) == 2 + assert buf.bytestr() == '42xxxxxx' +} + +fn test_write_dec_returns_minus_one_for_small_buffer() { + mut buf := []u8{len: 2, init: `x`} + assert strconv.write_dec(123, mut buf) == -1 + assert buf.bytestr() == 'xx' + assert strconv.write_dec(-12, mut buf) == -1 + assert buf.bytestr() == 'xx' + assert strconv.write_dec_u(123, mut buf) == -1 + assert buf.bytestr() == 'xx' + + assert strconv.write_dec(-1, mut buf) == 2 + assert buf.bytestr() == '-1' + assert strconv.write_dec_u(99, mut buf) == 2 + assert buf.bytestr() == '99' +} -- 2.39.5