diff --git a/lib/std/collections/linkedlist.c3 b/lib/std/collections/linkedlist.c3 index 69307a3f1..a864a8b60 100644 --- a/lib/std/collections/linkedlist.c3 +++ b/lib/std/collections/linkedlist.c3 @@ -28,9 +28,9 @@ fn void LinkedList.push_last(LinkedList* list, Type value) list.link_last(value); } -fn void LinkedList.init(LinkedList* list, Allocator* alloc = mem::heap()) +fn void LinkedList.init(LinkedList* list, Allocator* using = mem::heap()) { - *list = { .allocator = alloc }; + *list = { .allocator = using }; } fn void LinkedList.tinit(LinkedList* list) => list.init(mem::temp()) @inline; diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index efb6426ae..3b9ad3424 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -13,16 +13,16 @@ struct List } /** - * @require allocator != null "A valid allocator must be provided" + * @require using != null "A valid allocator must be provided" **/ -fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::heap()) +fn void List.init(List* list, usz initial_capacity = 16, Allocator* using = mem::heap()) { - list.allocator = allocator; + list.allocator = using; list.size = 0; if (initial_capacity > 0) { initial_capacity = math::next_power_of_2(initial_capacity); - list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = allocator)!!; + list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = using)!!; } else { diff --git a/lib/std/collections/map.c3 b/lib/std/collections/map.c3 index 08e9633d6..0484066e8 100644 --- a/lib/std/collections/map.c3 +++ b/lib/std/collections/map.c3 @@ -20,15 +20,15 @@ struct HashMap * @require load_factor > 0.0 "The load factor must be higher than 0" * @require !map.allocator "Map was already initialized" * @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum" - * @require allocator != null "The allocator must be non-null" + * @require using != null "The allocator must be non-null" **/ -fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::heap()) +fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* using = mem::heap()) { capacity = math::next_power_of_2(capacity); - map.allocator = allocator; + map.allocator = using; map.load_factor = load_factor; map.threshold = (uint)(capacity * load_factor); - map.table = calloc(Entry*, capacity, .using = allocator); + map.table = calloc(Entry*, capacity, .using = using); } /** @@ -42,9 +42,9 @@ fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, fl map.init(capacity, load_factor, mem::temp()); } -fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::heap()) +fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* using = mem::heap()) { - map.init(other_map.table.len, other_map.load_factor, allocator); + map.init(other_map.table.len, other_map.load_factor, using); map.put_all_for_create(other_map); } @@ -164,11 +164,11 @@ fn Key[] HashMap.key_tlist(HashMap* map) return map.key_list(mem::temp()) @inline; } -fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::heap()) +fn Key[] HashMap.key_list(HashMap* map, Allocator* using = mem::heap()) { if (!map.count) return Key[] {}; - Key[] list = calloc(Key, map.count, .using = allocator); + Key[] list = calloc(Key, map.count, .using = using); usz index = 0; foreach (Entry* entry : map.table) { @@ -186,10 +186,10 @@ fn Value[] HashMap.value_tlist(HashMap* map) return map.value_list(mem::temp()) @inline; } -fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::heap()) +fn Value[] HashMap.value_list(HashMap* map, Allocator* using = mem::heap()) { if (!map.count) return Value[] {}; - Value[] list = calloc(Value, map.count, .using = allocator); + Value[] list = calloc(Value, map.count, .using = using); usz index = 0; foreach (Entry* entry : map.table) { diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 3fc1c9254..4055bd0c3 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -80,10 +80,10 @@ fn void! Object.to_format(Object* o, Formatter* formatter) } } -fn Object* new_obj(Allocator* allocator = mem::heap()) +fn Object* new_obj(Allocator* using = mem::heap()) { - Object* o = malloc(Object, .using = allocator); - *o = { .allocator = allocator, .type = void.typeid }; + Object* o = malloc(Object, .using = using); + *o = { .allocator = using, .type = void.typeid }; return o; } @@ -92,31 +92,31 @@ fn Object* new_null() return &NULL_OBJECT; } -fn Object* new_int(int128 i, Allocator* allocator = mem::heap()) +fn Object* new_int(int128 i, Allocator* using = mem::heap()) { - Object* o = malloc(Object, .using = allocator); - *o = { .i = i, .allocator = allocator, .type = int128.typeid }; + Object* o = malloc(Object, .using = using); + *o = { .i = i, .allocator = using, .type = int128.typeid }; return o; } -macro Object* new_enum(e, Allocator* allocator = mem::heap()) +macro Object* new_enum(e, Allocator* using = mem::heap()) { - Object* o = malloc(Object, .using = allocator); - *o = { .i = (int128)e, .allocator = allocator, .type = $typeof(e).typeid }; + Object* o = malloc(Object, .using = using); + *o = { .i = (int128)e, .allocator = using, .type = $typeof(e).typeid }; return o; } -fn Object* new_float(double f, Allocator* allocator = mem::current_allocator()) +fn Object* new_float(double f, Allocator* using = mem::current_allocator()) { - Object* o = malloc(Object, .using = allocator); - *o = { .f = f, .allocator = allocator, .type = double.typeid }; + Object* o = malloc(Object, .using = using); + *o = { .f = f, .allocator = using, .type = double.typeid }; return o; } -fn Object* new_string(String s, Allocator* allocator = mem::heap()) +fn Object* new_string(String s, Allocator* using = mem::heap()) { - Object* o = malloc(Object, .using = allocator); - *o = { .s = s.copyz(allocator), .allocator = allocator, .type = String.typeid }; + Object* o = malloc(Object, .using = using); + *o = { .s = s.copy(using), .allocator = using, .type = String.typeid }; return o; } @@ -178,7 +178,7 @@ fn void Object.init_map_if_needed(Object* o) @private if (o.is_empty()) { o.type = ObjectInternalMap.typeid; - o.map.init(.allocator = o.allocator); + o.map.init(.using = o.allocator); } } @@ -190,7 +190,7 @@ fn void Object.init_array_if_needed(Object* o) @private if (o.is_empty()) { o.type = ObjectInternalList.typeid; - o.array.init(.allocator = o.allocator); + o.array.init(.using = o.allocator); } } @@ -206,7 +206,7 @@ fn void Object.set_object(Object* o, String key, Object* new_object) @private (void)free(entry.key, .using = o.allocator); entry.value.free(); } - o.map.set(str::copyz(key, o.map.allocator), new_object); + o.map.set(key.copy(o.map.allocator), new_object); } macro Object* object_from_value(value) @private diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index f42eec172..7adcdb058 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -27,7 +27,7 @@ struct TrackingAllocator fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* allocator) { *this = { .inner_allocator = allocator, .allocator.function = &tracking_allocator_fn }; - this.map.init(.allocator = allocator); + this.map.init(.using = allocator); } fn void TrackingAllocator.free(TrackingAllocator* this) diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index 25d8ffd64..2537904ba 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -7,11 +7,11 @@ const usz MIN_CAPACITY @private = 16; /** * @require !str.data() "String already initialized" **/ -fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* allocator = mem::heap()) +fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* using = mem::heap()) { if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; - StringData* data = malloc(StringData, 1, .using = allocator, .end_padding = capacity); - data.allocator = allocator; + StringData* data = malloc(StringData, 1, .using = using, .end_padding = capacity); + data.allocator = using; data.len = 0; data.capacity = capacity; *str = (DString)data; @@ -22,19 +22,19 @@ fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* alloc **/ fn void DString.tinit(DString *str, usz capacity = MIN_CAPACITY) => str.init(capacity, mem::temp()) @inline; -fn DString new_with_capacity(usz capacity, Allocator* allocator = mem::heap()) +fn DString new_with_capacity(usz capacity, Allocator* using = mem::heap()) { DString dstr; - dstr.init(capacity, allocator); + dstr.init(capacity, using); return dstr; } fn DString tnew_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline; -fn DString new(String c = "", Allocator* allocator = mem::heap()) +fn DString new(String c = "", Allocator* using = mem::heap()) { usz len = c.len; - StringData* data = (StringData*)new_with_capacity(len, allocator); + StringData* data = (StringData*)new_with_capacity(len, using); if (len) { data.len = len; @@ -45,10 +45,10 @@ fn DString new(String c = "", Allocator* allocator = mem::heap()) fn DString tnew(String s = "") => new(s, mem::temp()) @inline; -fn DString DString.new_concat(DString a, DString b, Allocator* allocator = mem::heap()) +fn DString DString.new_concat(DString a, DString b, Allocator* using = mem::heap()) { DString string; - string.init(a.len() + b.len(), allocator); + string.init(a.len() + b.len(), using); string.append(a); string.append(b); return string; @@ -167,37 +167,37 @@ fn void DString.append_char32(DString* str, Char32 c) fn DString DString.tcopy(DString* str) => str.copy(mem::temp()); -fn DString DString.copy(DString* str, Allocator* allocator = null) +fn DString DString.copy(DString* str, Allocator* using = null) { if (!str) { - if (allocator) return new_with_capacity(0, allocator); + if (using) return new_with_capacity(0, using); return (DString)null; } - if (!allocator) allocator = mem::heap(); + if (!using) using = mem::heap(); StringData* data = str.data(); - DString new_string = new_with_capacity(data.capacity, allocator); + DString new_string = new_with_capacity(data.capacity, using); 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::heap()) +fn ZString DString.copy_zstr(DString* str, Allocator* using = mem::heap()) { usz str_len = str.len(); if (!str_len) { - return (ZString)calloc(1, .using = allocator); + return (ZString)calloc(1, .using = using); } - char* zstr = malloc(str_len + 1, .using = allocator); + char* zstr = malloc(str_len + 1, .using = using); 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::heap()) +fn String DString.copy_str(DString* str, Allocator* using = mem::heap()) { - return (String)str.copy_zstr(allocator)[:str.len()]; + return (String)str.copy_zstr(using)[:str.len()]; } fn String DString.tcopy_str(DString* str) => str.copy_str(mem::temp()) @inline; @@ -259,9 +259,9 @@ fn void DString.append_chars(DString* this, String str) data.len += other_len; } -fn Char32[] DString.copy_utf32(DString* this, Allocator* allocator = mem::heap()) +fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap()) { - return str::utf8to32(this.str(), allocator) @inline!!; + return str::utf8to32(this.str(), using) @inline!!; } fn void DString.append_string(DString* this, DString str) @@ -329,7 +329,7 @@ fn usz! DString.printfn(DString* str, String format, args...) @maydiscard return len + 1; } -fn DString new_join(String[] s, String joiner, Allocator* allocator = mem::heap()) +fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap()) { if (!s.len) return (DString)null; usz total_size = joiner.len * s.len; @@ -337,7 +337,7 @@ fn DString new_join(String[] s, String joiner, Allocator* allocator = mem::heap( { total_size += str.len; } - DString res = new_with_capacity(total_size, allocator); + DString res = new_with_capacity(total_size, using); res.append(s[0]); foreach (String* &str : s[1..]) { diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index 131b7230f..54e2aa811 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -167,7 +167,7 @@ fn String! get_var(String name) $if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32): @pool() { - ZString val = libc::getenv(name.zstrtcopy()); + ZString val = libc::getenv(name.zstr_tcopy()); return val ? val.as_str() : SearchResult.MISSING!; }; $else: @@ -186,7 +186,7 @@ fn void set_var(String name, String value, bool overwrite = true) $if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32): @pool() { - if (libc::setenv(name.zstrtcopy(), value.zstrcopy(), (int)overwrite)) + if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite)) { unreachable(); } @@ -203,7 +203,7 @@ fn void clear_var(String name) $if (COMPILER_LIBC_AVAILABLE && OS_TYPE != OsType.WIN32): @pool() { - if (libc::unsetenv(name.zstrtcopy())) + if (libc::unsetenv(name.zstr_tcopy())) { unreachable(); } diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 6431a98a7..fc6f73f2c 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -321,10 +321,10 @@ macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @bui /** * Run with a specific allocator inside of the macro body. **/ -macro void @scoped(Allocator* allocator; @body()) +macro void @scoped(Allocator* using; @body()) { Allocator* old_allocator = thread_allocator; - thread_allocator = allocator; + thread_allocator = using; defer thread_allocator = old_allocator; @body(); } @@ -354,7 +354,7 @@ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @ * @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len" * @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'" **/ -macro tcalloc(..., usz end_padding = 0, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin +macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin { $if ($checks($vatype(0).sizeof)): var $Type = $vatype(0); @@ -369,7 +369,7 @@ macro tcalloc(..., usz end_padding = 0, usz alignment = allocator::DEFAULT_MEM_A $endif; } -fn void* trealloc(void* ptr, usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline +fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline { return temp_allocator().realloc_aligned(ptr, size, alignment)!!; } diff --git a/lib/std/core/mem_array.c3 b/lib/std/core/mem_array.c3 index 85ad1e0fe..f179965e7 100644 --- a/lib/std/core/mem_array.c3 +++ b/lib/std/core/mem_array.c3 @@ -17,9 +17,9 @@ macro talloc($Type, usz elements) @deprecated => tmalloc($Type, elements); /** * @require (usz.max / elements > $Type.sizeof) **/ -macro make($Type, usz elements, Allocator* allocator = mem::heap()) @deprecated +macro make($Type, usz elements, Allocator* using = mem::heap()) @deprecated { - return calloc($Type, elements, .using = allocator); + return calloc($Type, elements, .using = using); } /** diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index c0c84271e..dbaf0407e 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -1,4 +1,4 @@ -module std::core::str; +module std::core::string; typedef ZString = distinct char*; typedef String = char[]; @@ -23,6 +23,213 @@ fault NumberConversion FLOAT_OUT_OF_RANGE, } +macro String printf(String fmt, ..., Allocator* using = mem::heap()) +{ + if (using == mem::temp()) + { + DString str; + str.tinit(); + str.printf(fmt, $vasplat()); + return str.str(); + } + @pool() + { + DString str; + str.tinit(); + str.printf(fmt, $vasplat()); + return str.copy_str(using); + }; +} + +macro String tprintf(String fmt, ...) +{ + DString str; + str.tinit(); + str.printf(fmt, $vasplat()); + return str.str(); +} + +macro bool char_in_set(char c, String set) +{ + foreach (ch : set) if (ch == c) return true; + return false; +} + +/** + * @param [in] string + * @param [in] to_trim + **/ +fn String String.trim(String string, String to_trim = "\t\n\r ") +{ + usz start = 0; + usz len = string.len; + while (start < len && char_in_set(string[start], to_trim)) start++; + if (start == len) return string[:0]; + usz end = len - 1; + while (end > start && char_in_set(string[end], to_trim)) end--; + return string[start..end]; +} + +/** + * @param [in] string + * @param [in] needle + **/ +fn bool String.starts_with(String string, String needle) +{ + if (needle.len > string.len) return false; + if (!needle.len) return true; + return string[:needle.len] == needle; +} + + +/** + * Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" } + * + * @param [in] s + * @param [in] needle + * @param [&inout] using "The allocator, defaults to the heap allocator" + * @param max "Max number of elements, 0 means no limit, defaults to 0" + * @require needle.len > 0 "The needle must be at least 1 character long" + * @ensure return.len > 0 + **/ +fn String[] String.split(String s, String needle, usz max = 0, Allocator* using = mem::heap()) +{ + usz capacity = 16; + usz i = 0; + String* holder = malloc(String, capacity, .using = using); + bool no_more = false; + while (!no_more) + { + usz! index = i == max - 1 ? SearchResult.MISSING! : s.index_of(needle); + String res @noinit; + if (try index) + { + res = s[:index]; + s = s[index + needle.len..]; + } + else + { + res = s; + no_more = true; + } + if (i == capacity) + { + capacity *= 2; + holder = realloc(holder, String.sizeof * capacity, .using = using); + } + holder[i++] = res; + } + return holder[:i]; +} + +/** + * This function is identical to String.split, but implicitly uses the + * temporary allocator. + * + * @param [in] s + * @param [in] needle + * @param max "Max number of elements, 0 means no limit, defaults to 0" + **/ +fn String[] tsplit(String s, String needle, usz max = 0) +{ + return s.split(needle, max, mem::temp()) @inline; +} + +/** + * Find the index of the first incidence of a string. + * + * @param [in] s + * @param [in] needle + * @pure + * @ensure return < s.len + * @require needle.len > 0 "The needle must be len 1 or more" + **/ +fn usz! String.index_of(String s, String needle) +{ + usz match = 0; + usz needed = needle.len; + usz index_start = 0; + char search = needle[0]; + foreach (usz i, char c : s) + { + if (c == search) + { + if (!match) index_start = i; + match++; + if (match == needed) return index_start; + search = needle[match]; + continue; + } + if (match) + { + match = 0; + search = needle[0]; + } + } + return SearchResult.MISSING!; +} + +/** + * Find the index of the last incidence of a string. + * + * @param [in] s + * @param [in] needle + * @pure + * @ensure return < s.len + * @require needle.len > 0 "The needle must be len 1 or more" + **/ +fn usz! String.rindex_of(String s, String needle) +{ + usz match = 0; + usz needed = needle.len; + usz index_start = 0; + char search = needle[^1]; + foreach_r (usz i, char c : s) + { + if (c == search) + { + if (!match) index_start = i; + match++; + if (match == needed) return index_start - needle.len + 1; + search = needle[^(match + 1)]; + continue; + } + if (match) + { + match = 0; + search = needle[^1]; + } + } + return SearchResult.MISSING!; +} + +fn ZString String.zstr_copy(String s, Allocator* using = mem::heap()) +{ + usz len = s.len; + char* str = malloc(len + 1, .using = using); + mem::copy(str, s.ptr, len); + str[len] = 0; + return (ZString)str; +} + +fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline; + +fn String String.copy(String s, Allocator* using = mem::heap()) +{ + usz len = s.len; + char* str = malloc(len + 1, .using = using); + mem::copy(str, s.ptr, len); + str[len] = 0; + return str[:len]; +} + +fn String String.tcopy(String s) => s.copy(mem::temp()) @inline; + +fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline; +fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::temp()) @inline; + +module std::core::str; + fn VarString join(String[] s, String joiner) { if (!s.len) return (VarString)null; @@ -140,138 +347,21 @@ fn uint! to_uint(String str) => to_integer(uint, str); fn ushort! to_ushort(String str) => to_integer(ushort, str); fn char! to_uchar(String str) => to_integer(char, str); -fn String trim(String string, String to_trim = "\t\n\r ") -{ - usz start = 0; - usz len = string.len; - while (start < len && char_in_set(string[start], to_trim)) start++; - if (start == len) return string[:0]; - usz end = len - 1; - while (end > start && char_in_set(string[end], to_trim)) end--; - return string[start..end]; -} +fn String trim(String string, String to_trim = "\t\n\r ") @deprecated => string.trim(to_trim); -fn bool starts_with(String s, String needle) -{ - if (needle.len > s.len) return false; - foreach (i, c : needle) - { - if (c != s[i]) return false; - } - return true; -} +fn bool starts_with(String s, String needle) @deprecated => s.starts_with(needle); -fn String[] tsplit(String s, String needle) => split(s, needle, mem::temp()) @inline; +fn String[] tsplit(String s, String needle) @deprecated => s.split(needle, .using = mem::temp()) @inline; +fn String[] split(String s, String needle, Allocator* using = mem::heap()) @deprecated => s.split(needle, .using = using); +fn usz! rindex_of(String s, String needle) @deprecated => s.rindex_of(needle); +fn usz! index_of(String s, String needle) @deprecated => s.index_of(needle); -fn String[] split(String s, String needle, Allocator* allocator = mem::heap()) -{ - usz capacity = 16; - usz i = 0; - String* holder = malloc(String, capacity, .using = allocator); - while (s.len) - { - usz! index = index_of(s, needle); - String res @noinit; - if (try index) - { - res = s[:index]; - s = s[index + needle.len..]; - } - else - { - res = s; - s = s[:0]; - } - if (i == capacity) - { - capacity *= 2; - holder = realloc(holder, String.sizeof * capacity, .using = allocator); - } - holder[i++] = res; - } - return holder[:i]; -} +fn ZString String.zstrcopy(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using); +fn ZString String.zstrtcopy(String s) @deprecated => s.zstr_tcopy(); -fn usz! rindex_of(String s, String needle) -{ - usz match = 0; - usz needed = needle.len; - if (!needed) return SearchResult.MISSING!; - usz index_start = 0; - char search = needle[0]; - foreach_r (usz i, char c : s) - { - if (c == search) - { - if (!match) index_start = i; - match++; - if (match == needed) return index_start; - search = needle[match]; - continue; - } - if (match) - { - match = 0; - search = needle[0]; - } - } - return SearchResult.MISSING!; -} - -fn usz! index_of(String s, String needle) -{ - usz match = 0; - usz needed = needle.len; - if (!needed) return SearchResult.MISSING!; - usz index_start = 0; - char search = needle[0]; - foreach (usz i, char c : s) - { - if (c == search) - { - if (!match) index_start = i; - match++; - if (match == needed) return index_start; - search = needle[match]; - continue; - } - if (match) - { - match = 0; - search = needle[0]; - } - } - return SearchResult.MISSING!; -} - -fn ZString String.zstrcopy(String s, Allocator* allocator = mem::heap()) => copy_zstring(s, allocator) @inline; -fn ZString String.zstrtcopy(String s) => copy_zstring(s, mem::temp()) @inline; -fn String String.copyz(String s, Allocator* allocator = mem::heap()) => copyz(s, allocator) @inline; -fn String String.tcopyz(String s) => copyz(s, mem::temp()) @inline; - -fn ZString copy_zstring(String s, Allocator* allocator = mem::heap()) -{ - usz len = s.len; - char* str = malloc(len + 1, .using = allocator); - mem::copy(str, s.ptr, len); - str[len] = 0; - return (ZString)str; -} - - -fn String copyz(String s, Allocator* allocator = mem::heap()) -{ - usz len = s.len; - char* str = malloc(len + 1, .using = allocator); - mem::copy(str, s.ptr, len); - str[len] = 0; - return str[:len]; -} - -fn ZString tcopy_zstring(String s) -{ - return copy_zstring(s, mem::temp()); -} +fn ZString copy_zstring(String s, Allocator* using = mem::heap()) @deprecated => s.zstr_copy(using); +fn String copyz(String s, Allocator* using = mem::heap()) @deprecated => s.copy(using); +fn ZString tcopy_zstring(String s) @deprecated => s.zstr_tcopy(); fn bool compare(String a, String b) { @@ -282,6 +372,7 @@ fn bool compare(String a, String b) } return true; } + fault UnicodeResult { INVALID_UTF8, @@ -299,54 +390,54 @@ fn usz utf8_codepoints(String utf8) return len; } -fn Char32[]! utf8to32(String utf8, Allocator* allocator = mem::heap()) +fn Char32[]! utf8to32(String utf8, Allocator* using = mem::heap()) { usz codepoints = conv::utf8_codepoints(utf8); - Char32* data = malloc_checked(Char32, codepoints + 1, .using = allocator)?; + Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)?; conv::utf8to32_unsafe(utf8, data)?; data[codepoints] = 0; return data[:codepoints]; } -fn String utf32to8(Char32[] utf32, Allocator* allocator = mem::heap()) +fn String utf32to8(Char32[] utf32, Allocator* using = mem::heap()) { usz len = conv::utf8len_for_utf32(utf32); - char* data = malloc_checked(len + 1, .using = allocator)!!; + char* data = malloc_checked(len + 1, .using = using)!!; conv::utf32to8_unsafe(utf32, data); data[len] = 0; return data[:len]; } -fn Char16[]! utf8to16(String utf8, Allocator* allocator = mem::heap()) +fn Char16[]! utf8to16(String utf8, Allocator* using = mem::heap()) { usz len16 = conv::utf16len_for_utf8(utf8); - Char16* data = malloc_checked(Char16, len16 + 1, .using = allocator)?; + Char16* data = malloc_checked(Char16, len16 + 1, .using = using)?; conv::utf8to16_unsafe(utf8, data)?; data[len16] = 0; return data[:len16]; } -fn String! utf16to8(Char16[] utf16, Allocator* allocator = mem::heap()) +fn String! utf16to8(Char16[] utf16, Allocator* using = mem::heap()) { usz len = conv::utf8len_for_utf16(utf16); - char* data = malloc_checked(len + 1, .using = allocator)?; + char* data = malloc_checked(len + 1, .using = using)?; conv::utf16to8_unsafe(utf16, data)?; data[len] = 0; return data[:len]; } - fn String copy(String s, Allocator* allocator = mem::heap()) +fn String copy(String s, Allocator* using = mem::heap()) @deprecated { usz len = s.len; - ZString str_copy = copy_zstring(s, allocator) @inline; + ZString str_copy = s.zstr_copy(using) @inline; return str_copy[:len]; } -fn String tcopy(String s) +fn String tcopy(String s) @deprecated { usz len = s.len; - ZString str_copy = tcopy_zstring(s) @inline; + ZString str_copy = s.zstr_tcopy() @inline; return str_copy[:len]; } diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index d12c5a2ad..387a444d6 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -9,7 +9,7 @@ typedef Text = VarString; const usz MIN_CAPACITY = 16; -fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap()) +fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap()) @deprecated { if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; StringData* data = malloc(StringData, 1, .using = allocator, .end_padding = capacity); @@ -19,7 +19,7 @@ fn VarString new_with_capacity(usz capacity, Allocator* allocator = mem::heap()) return (VarString)data; } -fn VarString new(String c) +fn VarString new(String c) @deprecated { usz len = c.len; VarString str = new_with_capacity(len); diff --git a/lib/std/encoding/json.c3 b/lib/std/encoding/json.c3 index e7cf751d1..25d7e401f 100644 --- a/lib/std/encoding/json.c3 +++ b/lib/std/encoding/json.c3 @@ -46,9 +46,9 @@ fault JsonParsingError INVALID_NUMBER, } -fn void JsonParser.init(JsonParser* parser, Stream s, Allocator* allocator = mem::heap()) +fn void JsonParser.init(JsonParser* parser, Stream s, Allocator* using = mem::heap()) { - *parser = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator }; + *parser = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using }; } fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token) diff --git a/lib/std/io/dir.c3 b/lib/std/io/dir.c3 index 8c412b5ab..561fe261e 100644 --- a/lib/std/io/dir.c3 +++ b/lib/std/io/dir.c3 @@ -10,9 +10,9 @@ fault PathResult INVALID_PATH } -fn String! getcwd(Allocator* allocator = mem::default_allocator()) +fn String! getcwd(Allocator* using = mem::heap()) { - return os::getcwd(allocator); + return os::getcwd(using); } fn String! tgetcwd() @@ -136,9 +136,9 @@ fn void! normalize_path(String* path_ref) @private *path_ref = path[:len]; } -fn Path new_path(String path) +fn Path new_path(String path, Allocator* using = using) { - String copy = str::copy(path); + String copy = path.copy(using); normalize_path(©)!!; return (Path)copy; } diff --git a/lib/std/io/io_file.c3 b/lib/std/io/io_file.c3 index 38720170e..9837e555e 100644 --- a/lib/std/io/io_file.c3 +++ b/lib/std/io/io_file.c3 @@ -36,7 +36,7 @@ fn void! File.memopen(File* file, char[] data, String mode) { @pool() { - file.file = libc::memopen(data.ptr, data.len, mode.zstrtcopy(), file.file); + file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file); // TODO errors }; } @@ -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 DString File.getline(File* file, Allocator* allocator = mem::heap()) +fn DString File.getline(File* file, Allocator* using = mem::heap()) { - DString s = dstring::new_with_capacity(120, allocator); + DString s = dstring::new_with_capacity(120, using); while (!file.eof()) { int c = libc::fgetc(file.file); diff --git a/lib/std/io/io_fileinfo.c3 b/lib/std/io/io_fileinfo.c3 index d648ffddd..783838203 100644 --- a/lib/std/io/io_fileinfo.c3 +++ b/lib/std/io/io_fileinfo.c3 @@ -6,6 +6,14 @@ struct FileInfo ulong size; } +fn bool exits(String path) +{ + File f; + if (catch(f.open(path, "r"))) return false; + (void)f.close(); + return true; +} + $switch (env::OS_TYPE): $case MACOSX: $case IOS: @@ -38,12 +46,13 @@ struct Darwin64Stat @private long[2] st_qspare; } extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64"); + fn void! FileInfo.read(FileInfo* info, Path path) { @pool() { Darwin64Stat stat; - int res = _stat(str::tcopy_zstring((String)path), &stat); + int res = _stat(((String)path).zstr_tcopy(), &stat); if (res != 0) { switch (libc::errno()) diff --git a/lib/std/io/io_printf.c3 b/lib/std/io/io_printf.c3 index 887072efa..3225880eb 100644 --- a/lib/std/io/io_printf.c3 +++ b/lib/std/io/io_printf.c3 @@ -25,7 +25,7 @@ fault FormattingFault } typedef OutputFn = fn void!(char c, void* buffer); -typedef ToStringFunction = fn String(void* value, Allocator *allocator); +typedef ToStringFunction = fn String(void* value, Allocator *using); typedef ToFormatFunction = fn void!(void* value, Formatter* formatter); typedef FloatType = double; diff --git a/lib/std/io/os/file.c3 b/lib/std/io/os/file.c3 index 6fd8bec74..146c000f8 100644 --- a/lib/std/io/os/file.c3 +++ b/lib/std/io/os/file.c3 @@ -49,7 +49,7 @@ $else: str::utf8to16(filename, mem::temp())!!, str::utf8to16(mode, mem::temp())!!); $else: - CFile file = libc::fopen(filename.zstrtcopy(), mode.zstrtcopy()); + CFile file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); $endif; return file ?: file_open_errno()!; }; @@ -74,7 +74,7 @@ $else: str::utf8to16(mode, mem::temp())!!, file); $else: - file = libc::freopen(filename.zstrtcopy(), mode.zstrtcopy(), file); + file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); $endif; return file ?: file_open_errno()!; }; diff --git a/lib/std/io/os/getcwd.c3 b/lib/std/io/os/getcwd.c3 index e14b24ad5..ddb1ffa54 100644 --- a/lib/std/io/os/getcwd.c3 +++ b/lib/std/io/os/getcwd.c3 @@ -3,7 +3,7 @@ import libc; $if (!env::COMPILER_LIBC_AVAILABLE): -fn String! getcwd(Allocator* allocator = mem::default_allocator()) +fn String! getcwd(Allocator* using = mem::heap()) { unreachable("'getcwd' not available"); } @@ -13,7 +13,7 @@ $elif (env::OS_TYPE == OsType.WIN32): extern fn Char16* _wgetcwd(Char16* buffer, int maxlen); extern fn usz wcslen(Char16* str); -macro String! getcwd(Allocator* allocator = mem::default_allocator()) +macro String! getcwd(Allocator* using = mem::heap()) { const DEFAULT_BUFFER = 256; Char16[DEFAULT_BUFFER] buffer; @@ -27,13 +27,13 @@ macro String! getcwd(Allocator* allocator = mem::default_allocator()) free = true; } Char16[] str16 = res[:wcslen(res)]; - return str::utf16to8(str16, allocator); + return str::utf16to8(str16, using); } $else: extern fn ZString _getcwd(char* pwd, usz len) @extern("getcwd"); -macro String! getcwd(Allocator* allocator = mem::default_allocator()) +macro String! getcwd(Allocator* using = mem::heap()) { const usz DEFAULT_BUFFER = 256; char[DEFAULT_BUFFER] buffer; @@ -47,8 +47,7 @@ macro String! getcwd(Allocator* allocator = mem::default_allocator()) free = true; } defer if (free) libc::free((void*)res); - String copy = str::copyz(res.as_str(), allocator); - return copy; + return res.copy(using); } $endif; \ No newline at end of file diff --git a/lib/std/io/stream/bytewriter.c3 b/lib/std/io/stream/bytewriter.c3 index 12a8bd93d..2f90fc363 100644 --- a/lib/std/io/stream/bytewriter.c3 +++ b/lib/std/io/stream/bytewriter.c3 @@ -9,19 +9,18 @@ struct ByteWriter /** * @param [&inout] writer - * @param [&in] allocator + * @param [&in] using * @require writer.bytes.len == 0 "Init may not run on on already initialized data" - * @ensure allocator != null, index == 0 + * @ensure using != null, index == 0 **/ -fn void ByteWriter.init(ByteWriter* writer, Allocator* allocator = mem::heap()) +fn void ByteWriter.init(ByteWriter* writer, Allocator* using = mem::heap()) { - *writer = { .bytes = {}, .allocator = allocator }; + *writer = { .bytes = {}, .allocator = using }; } /** * @param [&inout] writer * @require writer.bytes.len == 0 "Init may not run on on already initialized data" - * @ensure allocator != null, index == 0 **/ fn void ByteWriter.tinit(ByteWriter* writer) { diff --git a/lib/std/net/inetaddr.c3 b/lib/std/net/inetaddr.c3 index b739efea7..79ee91aa6 100644 --- a/lib/std/net/inetaddr.c3 +++ b/lib/std/net/inetaddr.c3 @@ -54,7 +54,7 @@ fn void! InetAddress.to_format(InetAddress* addr, Formatter* formatter) formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?; } -fn String! InetAddress.to_string(InetAddress* addr, Allocator* allocator = mem::heap()) +fn String! InetAddress.to_string(InetAddress* addr, Allocator* using = mem::heap()) { if (addr.is_ipv6) { @@ -62,11 +62,11 @@ fn String! InetAddress.to_string(InetAddress* addr, Allocator* allocator = mem:: String res = (String)io::bprintf(&buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", addr.ipv6.a, addr.ipv6.b, addr.ipv6.c, addr.ipv6.d, addr.ipv6.e, addr.ipv6.f, addr.ipv6.g, addr.ipv6.h)?; - return res.copyz(allocator); + return res.copy(using); } char[3 * 4 + 3 + 1] buffer; String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)?; - return res.copyz(allocator); + return res.copy(using); } fn InetAddress! ipv6_from_str(String s) diff --git a/lib/std/net/net.c3 b/lib/std/net/net.c3 index e12f8372b..6048aac97 100644 --- a/lib/std/net/net.c3 +++ b/lib/std/net/net.c3 @@ -37,9 +37,9 @@ fn uint! ipv4toint(String s) return out; } -fn String! inttoipv4(uint val, Allocator* allocator = mem::heap()) +fn String! inttoipv4(uint val, Allocator* using = mem::heap()) { char[3 * 4 + 3 + 1] buffer; String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)?; - return res.copyz(allocator); + return res.copy(using); } \ No newline at end of file diff --git a/lib/std/os/macos/objc.c3 b/lib/std/os/macos/objc.c3 index 085b66e8f..03a887ed8 100644 --- a/lib/std/os/macos/objc.c3 +++ b/lib/std/os/macos/objc.c3 @@ -28,11 +28,11 @@ macro Class! class_by_name(char* c) return cls; } -macro Class[] class_get_list(Allocator *allocator = mem::heap()) +macro Class[] class_get_list(Allocator *using = mem::heap()) { int num_classes = _macos_objc_getClassList(null, 0); if (!num_classes) return {}; - Class[] entries = array::make(Class, num_classes, allocator); + Class[] entries = array::make(Class, num_classes, using); _macos_objc_getClassList(entries.ptr, entries.len); return entries; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index b4b2287de..c1b70b95b 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -7122,6 +7122,7 @@ static inline bool sema_expr_analyse_retval(SemaContext *c, Expr *expr) { TODO } + expr->type = type_no_optional(c->rtype); if (expr->type == type_void) { SEMA_ERROR(expr, "'return' cannot be used on void functions."); @@ -7129,7 +7130,6 @@ static inline bool sema_expr_analyse_retval(SemaContext *c, Expr *expr) } Expr *return_value = c->return_expr; assert(return_value); - expr->type = c->rtype; if (expr_is_const(return_value)) { expr_replace(expr, return_value); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9c77c988a..9855aca1b 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -468,6 +468,8 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement sema_inline_return_defers(context, statement, context->active_scope.defer_last, 0); if (context->call_env.ensures) { + // Never generate an expression. + if (return_expr && return_expr->expr_kind == EXPR_OPTIONAL) goto SKIP_ENSURE; AstId first = 0; AstId *append_id = &first; // Creating an assign statement @@ -486,7 +488,8 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement if (statement->return_stmt.cleanup) { // If we have the same ast on cleanup / cleanup-fail we need to separate them. - if (type_is_optional(expected_rtype) && statement->return_stmt.cleanup == statement->return_stmt.cleanup_fail) + if (type_is_optional(expected_rtype) && + statement->return_stmt.cleanup == statement->return_stmt.cleanup_fail) { statement->return_stmt.cleanup_fail = astid(copy_ast_defer(astptr(statement->return_stmt.cleanup))); } @@ -495,7 +498,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement } else { - statement->return_stmt.cleanup = statement->return_stmt.cleanup_fail = first; + statement->return_stmt.cleanup = first; } } SKIP_ENSURE:; diff --git a/src/compiler/types.c b/src/compiler/types.c index 013898a5a..6e45222b7 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1916,7 +1916,14 @@ static inline Type *type_find_max_ptr_type(Type *type, Type *other) // Decay foo[n]* to foo* other_pointer_type = type_get_ptr(other_pointer_type->array.base); } - + if (type_is_subtype(pointer_type->canonical, other_pointer_type->canonical)) + { + return type; + } + if (type_is_subtype(other_pointer_type->canonical, pointer_type->canonical)) + { + return other; + } Type *max_type = type_find_max_type(pointer_type, other_pointer_type); if (!max_type) return NULL; return type_get_ptr(max_type); diff --git a/src/version.h b/src/version.h index eee4ac948..1fa1befee 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.101" \ No newline at end of file +#define COMPILER_VERSION "0.4.102" \ No newline at end of file diff --git a/test/unit/stdlib/core/string.c3 b/test/unit/stdlib/core/string.c3 new file mode 100644 index 000000000..cee899f71 --- /dev/null +++ b/test/unit/stdlib/core/string.c3 @@ -0,0 +1,57 @@ +module std::core::string::tests @test; + +fn void test_starts_with() +{ + String s = "ofke"; + assert(s.starts_with("of")); + assert(s.starts_with("ofke")); + assert(!s.starts_with("ofkes")); + assert(!s.starts_with("ofkf")); + s = ""; + assert(s.starts_with("")); + assert(!s.starts_with("o")); +} + +fn void test_trim() +{ + String s = " \t\nabc "; + assert(s.trim() == "abc"); + assert(((String)"\n\t").trim() == ""); + assert(((String)" \n\tok").trim() == "ok"); + assert(((String)"!! \n\t ").trim() == "!!"); + assert(s.trim("c \t") == "\nab"); +} + +fn void test_split() +{ + String test = "abc|b||c|"; + String[] strings = test.split("|"); + assert(strings.len == 5); + assert(strings[0] == "abc"); + assert(strings[1] == "b"); + assert(strings[2] == ""); + assert(strings[3] == "c"); + assert(strings[4] == ""); + strings = test.split("|", 2); + assert(strings.len == 2); + assert(strings[0] == "abc"); + assert(strings[1] == "b||c|"); +} + +fn void! test_index_of() +{ + String test = "hello world hello"; + assert(test.index_of("o")? == 4); + assert(test.index_of("ll")? == 2); + assert(catch(test.index_of("wi"))); +} + +fn void! test_rindex_of() +{ + String test = "hello world hello"; + assert(test.rindex_of("o")? == 16); + assert(test.rindex_of("ll")? == 14); + assert(test.rindex_of("he")? == 12); + assert(test.rindex_of("world")? == 6); + assert(catch(test.rindex_of("wi"))); +} \ No newline at end of file