"@ensure" now correctly only runs on non-optional results. Subtypes now merge to a single type. Beginning deprecation of "std::core::str". Refreshed String functions. Consistent use of ".using" parameter. Functions moved to string methods. Tests on more string methods. Fixes to split, rindex_of.

This commit is contained in:
Christoffer Lerno
2023-03-07 19:32:47 +01:00
committed by Christoffer Lerno
parent 33cc2d889b
commit 89de0a70d2
27 changed files with 410 additions and 245 deletions

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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)

View File

@@ -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..])
{

View File

@@ -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();
}

View File

@@ -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)!!;
}

View File

@@ -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);
}
/**

View File

@@ -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];
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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(&copy)!!;
return (Path)copy;
}

View File

@@ -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);

View File

@@ -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())

View File

@@ -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;

View File

@@ -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()!;
};

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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:;

View File

@@ -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);

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.101"
#define COMPILER_VERSION "0.4.102"

View File

@@ -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")));
}