diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index caa9a7a45..c60cd8f35 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -177,7 +177,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_ba { backtraces[++backtraces_to_ignore] = added_backtrace; } - @stack_mem(2048; Allocator mem) + @stack_mem(4096; Allocator mem) { BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces); if (catch backtrace) return false; diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index a3c7a210f..f582f55b3 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -192,11 +192,16 @@ fn String join(Allocator allocator, String[] s, String joiner) *> fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard { - @pool() + Splitter s = self.tokenize_all(needle); + DString d; + d.init(tmem, new_str.len * 2 + self.len + 16); + (void)d.append(s.next()); + while (try element = s.next()) { - String[] split = self.tsplit(needle); - return dstring::join(tmem, split, new_str).copy_str(allocator); - }; + d.append(new_str); + d.append(element); + } + return d.copy_str(allocator); } <* @@ -209,8 +214,16 @@ fn String String.replace(self, Allocator allocator, String needle, String new_st *> fn String String.treplace(self, String needle, String new_str) { - String[] split = self.tsplit(needle); - return dstring::join(tmem, split, new_str).str_view(); + Splitter s = self.tokenize_all(needle); + DString d; + d.init(tmem, new_str.len * 2 + self.len + 16); + (void)d.append(s.next()); + while (try element = s.next()) + { + d.append(new_str); + d.append(element); + } + return d.str_view(); } @@ -1184,6 +1197,11 @@ fn void Splitter.reset(&self) self.current = 0; } +fn bool Splitter.at_end(&self) +{ + return self.current > self.string.len; +} + fn String? Splitter.next(&self) { while (true) @@ -1205,7 +1223,7 @@ fn String? Splitter.next(&self) if (!next && self.type == TOKENIZE) continue; return remaining[:next]; } - self.current = len; + self.current = len + 1; return remaining; } } diff --git a/lib/std/os/linux/linux.c3 b/lib/std/os/linux/linux.c3 index fe0dc47a1..b88a91912 100644 --- a/lib/std/os/linux/linux.c3 +++ b/lib/std/os/linux/linux.c3 @@ -236,56 +236,49 @@ fn void? backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, voi fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined) { - @stack_mem(256; Allocator mem) + Splitter s = string.trim().tokenize(" at "); + String first = s.next() ?? NOT_FOUND~!; + String second = s.next() ?? NOT_FOUND~!; + uint line = 0; + String source = ""; + if (!second.contains("?") && second.contains(":")) { - String[] parts = string.trim().split(mem, " at "); - if (parts.len != 2) return NOT_FOUND?; - - uint line = 0; - String source = ""; - if (!parts[1].contains("?") && parts[1].contains(":")) - { - usz index = parts[1].rindex_of_char(':')!; - source = parts[1][:index]; - line = parts[1][index + 1..].to_uint()!; - } - return { - .function = parts[0].copy(allocator), - .object_file = obj_name.copy(allocator), - .file = source.copy(allocator), - .line = line, - .allocator = allocator, - .is_inline = is_inlined - }; + usz index = second.rindex_of_char(':')!; + source = second[:index]; + line = second[index + 1..].to_uint()!; + } + return { + .function = first.copy(allocator), + .object_file = obj_name.copy(allocator), + .file = source.copy(allocator), + .line = line, + .allocator = allocator, + .is_inline = is_inlined }; - } + fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local { - @stack_mem(1024; Allocator mem) + Splitter splitter = addr2line.tokenize("(inlined by)"); + while (try part = splitter.next()) { - String[] inline_parts = addr2line.split(mem, "(inlined by)"); - usz last = inline_parts.len - 1; - foreach (i, part : inline_parts) + bool is_inline = splitter.at_end(); + Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline); + if (catch trace) { - bool is_inline = i != last; - Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline); - if (catch trace) - { - list.push({ - .function = func_name.copy(allocator), - .object_file = obj_name.copy(allocator), - .offset = (uptr)addr, - .file = "".copy(allocator), - .line = 0, - .allocator = allocator, - .is_inline = is_inline + list.push({ + .function = func_name.copy(allocator), + .object_file = obj_name.copy(allocator), + .offset = (uptr)addr, + .file = "".copy(allocator), + .line = 0, + .allocator = allocator, + .is_inline = is_inline }); - continue; - } - list.push(trace); + continue; } - }; + list.push(trace); + } } fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local diff --git a/releasenotes.md b/releasenotes.md index e6003cdb2..c51060b03 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -12,6 +12,7 @@ - `$$MASK_TO_INT` and `$$INT_TO_MASK` to create bool masks from integers and back. - Better error messages when slicing a pointer to a slice or vector. #2681 - Generics using `@generic` rather than module based. +- Reduced memory usage for backtraces on Linux. ### Fixes - Regression with npot vector in struct triggering an assert #2219. @@ -44,6 +45,8 @@ - `foo.x` was not always handled correctly when `foo` was optional. - `x'1234' +++ (ichar[1]) { 'A' }` would fail due to missing const folding. - Miscompilation: global struct with vector could generate an incorrect initializer. +- `String.tokenize_all` would yield one too many empty tokens at the end. +- `String.replace` no longer depends on `String.split`. ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/test/unit/stdlib/core/string.c3 b/test/unit/stdlib/core/string.c3 index 4f3fb580e..17994b258 100644 --- a/test/unit/stdlib/core/string.c3 +++ b/test/unit/stdlib/core/string.c3 @@ -182,14 +182,6 @@ fn void test_split_to_buffer() free(strings); } -fn void test_treplace() -{ - String test = "Befriend some dragons?"; - assert(test.treplace("some", "all") == "Befriend all dragons?"); - assert(test.treplace("?", "!") == "Befriend some dragons!"); - assert(test.treplace("Never", "Always") == "Befriend some dragons?"); -} - fn void test_zstring() { String test = "hello"; @@ -211,10 +203,28 @@ fn void test_replace() free(val); val = test.replace(mem, "?", "!"); test::eq(val, "Befriend some dragons!"); - free(val); - val = test.replace(mem, "Never", "Always"); - test::eq(val, "Befriend some dragons?"); - free(val); + free(val); + val = test.replace(mem, "Never", "Always"); + test::eq(val, "Befriend some dragons?"); + free(val); + test = "some dragonsomesome"; + val = test.replace(mem, "some", "x"); + test::eq(val, "x dragonxx"); + free(val); +} + +fn void test_treplace() => @pool() +{ + String test = "Befriend some dragons?"; + String val = test.treplace("some", "all"); + test::eq(val, "Befriend all dragons?"); + val = test.treplace("?", "!"); + test::eq(val, "Befriend some dragons!"); + val = test.treplace("Never", "Always"); + test::eq(val, "Befriend some dragons?"); + test = "some dragonsomesome"; + val = test.treplace("some", "x"); + test::eq(val, "x dragonxx"); } fn void test_index_of()