Add HashSet and String methods (#2386)

* Add `String.contains_char` using `String.index_of_char` and `HashSet.values` together with `HashSet.tvalues`

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
This commit is contained in:
Sander van den Bosch
2025-08-16 16:57:31 +02:00
committed by GitHub
parent d07da2804e
commit 261184b5c1
6 changed files with 158 additions and 21 deletions

View File

@@ -332,10 +332,7 @@ macro HashMap.@each_entry(map; @body(entry))
}
}
fn Value[] HashMap.tvalues(&map)
{
return map.values(tmem) @inline;
}
fn Value[] HashMap.tvalues(&self) => self.values(tmem) @inline;
fn Value[] HashMap.values(&self, Allocator allocator)
{

View File

@@ -14,14 +14,14 @@ const Allocator SET_HEAP_ALLOCATOR = (Allocator)&dummy;
<* Copy the ONHEAP allocator to initialize to a set that is heap allocated *>
const HashSet ONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
struct Entry
struct Entry
{
uint hash;
Value value;
Entry* next;
}
struct HashSet (Printable)
struct HashSet (Printable)
{
Entry*[] table;
Allocator allocator;
@@ -39,7 +39,7 @@ fn int HashSet.len(&self) @operator(len) => (int) self.count;
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
fn HashSet* HashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
@@ -285,7 +285,7 @@ fn usz HashSet.remove_all_from(&set, HashSet* other)
<*
Free all memory allocated by the hash set.
*>
fn void HashSet.free(&set)
fn void HashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
@@ -329,7 +329,23 @@ fn void HashSet.reserve(&set, usz capacity)
}
}
fn Value[] HashSet.tvalues(&self) => self.values(tmem) @inline;
fn Value[] HashSet.values(&self, Allocator allocator)
{
if (!self.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, self.count);
usz index = 0;
foreach (Entry* entry : self.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
// --- Set Operations ---

View File

@@ -427,6 +427,19 @@ fn bool String.contains(s, String substr)
return @ok(s.index_of(substr));
}
<*
Check if a character is found in the string.
@param [in] s
@param character : "The character to look for."
@pure
@return "true if the string contains the character, false otherwise"
*>
fn bool String.contains_char(s, char character)
{
return @ok(s.index_of_char(character));
}
<*
Check how many non-overlapping instances of a substring there is.

View File

@@ -54,6 +54,7 @@
- Added array `@zip` and `@zip_into` macros. #2370
- Updated termios bindings to use bitstructs and fixed some constants with incorrect values #2372
- Added libloaderapi to `std::os::win32`.
- Added `HashSet.values` and `String.contains_char` #2386
## 0.7.4 Change list
@@ -70,7 +71,7 @@
- Formatting option "%h" now supports pointers.
- Improve error on unsigned implicit conversion to signed.
- Update error message for struct initialization #2286
- Add SipHash family of keyed PRFs. #2287
- Add SipHash family of keyed PRFs. #2287
- `$is_const` is deprecated in favour of `@is_const` based on `$defined`.
- Multiline contract comments #2113
- Removed the use of temp allocator in backtrace printing.
@@ -113,7 +114,7 @@
- Array indices are now using int64 internally.
- Bit shift operation fails with inline uint enum despite matching underlying type #2279.
- Fix to codegen when using a bitstruct constant defined using a cast with an operator #2248.
- Function pointers are now compile time constants.
- Function pointers are now compile time constants.
- Splat 8 arguments can sometimes cause incorrect behaviour in the compiler. #2283
- Correctly poison the analysis after a failed $assert or $error. #2284
- `$foo` variables could be assigned non-compile time values.
@@ -217,7 +218,7 @@
- Incorrect codegen if a macro ends with unreachable and is assigned to something. #2210
- Fix error for named arguments-order with compile-time arguments #2212
- Bug in AST copying would make operator overloading like `+=` compile incorrectly #2217.
- `$defined(#expr)` broken with binary. #2219
- `$defined(#expr)` broken with binary. #2219
- Method ambiguity when importing parent module publicly in private submodule. #2208
- Linker errors when shadowing @local with public function #2198
- Bug when offsetting pointers of large structs using ++ and --.

View File

@@ -184,7 +184,7 @@ fn void edge_cases()
alias StringSet = HashSet{String};
fn void string_set_test()
{
{
StringSet set;
set.tinit();
defer set.free();
@@ -203,22 +203,114 @@ fn void string_set_test()
}
fn void add_all_test()
{
StringSet set1;
set1.init(mem);
defer set1.free();
String[] list = {"hello", "world", "hello"};
usz total = set1.add_all(list);
assert(total == 2);
assert(set1.contains("hello"));
assert(set1.contains("world"));
assert(!set1.contains("foo"));
set1.remove("hello");
assert(!set1.contains("hello"));
assert(set1.len() == 1);
StringSet set2;
set2.init(mem);
defer set2.free();
total = set2.add_all({"foo", "bar"});
assert(total == 2);
total = set2.add_all_from(&set1);
assert(total == 1);
assert(set2.contains("foo"));
assert(set2.contains("bar"));
assert(set2.contains("world"));
assert(!set2.contains("hello"));
}
fn void remove_all_test()
{
String[] list1 = {"foo", "bar", "baz"};
String[] list2 = {"Hello", "World", "foo"};
String[] list3 = {"the", "C3", "compiler", "said", "Hello"};
StringSet set1;
set1.init_from_values(mem, list1);
defer set1.free();
assert(set1.len() == 3);
StringSet set2;
set2.init_from_values(mem, list1);
defer set2.free();
assert(set1.len() == 3);
usz total = set2.add_all(list2);
assert(total == 2);
assert(set2.len() == 5);
total = set2.remove_all(list1);
assert(total == 3);
assert(set2.len() == 2);
assert(set2.contains("Hello"));
assert(set2.contains("World"));
assert(!set2.contains("foo"));
assert(!set2.contains("bar"));
assert(!set2.contains("baz"));
total = set1.add_all(list3);
assert(total == 5);
assert(set1.len() == 8);
total = set1.remove_all_from(&set2);
assert(total == 1);
assert(set1.len() == 7);
assert(set1.contains("foo"));
assert(set1.contains("bar"));
assert(set1.contains("baz"));
assert(set1.contains("the"));
assert(set1.contains("C3"));
assert(set1.contains("compiler"));
assert(set1.contains("said"));
assert(!set1.contains("Hello"));
}
fn void values_test()
{
StringSet set;
set.init(mem);
defer set.free();
String[] list = { "hello", "world", "hello" };
usz total = set.add_all(list);
assert(total == 2);
String[] values = set.values(mem);
assert(values.len == 0);
free(values);
assert(set.contains("hello"));
assert(set.contains("world"));
assert(!set.contains("foo"));
String[] list1 = {"foo", "bar", "baz"};
set.add_all(list1);
assert(set.len() == 3);
set.remove("hello");
assert(!set.contains("hello"));
assert(set.len() == 1);
values = set.values(mem);
assert(values.len == 3);
assert(array::contains(values, "foo"));
assert(array::contains(values, "bar"));
assert(array::contains(values, "baz"));
free(values);
set.remove("bar");
values = set.tvalues();
assert(values.len == 2);
assert(array::contains(values, "foo"));
assert(array::contains(values, "baz"));
assert(!array::contains(values, "bar"));
}
fn void is_initialized_test()

View File

@@ -255,6 +255,24 @@ fn void test_rindex_of_char()
assert(@catch(test.index_of_char('x')));
}
fn void test_contains()
{
String test = "hello world hello";
assert(test.contains("o"));
assert(test.contains("ll"));
assert(test.contains(" hello"));
assert(!test.contains("wi"));
}
fn void contains_char()
{
String test = "hello world hello";
assert(test.contains_char('o'));
assert(test.contains_char('l'));
assert(test.contains_char('h'));
assert(!test.contains_char('x'));
}
fn void test_base_13_convesion()
{
assert("13".to_long(13)!! == 13 + 3);