diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 5da8c560eb..e66b05e8be 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -9,6 +9,10 @@ fn (mut g Gen) write_str_fn_definitions() { void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) { va_list args; va_start(args, guess); + // NB: (*memsize - *nbytes) === how much free space is left at the end of the current buffer refbufp + // *memsize === total length of the buffer refbufp + // *nbytes === already occupied bytes of buffer refbufp + // guess === how many bytes were taken during the current vsnprintf run for(;;) { if (guess < *memsize - *nbytes) { guess = vsnprintf(*refbufp + *nbytes, *memsize - *nbytes, fmt, args); @@ -62,7 +66,7 @@ string _STR(const char *fmt, int nfmts, ...) { fwidth -= (s.len - utf8_str_visible_length(s)); else fwidth += (s.len - utf8_str_visible_length(s)); - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+fwidth-4, fwidth, s.len, s.str); + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, fwidth, s.len, s.str); } else { // %.*s _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, s.len, s.str); } diff --git a/vlib/v/tests/string_interpolation_test.v b/vlib/v/tests/string_interpolation_test.v index 78dff09c3d..b57bb517d8 100644 --- a/vlib/v/tests/string_interpolation_test.v +++ b/vlib/v/tests/string_interpolation_test.v @@ -56,7 +56,7 @@ fn test_implicit_str() { assert text == '4242' } -fn test_string_interpolation_percent_escaping(){ +fn test_string_interpolation_percent_escaping() { test := 'hello' hello := 'world' x := '%.*s$hello$test |${hello:-30s}|' @@ -111,7 +111,11 @@ fn test_utf8_string_interpolation() { st := 'Sträßle' m := '10€' assert '$a $st $m' == 'à-côté Sträßle 10€' - assert '>${a:10}< >${st:-8}< >${m:5}<-' == '> à-côté< >Sträßle < > 10€<-' + zz := '>${a:10}< >${st:-8}< >${m:5}<-' + zz_expected := '> à-côté< >Sträßle < > 10€<-' + eprintln(' zz: $zz') + eprintln('zz_expected: $zz_expected') + assert zz == zz_expected // e := '\u20AC' // Eurosign doesn' work with MSVC and tcc e := '€' assert '100.00 $e' == '100.00 €' @@ -135,3 +139,15 @@ fn test_string_interpolation_str_evaluation() { mut x := S{17, 13.455893} assert '$x' == '[17, 13.456]' } + + +fn test_string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() { + // discovered during debugging VLS + i := 3 + input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' + eprintln('---------------------------------------------------------------------------------------------') + eprintln('+60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():60} | $input') + eprintln('-60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():-60} | $input') + eprintln('---------------------------------------------------------------------------------------------') + assert true +}