From 70540cc2ff09d813c2a8ef35e55904d0530ac745 Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Sun, 24 May 2026 22:36:06 +0800 Subject: [PATCH] Adjust StringBuilder size hint semantics --- builtin/stringbuilder_buffer.mbt | 20 ++++++++++---------- builtin/stringbuilder_concat.mbt | 11 ++++++----- builtin/stringbuilder_test.mbt | 12 ++++++++++++ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/builtin/stringbuilder_buffer.mbt b/builtin/stringbuilder_buffer.mbt index f00d9945e..b105c79a1 100644 --- a/builtin/stringbuilder_buffer.mbt +++ b/builtin/stringbuilder_buffer.mbt @@ -23,16 +23,16 @@ struct StringBuilder { /// /// Parameters: /// -/// * `size_hint` : An optional initial capacity hint for the internal buffer. If -/// less than 1, a minimum capacity of 1 is used. Defaults to 0. It is the size of bytes, -/// not the size of characters. `size_hint` may be ignored on some platforms, JS for example. +/// * `size_hint` : An optional initial capacity hint for the internal buffer, +/// in UTF-16 code units. Defaults to 8. Use 0 to request an empty initial +/// buffer. `size_hint` may be ignored on some platforms, JS for example. /// -/// Returns a new `StringBuilder` instance with the specified initial capacity. +/// Returns a new `StringBuilder` instance. /// #alias(new) -pub fn StringBuilder::StringBuilder(size_hint? : Int = 0) -> StringBuilder { - let initial = if size_hint < 1 { 1 } else { (size_hint + 1) / 2 } - let data : FixedArray[UInt16] = FixedArray::make(initial, 0) +pub fn StringBuilder::StringBuilder(size_hint? : Int = 8) -> StringBuilder { + guard size_hint >= 0 else { abort("size_hint must be non-negative") } + let data : FixedArray[UInt16] = FixedArray::make(size_hint, 0) { data, len: 0 } } @@ -51,9 +51,9 @@ fn StringBuilder::grow_if_necessary( if required <= current_len { return } - // current_len is at least 1 - // double the enough_space until it larger than required - let enough_space = for enough_space = current_len; enough_space < required; { + // Double the capacity until it can hold the required length. + let initial = current_len.max(1) + let enough_space = for enough_space = initial; enough_space < required; { continue enough_space * 2 } nobreak { enough_space diff --git a/builtin/stringbuilder_concat.mbt b/builtin/stringbuilder_concat.mbt index 5ba72bdac..6e2b9f62f 100644 --- a/builtin/stringbuilder_concat.mbt +++ b/builtin/stringbuilder_concat.mbt @@ -22,14 +22,15 @@ struct StringBuilder { /// /// Parameters: /// -/// * `size_hint` : An optional initial capacity hint for the internal buffer. If -/// less than 1, a minimum capacity of 1 is used. Defaults to 0. It is the size of bytes, -/// not the size of characters. `size_hint` may be ignored on some platforms, JS for example. +/// * `size_hint` : An optional initial capacity hint for the internal buffer, +/// in UTF-16 code units. Defaults to 8. Use 0 to request an empty initial +/// buffer. `size_hint` may be ignored on some platforms, JS for example. /// -/// Returns a new `StringBuilder` instance with the specified initial capacity. +/// Returns a new `StringBuilder` instance. /// #alias(new) -pub fn StringBuilder::StringBuilder(size_hint? : Int = 0) -> StringBuilder { +pub fn StringBuilder::StringBuilder(size_hint? : Int = 8) -> StringBuilder { + guard size_hint >= 0 else { abort("size_hint must be non-negative") } ignore(size_hint) { val: "" } } diff --git a/builtin/stringbuilder_test.mbt b/builtin/stringbuilder_test.mbt index ff3418165..90b2948b7 100644 --- a/builtin/stringbuilder_test.mbt +++ b/builtin/stringbuilder_test.mbt @@ -47,6 +47,18 @@ test "reset does not mutate returned string" { inspect(buf.to_string(), content="bye") } +///| +test "StringBuilder size_hint zero grows" { + let buf = StringBuilder(size_hint=0) + buf.write_string("hello") + inspect(buf.to_string(), content="hello") +} + +///| +test "panic StringBuilder constructor negative size_hint" { + ignore(StringBuilder(size_hint=-1)) +} + ///| test { let data = ["a", "b", "c", "hello world"]