diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-19 22:18:33 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-19 22:18:33 +0000 |
| commit | 3af5dcf34d705cc52c1ffe5b85c2a90b5104e4c9 (patch) | |
| tree | 8ad5b61b2818baf1af80c0a725622cda2c3fa1b8 | |
| parent | 959a27fd763e2e960ef31adf891118a261bad1c7 (diff) | |
Improve 'std/cuda.zc' and 'std/vec.zc' + iteration...
| -rw-r--r-- | docs/std/README.md | 3 | ||||
| -rw-r--r-- | docs/std/vec.md | 90 | ||||
| -rw-r--r-- | examples/gpu/cuda_info.zc | 33 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 26 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 37 | ||||
| -rw-r--r-- | std/cuda.zc | 85 | ||||
| -rw-r--r-- | std/vec.zc | 109 | ||||
| -rw-r--r-- | tests/collections/test_vec_suite.zc | 177 | ||||
| -rw-r--r-- | tests/std/test_vec.zc | 168 |
9 files changed, 534 insertions, 194 deletions
diff --git a/docs/std/README.md b/docs/std/README.md new file mode 100644 index 0000000..8202192 --- /dev/null +++ b/docs/std/README.md @@ -0,0 +1,3 @@ +# Standard Library + +- [Vector (Vec)](./vec.md) - A growable dynamic array. diff --git a/docs/std/vec.md b/docs/std/vec.md new file mode 100644 index 0000000..a0422bb --- /dev/null +++ b/docs/std/vec.md @@ -0,0 +1,90 @@ +# Standard Library: Vector (`std/vec.zc`) + +`Vec<T>` is a contiguous, growable array type. It is the standard dynamic array used in Zen-C. + +## Overview + +- **Generic**: Works with any type `T`. +- **Dynamic**: Automatically resizes as elements are added. +- **Safe**: Bounds checks on access (panics on failure). +- **RAII**: Automatically frees memory when it goes out of scope (implements `Drop`). + +## Usage + +```zc +import "std/vec.zc" + +fn main() { + var v = Vec<int>::new(); + v.push(10); + v.push(20); + + // Iteration + for x in &v { + println "{(*x)}"; + } +} // v is freed automatically here +``` + +## Struct Definition + +```zc +struct Vec<T> { + data: T*; + len: usize; + cap: usize; +} +``` + +## Methods + +### Construction + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **new** | `Vec<T>::new() -> Vec<T>` | Creates a new, empty vector. Does not allocate memory until the first push. | +| **with_capacity** | `Vec<T>::with_capacity(cap: usize) -> Vec<T>` | Creates a new vector with an initial capacity of `cap`. Useful for optimization if you know the number of elements in advance. | + +### Modification + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **push** | `push(self, item: T)` | Appends an element to the back. Panics if allocation fails. | +| **pop** | `pop(self) -> T` | Removes the last element and returns it. Panics if empty. | +| **pop_opt** | `pop_opt(self) -> Option<T>` | Removes the last element and returns `Some(val)`. Returns `None` if empty. Safe usage. | +| **insert** | `insert(self, idx: usize, item: T)` | Inserts an element at `idx`. Shifts elements right. Panics if `idx > len`. | +| **remove** | `remove(self, idx: usize) -> T` | Removes and returns the element at `idx`. Shifts elements left. Panics if `idx >= len`. | +| **clear** | `clear(self)` | Removes all values. Has no effect on allocated capacity. | +| **reverse** | `reverse(self)` | Reverses the order of elements in place. | + +### Access + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **get** | `get(self, idx: usize) -> T` | Returns a copy of the element at `idx`. Panics if out of bounds. | +| **set** | `set(self, idx: usize, item: T)` | Overwrites the element at `idx`. Panics if out of bounds. | +| **first** | `first(self) -> T` | Returns a copy of the first element. Panics if empty. | +| **last** | `last(self) -> T` | Returns a copy of the last element. Panics if empty. | + +### Utility + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **length** | `length(self) -> usize` | Returns the number of elements. | +| **is_empty** | `is_empty(self) -> bool` | Returns `true` if the vector contains no elements. | +| **contains** | `contains(self, item: T) -> bool` | Returns `true` if vector contains an element equal to `item` (byte-wise). | +| **clone** | `clone(self) -> Vec<T>` | Returns a new vector with a deep copy of the data. | + +### Iteration + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **iterator** | `iterator(self) -> VecIter<T>` | Returns an iterator yielding copies. Used by `for x in v`. | +| **iter_ref** | `iter_ref(self) -> VecIterRef<T>` | Returns an iterator yielding pointers. Used by `for x in &v` (sugar) or `for x in v.iter_ref()`. Allows in-place mod. | + +## Memory Management + +| Method | Signature | Description | +| :--- | :--- | :--- | +| **Free** | `free(self)` | Manually frees memory. Safe to call multiple times. | +| **Trait** | `impl Drop for Vec` | Automatically calls `free()` when `Vec` goes out of scope. | diff --git a/examples/gpu/cuda_info.zc b/examples/gpu/cuda_info.zc new file mode 100644 index 0000000..7e832d1 --- /dev/null +++ b/examples/gpu/cuda_info.zc @@ -0,0 +1,33 @@ + +import "std/cuda.zc" +import "std/string.zc" + +fn main() { + var count = cuda_device_count(); + "---------------------------"; + "CUDA Device Count: {count}"; + "---------------------------"; + + if (count > 0) { + var props = cuda_device_properties(0); + + "Device Name: {props.name.vec.data}"; + "Total Global Mem: {props.total_global_mem}"; + "SM Count: {props.multi_processor_count}"; + "Compute Capability: {props.major}.{props.minor}"; + "Max Threads per Block: {props.max_threads_per_block}"; + "Warp Size: {props.warp_size}"; + + props.name.free(); + + var driver = cuda_driver_version(); + var runtime = cuda_runtime_version(); + + "Driver Version: {driver}"; + "Runtime Version: {runtime}"; + + var mem = cuda_mem_info(); + "Free Mem: {mem.free}"; + "Total Mem: {mem.total}"; + } +} diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 699b9fc..db47d16 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -1665,17 +1665,26 @@ ASTNode *parse_for(ParserContext *ctx, Lexer *l) var_name[var.len] = 0; ASTNode *obj_expr = start_expr; + char *iter_method = "iterator"; + + // Check for reference iteration: for x in &vec + if (obj_expr->type == NODE_EXPR_UNARY && + obj_expr->unary.op && strcmp(obj_expr->unary.op, "&") == 0) + { + obj_expr = obj_expr->unary.operand; + iter_method = "iter_ref"; + } // var __it = obj.iterator(); ASTNode *it_decl = ast_create(NODE_VAR_DECL); it_decl->var_decl.name = xstrdup("__it"); it_decl->var_decl.type_str = NULL; // inferred - // obj.iterator() + // obj.iterator() or obj.iter_ref() ASTNode *call_iter = ast_create(NODE_EXPR_CALL); ASTNode *memb_iter = ast_create(NODE_EXPR_MEMBER); memb_iter->member.target = obj_expr; - memb_iter->member.field = xstrdup("iterator"); + memb_iter->member.field = xstrdup(iter_method); call_iter->call.callee = memb_iter; call_iter->call.args = NULL; call_iter->call.arg_count = 0; @@ -3391,6 +3400,19 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) n->impl_trait.target_type = name2; n->impl_trait.methods = h; add_to_impl_list(ctx, n); + + // If target struct is generic, register this impl as a template + ASTNode *def = find_struct_def(ctx, name2); + if (def && ((def->type == NODE_STRUCT && def->strct.is_template) || + (def->type == NODE_ENUM && def->enm.is_template))) + { + const char *gp = "T"; + if (def->type == NODE_STRUCT && def->strct.generic_param_count > 0) + gp = def->strct.generic_params[0]; + // TODO: Enum generic params support if needed + register_impl_template(ctx, name2, gp, n); + } + return n; } else diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index 1f881b7..f96ed24 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -1518,6 +1518,11 @@ ASTNode *copy_ast_replacing(ASTNode *n, const char *p, const char *c, const char new_node->impl.struct_name = replace_type_str(n->impl.struct_name, p, c, os, ns); new_node->impl.methods = copy_ast_replacing(n->impl.methods, p, c, os, ns); break; + case NODE_IMPL_TRAIT: + new_node->impl_trait.trait_name = xstrdup(n->impl_trait.trait_name); + new_node->impl_trait.target_type = replace_type_str(n->impl_trait.target_type, p, c, os, ns); + new_node->impl_trait.methods = copy_ast_replacing(n->impl_trait.methods, p, c, os, ns); + break; default: break; } @@ -1879,8 +1884,17 @@ void instantiate_methods(ParserContext *ctx, GenericImplTemplate *it, it->struct_name, mangled_struct_name); it->impl_node->next = backup_next; // Restore - new_impl->impl.struct_name = xstrdup(mangled_struct_name); - ASTNode *meth = new_impl->impl.methods; + + ASTNode *meth = NULL; + + if (new_impl->type == NODE_IMPL) { + new_impl->impl.struct_name = xstrdup(mangled_struct_name); + meth = new_impl->impl.methods; + } else if (new_impl->type == NODE_IMPL_TRAIT) { + new_impl->impl_trait.target_type = xstrdup(mangled_struct_name); + meth = new_impl->impl_trait.methods; + } + while (meth) { char *suffix = meth->func.name + strlen(it->struct_name); @@ -1995,6 +2009,16 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, ASTNode *i = ast_create(NODE_STRUCT); i->strct.name = xstrdup(m); i->strct.is_template = 0; + + // Copy type attributes (e.g. has_drop) + i->type_info = type_new(TYPE_STRUCT); + i->type_info->name = xstrdup(m); + if (t->struct_node->type_info) + { + i->type_info->traits = t->struct_node->type_info->traits; + i->type_info->is_restrict = t->struct_node->type_info->is_restrict; + } + // Use first generic param for substitution (single-param backward compat) const char *gp = (t->struct_node->strct.generic_param_count > 0) ? t->struct_node->strct.generic_params[0] @@ -2009,6 +2033,15 @@ void instantiate_generic(ParserContext *ctx, const char *tpl, const char *arg, ASTNode *i = ast_create(NODE_ENUM); i->enm.name = xstrdup(m); i->enm.is_template = 0; + + // Copy type attributes (e.g. has_drop) + i->type_info = type_new(TYPE_ENUM); + i->type_info->name = xstrdup(m); + if (t->struct_node->type_info) + { + i->type_info->traits = t->struct_node->type_info->traits; + } + ASTNode *h = 0, *tl = 0; ASTNode *v = t->struct_node->enm.variants; while (v) diff --git a/std/cuda.zc b/std/cuda.zc index 851acb3..dbb1fe6 100644 --- a/std/cuda.zc +++ b/std/cuda.zc @@ -1,5 +1,7 @@ include <cuda_runtime.h> +import "./string.zc" +import "./mem.zc" // Memory Management. @@ -97,3 +99,86 @@ fn cuda_last_error() -> int { fn cuda_ok() -> bool { return cuda_last_error() == 0; } + + +raw { + void _z_cuda_get_props(int dev, char* name, size_t* total_mem, int* sm_count, int* major, int* minor, int* max_threads, int* warp_size) { + struct cudaDeviceProp prop; + if (cudaGetDeviceProperties(&prop, dev) == 0) { + strcpy(name, prop.name); + *total_mem = prop.totalGlobalMem; + *sm_count = prop.multiProcessorCount; + *major = prop.major; + *minor = prop.minor; + *max_threads = prop.maxThreadsPerBlock; + *warp_size = prop.warpSize; + } + } +} + +extern fn _z_cuda_get_props(dev: int, name: char*, mem: usize*, sm: int*, maj: int*, min: int*, max_t: int*, warp: int*); + +struct CudaDeviceProp { + name: String; + total_global_mem: usize; + multi_processor_count: int; + major: int; + minor: int; + max_threads_per_block: int; + warp_size: int; +} + +struct CudaMemInfo { + free: usize; + total: usize; +} + +fn cuda_device_properties(device_id: int) -> CudaDeviceProp { + var mem: usize = 0; + var sm: int = 0; + var maj: int = 0; + var min: int = 0; + var max_t: int = 0; + var warp: int = 0; + + var name_ptr = alloc_n<char>(256); + name_ptr[0] = 0; + + _z_cuda_get_props(device_id, name_ptr, &mem, &sm, &maj, &min, &max_t, &warp); + + var s = String::new(name_ptr); + free(name_ptr); + + return CudaDeviceProp { + name: s, + total_global_mem: mem, + multi_processor_count: sm, + major: maj, + minor: min, + max_threads_per_block: max_t, + warp_size: warp + }; +} + +fn cuda_driver_version() -> int { + var d: int = 0; + cudaDriverGetVersion(&d); + return d; +} + +fn cuda_runtime_version() -> int { + var r: int = 0; + cudaRuntimeGetVersion(&r); + return r; +} + +fn cuda_mem_info() -> CudaMemInfo { + var f: usize = 0; + var t: usize = 0; + cudaMemGetInfo(&f, &t); + return CudaMemInfo { free: f, total: t }; +} + +fn cuda_device_reset() { + cudaDeviceReset(); +} @@ -14,6 +14,30 @@ struct VecIter<T> { idx: usize; } +struct VecIterResult<T> { + ptr: T*; +} + +impl VecIterResult<T> { + fn is_none(self) -> bool { + return self.ptr == 0; + } + + fn unwrap(self) -> T* { + if (self.ptr == 0) { + !"Panic: unwrap called on null VecIterResult"; + exit(1); + } + return self.ptr; + } +} + +struct VecIterRef<T> { + data: T*; + count: usize; + idx: usize; +} + impl VecIter<T> { fn next(self) -> Option<T> { if (self.idx < self.count) { @@ -23,6 +47,25 @@ impl VecIter<T> { } return Option<T>::None(); } + + fn iterator(self) -> VecIter<T> { + return *self; + } +} + +impl VecIterRef<T> { + fn next(self) -> VecIterResult<T> { + if (self.idx < self.count) { + var item = &self.data[self.idx]; + self.idx = self.idx + 1; + return VecIterResult<T> { ptr: item }; + } + return VecIterResult<T> { ptr: 0 }; + } + + fn iterator(self) -> VecIterRef<T> { + return *self; + } } impl Vec<T> { @@ -30,6 +73,23 @@ impl Vec<T> { return Vec<T> { data: 0, len: 0, cap: 0 }; } + fn with_capacity(cap: usize) -> Vec<T> { + if (cap == 0) { + return Vec<T> { data: 0, len: 0, cap: 0 }; + } + return Vec<T> { + data: (T*)malloc(cap * sizeof(T)), + len: 0, + cap: cap + }; + } + + fn grow(self) { + if (self.cap == 0) { self.cap = 8; } + else { self.cap = self.cap * 2; } + self.data = (T*)realloc(self.data, self.cap * sizeof(T)); + } + fn iterator(self) -> VecIter<T> { return VecIter<T> { data: self.data, @@ -37,16 +97,21 @@ impl Vec<T> { idx: 0 }; } + + fn iter_ref(self) -> VecIterRef<T> { + return VecIterRef<T> { + data: self.data, + count: self.len, + idx: 0 + }; + } fn push(self, item: T) { if (self.len >= self.cap) { - if (self.cap == 0) { self.cap = 8; - } - else { self.cap = self.cap * 2; - } - self.data = realloc(self.data, self.cap * sizeof(T)); + self.grow(); } self.data[self.len] = item; + self.data[self.len] = item; self.len = self.len + 1; } @@ -56,11 +121,7 @@ impl Vec<T> { exit(1); } if (self.len >= self.cap) { - if (self.cap == 0) { self.cap = 8; - } - else { self.cap = self.cap * 2; - } - self.data = realloc(self.data, self.cap * sizeof(T)); + self.grow(); } // Shift elements right if (idx < self.len) { @@ -79,6 +140,14 @@ impl Vec<T> { return self.data[self.len]; } + fn pop_opt(self) -> Option<T> { + if (self.len == 0) { + return Option<T>::None(); + } + self.len = self.len - 1; + return Option<T>::Some(self.data[self.len]); + } + fn remove(self, idx: usize) -> T { if (idx >= self.len) { !"Panic: Remove index out of bounds"; @@ -176,12 +245,26 @@ impl Vec<T> { } fn clone(self) -> Vec<T> { - var new_vec: Vec<T> = Vec<T>::new(); + if (self.len == 0) { + return Vec<T> { data: 0, len: 0, cap: 0 }; + } + var new_data = (T*)malloc(self.len * sizeof(T)); var i: usize = 0; while i < self.len { - new_vec.push(self.data[i]); + new_data[i] = self.data[i]; i = i + 1; } - return new_vec; + return Vec<T> { + data: new_data, + len: self.len, + cap: self.len // Set capacity to exact length + }; + // No local Vec variable means no Drop is called here. + } +} + +impl Drop for Vec { + fn drop(self) { + self.free(); } } diff --git a/tests/collections/test_vec_suite.zc b/tests/collections/test_vec_suite.zc deleted file mode 100644 index 9722972..0000000 --- a/tests/collections/test_vec_suite.zc +++ /dev/null @@ -1,177 +0,0 @@ - -import "std.zc" - -@derive(Eq, Clone) -struct Point { - x: int; - y: int; -} - -test "test_vec_methods" { - println "Testing Vec methods..."; - - // Insert at beginning - var v = Vec<int>::new(); - v.push(10); - v.push(20); - v.push(30); - v.insert(0, 5); - - if (v.get(0) == 5 && v.get(1) == 10 && v.length() == 4) { - println " -> insert(0, 5): Passed"; - } else { - assert(false, "insert(0, 5) failed"); - } - - // Insert in middle - v.insert(2, 15); - if (v.get(2) == 15 && v.get(3) == 20 && v.length() == 5) { - println " -> insert(2, 15): Passed"; - } else { - assert(false, "insert(2, 15) failed"); - } - - // Insert at end - v.insert(5, 35); - if (v.get(5) == 35 && v.length() == 6) { - println " -> insert(5, 35): Passed"; - } else { - assert(false, "insert(5, 35) failed"); - } - - // Remove from beginning - var removed = v.remove(0); - if (removed == 5 && v.get(0) == 10 && v.length() == 5) { - println " -> remove(0): Passed"; - } else { - assert(false, "remove(0) failed"); - } - - // Remove from middle - var removed2 = v.remove(1); - if (removed2 == 15 && v.get(1) == 20 && v.length() == 4) { - println " -> remove(1): Passed"; - } else { - assert(false, "remove(1) failed"); - } - - // Remove from end - var removed3 = v.remove(3); - if (removed3 == 35 && v.length() == 3) { - println " -> remove(3): Passed"; - } else { - assert(false, "remove(3) failed"); - } - - // Verify final state - if (v.get(0) == 10 && v.get(1) == 20 && v.get(2) == 30) { - println " -> final state: Passed"; - } else { - assert(false, "final state failed"); - } - - println "All Vec methods passed!"; -} - -test "test_vec_extensions" { - println "Testing Vec extensions (pop, clear, etc)..."; - var v = Vec<int>::new(); - v.push(10); - v.push(20); - v.push(30); - - assert(v.length() == 3, "Length should be 3"); - assert(v.last() == 30, "Last should be 30"); - - var popped = v.pop(); - println "Popped: {popped}"; - assert(popped == 30, "Popped value mismatch"); - assert(v.length() == 2, "Length after pop mismatch"); - - assert(!v.is_empty(), "Vector should not be empty"); - - v.clear(); - assert(v.is_empty(), "Vector should be empty after clear"); - assert(v.length() == 0, "Length should be 0"); - - v.free(); -} - -test "test_vec_basic" { - var v = Vec<int>::new(); - v.push(10); - v.push(10); - v.push(20); - var v0 = v.get(0); - var v1 = v.get(1); - println "Vec: {v0}, {v1}"; - - var s1 = String::from("Hello "); - var s2 = String::from("World"); - var s3 = s1 + s2; - - println "{s3.c_str()}"; -} - -test "test_vec_clone" { - var v = Vec<int>::new(); - v.push(10); - v.push(20); - v.push(30); - - println "Vec has {(int)v.len} elements"; - println "First: {v.first()}"; - println "Last: {v.last()}"; - - v.free(); - "Done"; -} - -test "test_vec_contains" { - println "Testing Vec::contains with structs..."; - - var v = Vec<Point>::new(); - v.push(Point { x: 1, y: 2 }); - v.push(Point { x: 3, y: 4 }); - - var p1 = Point { x: 1, y: 2 }; - if (v.contains(p1)) { - println " -> contains((1, 2)): Passed"; - } else { - assert(false, "contains({1, 2}) failed"); - } - - var p2 = Point { x: 3, y: 4 }; - if (v.contains(p2)) { - println " -> contains((3, 4)): Passed"; - } else { - assert(false, "contains({3, 4}) failed"); - } - - var p3 = Point { x: 5, y: 6 }; - if (!v.contains(p3)) { - println " -> !contains((5, 6)): Passed"; - } else { - assert(false, "!contains({5, 6}) failed"); - } - - var vi = Vec<int>::new(); - vi.push(10); - vi.push(20); - - if (vi.contains(10)) { - println " -> Vec<int>::contains(10): Passed"; - } else { - assert(false, "Vec<int>::contains(10) failed"); - } - - if (!vi.contains(30)) { - println " -> !Vec<int>::contains(30): Passed"; - } else { - assert(false, "!Vec<int>::contains(30) failed"); - } - - v.free(); - vi.free(); - println "All tests passed!"; -} diff --git a/tests/std/test_vec.zc b/tests/std/test_vec.zc new file mode 100644 index 0000000..4787e06 --- /dev/null +++ b/tests/std/test_vec.zc @@ -0,0 +1,168 @@ +import "std/vec.zc" +import "std/io.zc" + +// Helper for assertions +fn assert_true(cond: bool, msg: char*) { + if (!cond) { + print "Assertion failed: {msg}\n"; + exit(1); + } +} + +fn assert_eq(a: int, b: int, msg: char*) { + if (a != b) { + print "Assertion failed: {msg} (Expected {a}, Got {b})\n"; + exit(1); + } +} + +@derive(Eq, Clone) +struct Point { + x: int; + y: int; +} + +test "Vec Basics (Construction, Push, Pop, Access)" { + print "Testing Vec basics...\n"; + var v = Vec<int>::new(); + assert_eq(v.len, 0, "Initial len"); + assert_eq(v.cap, 0, "Initial cap"); + + v.push(10); + v.push(20); + v.push(30); + + assert_eq(v.len, 3, "Len after push"); + assert_eq(v.get(0), 10, "get(0)"); + assert_eq(v.get(1), 20, "get(1)"); + assert_eq(v.get(2), 30, "get(2)"); + assert_eq(v.last(), 30, "last()"); + assert_eq(v.first(), 10, "first()"); + + assert_eq(v.pop(), 30, "pop()"); + assert_eq(v.len, 2, "Len after pop"); + + v.set(0, 99); + assert_eq(v.get(0), 99, "set()"); + + // Explicit clean up check (safe idempotent free) + v.free(); + assert_eq(v.len, 0, "Len after free"); +} + +test "Vec Capacity and Allocation" { + print "Testing Vec capacity...\n"; + // with_capacity + var v1 = Vec<int>::with_capacity(10); + assert_eq(v1.len, 0, "with_capacity len"); + assert_eq(v1.cap, 10, "with_capacity cap"); + v1.push(1); + assert_eq(v1.cap, 10, "Cap should not change yet"); + + // Implicit grow + var v2 = Vec<int>::new(); + // Force grow logic: 0 -> 8 -> 16... + var i = 0; + while i < 9 { + v2.push(i); + i = i + 1; + } + assert_true(v2.cap >= 9, "Capacity grew to hold 9 items"); + // Expected strategy: 0 -> 8 -> 16 + assert_eq(v2.cap, 16, "Growth strategy check (8->16)"); +} + +test "Vec Modification (Insert, Remove, Clear)" { + print "Testing Vec modification...\n"; + var v = Vec<int>::new(); + v.push(1); + v.push(3); + + v.insert(1, 2); // [1, 2, 3] + assert_eq(v.get(1), 2, "insert middle"); + assert_eq(v.len, 3, "insert len"); + + var val = v.remove(0); // [2, 3] + assert_eq(val, 1, "remove return"); + assert_eq(v.get(0), 2, "remove shift"); + assert_eq(v.len, 2, "remove len"); + + v.clear(); + assert_eq(v.len, 0, "clear len"); + assert_true(v.is_empty(), "is_empty"); +} + +test "Vec Extensions (pop_opt)" { + print "Testing Vec extensions...\n"; + var v = Vec<int>::new(); + v.push(42); + + var opt = v.pop_opt(); + assert_true(!opt.is_none(), "pop_opt some"); + assert_eq(opt.unwrap(), 42, "pop_opt val"); + + var empty = v.pop_opt(); + assert_true(empty.is_none(), "pop_opt none"); +} + +test "Vec Iteration (Reference and Value)" { + print "Testing Vec iteration...\n"; + var v = Vec<Point>::new(); + v.push(Point { x: 10, y: 10 }); + v.push(Point { x: 20, y: 20 }); + + // 1. Value Iteration + var sum_val = 0; + for p in v { + sum_val = sum_val + p.x; + } + assert_eq(sum_val, 30, "Value iteration sum"); + + // 2. Reference Iteration (Sugar &v) - In-place modification + for p in &v { + (*p).x = (*p).x + 1; + } + assert_eq(v.get(0).x, 11, "Ref iter mod 0"); + assert_eq(v.get(1).x, 21, "Ref iter mod 1"); + + // 3. Explicit iter_ref() + for p in v.iter_ref() { + (*p).y = (*p).y + 2; + } + assert_eq(v.get(0).y, 12, "Explicit ref iter mod 0"); + assert_eq(v.get(1).y, 22, "Explicit ref iter mod 1"); +} + +test "Vec Clone and Drop" { + print "Testing Vec clone and drop...\n"; + var v = Vec<int>::new(); + v.push(100); + v.push(200); + + var v2 = v.clone(); + assert_eq(v2.len, 2, "Clone len"); + assert_eq(v2.get(0), 100, "Clone val 0"); + + v.set(0, 999); + assert_eq(v.get(0), 999, "Original mod"); + assert_eq(v2.get(0), 100, "Clone independent"); + + // Drop is implicit at end of scope. + // We trust valgrind/previous verification for actual memory free. + // This test ensures no double-free crashes or logic errors. +} + +test "Vec Utils (Reverse, Contains)" { + print "Testing Vec utils...\n"; + var v = Vec<int>::new(); + v.push(1); + v.push(2); + v.push(3); + + v.reverse(); // [3, 2, 1] + assert_eq(v.get(0), 3, "Reverse 0"); + assert_eq(v.get(2), 1, "Reverse 2"); + + assert_true(v.contains(2), "Contains true"); + assert_true(!v.contains(99), "Contains false"); +} |
