diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index c316999b9..05184c29c 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -151,7 +151,7 @@ fn void List.reserve(List* list, usz min_capacity) { if (!min_capacity) return; if (list.capacity >= min_capacity) return; - if (!list.allocator) list.allocator = mem::temp_allocator(); + if (!list.allocator) list.allocator = mem::current_allocator(); min_capacity = math::next_power_of_2(min_capacity); list.entries = realloc_aligned(list.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof, .using = list.allocator) ?? null; list.capacity = min_capacity; diff --git a/lib/std/core/allocators/temp_allocator.c3 b/lib/std/core/allocators/temp_allocator.c3 index 81579b036..2bfe82970 100644 --- a/lib/std/core/allocators/temp_allocator.c3 +++ b/lib/std/core/allocators/temp_allocator.c3 @@ -154,8 +154,6 @@ fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, u TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof; return this._realloc_page(page, size, alignment, offset); } - assert(pointer < &this.data + this.capacity && pointer >= &this.data, "This is not a temp allocated pointer."); - assert(pointer < &this.data + this.used, "This is a stale temp pointer."); // TODO optimize last allocation TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)?; diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 new file mode 100644 index 000000000..88771e1ac --- /dev/null +++ b/lib/std/core/dstring.c3 @@ -0,0 +1,368 @@ +module std::core::dstring; + +typedef DString = distinct void*; + +const usz MIN_CAPACITY @private = 16; + +fn DString new_with_capacity(usz capacity, Allocator* allocator = mem::current_allocator()) +{ + if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; + StringData* data = malloc(StringData, 1, .using = allocator, .end_padding = capacity); + data.allocator = allocator; + data.len = 0; + data.capacity = capacity; + return (DString)data; +} + +fn DString tnew_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp_allocator()) @inline; + +fn DString new(String c = "", Allocator* allocator = mem::current_allocator()) +{ + usz len = c.len; + StringData* data = (StringData*)new_with_capacity(len, allocator); + if (len) + { + data.len = len; + mem::copy(&data.chars, c.ptr, len); + } + return (DString)data; +} + +fn DString tnew(String s = "") => new(s, mem::temp_allocator()) @inline; + +fn DString DString.new_concat(DString a, DString b, Allocator* allocator = mem::current_allocator()) +{ + DString string = new_with_capacity(a.len() + b.len(), allocator); + string.append(a); + string.append(b); + return string; +} + +fn DString DString.new_tconcat(DString a, DString b) => a.new_concat(b, mem::temp_allocator()); + +fn ZString DString.zstr(DString str) +{ + StringData* data = str.data(); + if (!data) return (ZString)""; + if (data.capacity == data.len) + { + str.reserve(1); + data.chars[data.len] = 0; + } + else if (data.chars[data.len] != 0) + { + data.chars[data.len] = 0; + } + return (ZString)&data.chars[0]; +} + +fn usz DString.capacity(DString this) +{ + if (!this) return 0; + return this.data().capacity; +} + +fn usz DString.len(DString this) +{ + if (!this) return 0; + return this.data().len; +} + +/** + * @require new_size <= this.len() + */ +fn void DString.chop(DString this, usz new_size) +{ + if (!this) return; + this.data().len = new_size; +} + +fn String DString.str(DString str) +{ + StringData* data = (StringData*)str; + if (!data) return String {}; + return (String)data.chars[:data.len]; +} + +fn void DString.append_utf32(DString* str, Char32[] chars) +{ + str.reserve(chars.len); + foreach (Char32 c : chars) + { + str.append_char32(c); + } +} + +/** + * @require index < str.len() + **/ +fn void DString.set(DString str, usz index, char c) +{ + str.data().chars[index] = c; +} + +fn void DString.append_repeat(DString* str, char c, usz times) +{ + if (times == 0) return; + str.reserve(times); + StringData* data = str.data(); + for (usz i = 0; i < times; i++) + { + data.chars[data.len++] = c; + } +} + +/** + * @require c <= 0x10ffff + */ +fn void DString.append_char32(DString* str, Char32 c) +{ + if (c < 0x7f) + { + str.reserve(1); + StringData* data = str.data(); + data.chars[data.len++] = (char)c; + return; + } + if (c < 0x7ff) + { + str.reserve(2); + StringData* data = str.data(); + data.chars[data.len++] = (char)(0xC0 | c >> 6); + data.chars[data.len++] = (char)(0x80 | (c & 0x3F)); + return; + } + if (c < 0xffff) + { + str.reserve(3); + StringData* data = str.data(); + data.chars[data.len++] = (char)(0xE0 | c >> 12); + data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F)); + data.chars[data.len++] = (char)(0x80 | (c & 0x3F)); + return; + } + str.reserve(4); + StringData* data = str.data(); + data.chars[data.len++] = (char)(0xF0 | c >> 18); + data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F)); + data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F)); + data.chars[data.len++] = (char)(0x80 | (c & 0x3F)); +} + +fn DString DString.tcopy(DString* str) => str.copy(mem::temp_allocator()); + +fn DString DString.copy(DString* str, Allocator* allocator = null) +{ + if (!str) + { + if (allocator) return new_with_capacity(0, allocator); + return (DString)null; + } + if (!allocator) allocator = mem::current_allocator(); + StringData* data = str.data(); + DString new_string = new_with_capacity(data.capacity, allocator); + mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len); + return new_string; +} + +fn ZString DString.copy_zstr(DString* str, Allocator* allocator = mem::current_allocator()) +{ + usz str_len = str.len(); + if (!str_len) + { + return (ZString)calloc(1, .using = allocator); + } + char* zstr = malloc(str_len + 1, .using = allocator); + StringData* data = str.data(); + mem::copy(zstr, &data.chars, str_len); + zstr[str_len] = 0; + return (ZString)zstr; +} + +fn String DString.copy_str(DString* str, Allocator* allocator = mem::current_allocator()) +{ + return (String)str.copy_zstr(allocator)[:str.len()]; +} + +fn String DString.tcopy_str(DString* str) => str.copy_str(mem::temp_allocator()) @inline; + +fn bool DString.equals(DString str, DString other_string) +{ + StringData *str1 = str.data(); + StringData *str2 = other_string.data(); + if (str1 == str2) return true; + if (!str1) return str2.len == 0; + if (!str2) return str1.len == 0; + usz str1_len = str1.len; + if (str1_len != str2.len) return false; + for (int i = 0; i < str1_len; i++) + { + if (str1.chars[i] != str2.chars[i]) return false; + } + return true; +} + +fn void DString.free(DString* str) +{ + if (!*str) return; + StringData* data = str.data(); + if (!data) return; + free(data, .using = data.allocator); + *str = (DString)null; +} + +fn bool DString.less(DString str, DString other_string) +{ + StringData* str1 = str.data(); + StringData* str2 = other_string.data(); + if (str1 == str2) return false; + if (!str1) return str2.len != 0; + if (!str2) return str1.len == 0; + usz str1_len = str1.len; + usz str2_len = str2.len; + if (str1_len != str2_len) return str1_len < str2_len; + for (int i = 0; i < str1_len; i++) + { + if (str1.chars[i] >= str2.chars[i]) return false; + } + return true; +} + +fn void DString.append_chars(DString* this, String str) +{ + usz other_len = str.len; + if (!other_len) return; + if (!*this) + { + *this = new(str); + return; + } + this.reserve(other_len); + StringData* data = (StringData*)*this; + mem::copy(&data.chars[data.len], str.ptr, other_len); + data.len += other_len; +} + +fn Char32[] DString.copy_utf32(DString* this, Allocator* allocator = mem::current_allocator()) +{ + return str::utf8to32(this.str(), allocator) @inline!!; +} + +fn void DString.append_string(DString* this, DString str) +{ + StringData* other = (StringData*)str; + if (!other) return; + this.append(str.str()); +} + +fn void DString.clear(DString* str) +{ + str.data().len = 0; +} + +fn void DString.append_char(DString* str, char c) +{ + if (!*str) + { + *str = new_with_capacity(MIN_CAPACITY); + } + str.reserve(1); + StringData* data = (StringData*)*str; + data.chars[data.len++] = c; +} + + +macro void DString.append(DString* str, value) +{ + var $Type = $typeof(value); + $switch ($Type): + $case char: + $case ichar: + str.append_char(value); + $case DString: + str.append_string(value); + $case String: + str.append_chars(value); + $case Char32: + str.append_char32(value); + $default: + $if (@convertible(value, Char32)): + str.append_char32(value); + $elif (@convertible(value, String)): + str.append_chars(value); + $else: + $assert(false, "Unsupported type for append – use printf instead."); + $endif; + $endswitch; +} + + +fn usz! DString.printf(DString* str, String format, args...) @maydiscard +{ + Formatter formatter; + formatter.init(&out_string_append_fn, str); + return formatter.vprintf(format, args); +} + +fn usz! DString.printfn(DString* str, String format, args...) @maydiscard +{ + Formatter formatter; + formatter.init(&out_string_append_fn, str); + usz len = formatter.vprintf(format, args)?; + str.append('\n'); + return len + 1; +} + +fn DString new_join(String[] s, String joiner, Allocator* allocator = mem::current_allocator()) +{ + if (!s.len) return (DString)null; + usz total_size = joiner.len * s.len; + foreach (String* &str : s) + { + total_size += str.len; + } + DString res = new_with_capacity(total_size, allocator); + res.append(s[0]); + foreach (String* &str : s[1..]) + { + res.append(joiner); + res.append(*str); + } + return res; +} + +fn void! out_string_append_fn(char c, void* data) @private +{ + DynString* s = data; + s.append_char(c); +} + + +fn StringData* DString.data(DString str) @inline @private +{ + return (StringData*)str; +} + +fn void DString.reserve(DString* str, usz addition) @private +{ + StringData* data = str.data(); + if (!data) + { + *str = dstring::new_with_capacity(addition); + return; + } + usz len = data.len + addition; + if (data.capacity >= len) return; + usz new_capacity = data.capacity *= 2; + if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY; + *str = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator); +} + + +struct StringData @private +{ + Allocator* allocator; + usz len; + usz capacity; + char[*] chars; +} diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index 63a37ff6b..d386d5338 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -22,6 +22,7 @@ fault NumberConversion MALFORMED_FLOAT, FLOAT_OUT_OF_RANGE, } + fn VarString join(String[] s, String joiner) { if (!s.len) return (VarString)null; @@ -335,7 +336,7 @@ fn String! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocato return data[:len]; } -fn String copy(String s, Allocator* allocator = mem::current_allocator()) + fn String copy(String s, Allocator* allocator = mem::current_allocator()) { usz len = s.len; ZString str_copy = copy_zstring(s, allocator) @inline; diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index a393161fe..a995c240d 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -4,7 +4,6 @@ import libc; typedef VarString = distinct void*; typedef DynStr = VarString; typedef DynString = VarString; -typedef DString = VarString; typedef VString = VarString; typedef Text = VarString; diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index 00951e431..140b78816 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -57,6 +57,8 @@ macro void print(x) catch(stdout().print(x)); $case ZString: catch(stdout().print(x.as_str())); + $case DString: + catch(stdout().print(x.str())); $case VarString: catch(stdout().print(x.str())); $default: @@ -76,6 +78,8 @@ macro void printn(x = "") catch(stdout().printn(x)); $case ZString: catch(stdout().printn(x.as_str())); + $case DString: + catch(stdout().printn(x.str())); $case VarString: catch(stdout().printn(x.str())); $default: diff --git a/lib/std/io/io_file.c3 b/lib/std/io/io_file.c3 index be8f77756..3010af629 100644 --- a/lib/std/io/io_file.c3 +++ b/lib/std/io/io_file.c3 @@ -131,9 +131,9 @@ fn usz! File.println(File file, String string) => file.printn(string); * @param [&in] file * @require file.file `File must be initialized` */ -fn VarString File.getline(File* file, Allocator* allocator = mem::current_allocator()) +fn DString File.getline(File* file, Allocator* allocator = mem::current_allocator()) { - VarString s = string::new_with_capacity(120, allocator); + DString s = dstring::new_with_capacity(120, allocator); while (!file.eof()) { int c = libc::fgetc(file.file); diff --git a/lib/std/io/io_printf.c3 b/lib/std/io/io_printf.c3 index 8e8f08be5..cb9492313 100644 --- a/lib/std/io/io_printf.c3 +++ b/lib/std/io/io_printf.c3 @@ -236,6 +236,10 @@ fn void! Formatter.out_str(Formatter* this, variant arg) @private { return this.out_substr(((VarString*)arg).str()); } + if (arg.type == DString.typeid) + { + return this.out_substr(((DString*)arg).str()); + } return this.out_str(variant { arg.ptr, arg.type.inner }); case POINTER: if (this.print_with_function(arg)?) return;