mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
"@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:
committed by
Christoffer Lerno
parent
33cc2d889b
commit
89de0a70d2
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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..])
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)!!;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()!;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.101"
|
||||
#define COMPILER_VERSION "0.4.102"
|
||||
57
test/unit/stdlib/core/string.c3
Normal file
57
test/unit/stdlib/core/string.c3
Normal 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")));
|
||||
}
|
||||
Reference in New Issue
Block a user