import "./core.zc" // --- MISRA static arena --- // A fixed-size arena used as the allocation backend in MISRA mode (where // malloc/calloc/free are disallowed). It lives here rather than in arena.zc so // that mem.zc does not import arena.zc: arena.zc implements Drop (which // auto-imports mem.zc), so a mem.zc -> arena.zc edge would form an import cycle. struct StaticArena { data: byte*; capacity: usize; used: usize; } // 64KB static buffer for MISRA modes let _z_misra_static_buffer: [byte; 65536]; impl StaticArena { fn init(self) { self.data = (byte*) &_z_misra_static_buffer; self.capacity = 65536; self.used = 0; } fn alloc_bytes(self, size: usize) -> void* { // Simple 8-byte alignment let aligned = (self.used + 7) & ~7; if (aligned + size > self.capacity) { // In MISRA mode, we panic on exhaustion rather than returning NULL // to ensure deterministic failure. !"Panic: MISRA Static Arena exhausted!"; exit(1); } let ptr: void* = (byte*)self.data + aligned; self.used = aligned + size; return ptr; } fn alloc(self) -> T* { let ptr: T* = (T*)self.alloc_bytes(sizeof(T)); if (ptr != NULL) { memset(ptr, 0, sizeof(T)); } return ptr; } fn alloc_n(self, count: usize) -> T* { let size = sizeof(T) * count; let ptr: T* = (T*)self.alloc_bytes(size); if (ptr != NULL) { memset(ptr, 0, size); } return ptr; } fn realloc(self, old_ptr: T*, old_count: usize, new_count: usize) -> T* { let new_size = sizeof(T) * new_count; let old_size = sizeof(T) * old_count; // If it's the most recent allocation, we can just grow it in place! let aligned_old = (self.used - old_size + 7) & ~7; if ((byte*)self.data + aligned_old == (byte*)old_ptr) { let new_aligned = (aligned_old + new_size + 7) & ~7; if (new_aligned <= self.capacity) { self.used = aligned_old + new_size; return old_ptr; } } // Otherwise, allocate new space and copy let new_ptr = (T*)self.alloc_bytes(new_size); if (old_ptr != NULL && old_size > 0) { memcpy(new_ptr, old_ptr, old_size); } return new_ptr; } } let _z_internal_arena: StaticArena; let _z_arena_initialized: bool = false; fn _z_get_std_arena() -> StaticArena* { if (!_z_arena_initialized) { _z_internal_arena.init(); _z_arena_initialized = true; } return &_z_internal_arena; } // --- Non-generic internal helpers for conditional compilation --- // These avoid the 'StringPtr' mangling bug in generic functions with @cfg attributes. @cfg(misra) fn _z_internal_alloc_bytes(size: usize) -> void* { return _z_get_std_arena().alloc_bytes(size); } @cfg(not(misra)) fn _z_internal_alloc_bytes(size: usize) -> void* { return calloc(1, size); } @cfg(misra) fn _z_internal_zalloc_bytes(size: usize) -> void* { let ptr = _z_get_std_arena().alloc_bytes(size); if (ptr != NULL) { memset(ptr, 0, size); } return ptr; } @cfg(not(misra)) fn _z_internal_zalloc_bytes(size: usize) -> void* { return calloc(1, size); } @cfg(misra) fn _z_internal_realloc_bytes(ptr: void*, old_size: usize, new_size: usize) -> void* { // In StaticArena, realloc needs the original type/count, but we can approximate // if we treat it as an array of bytes. return _z_get_std_arena().realloc((byte*)ptr, old_size, new_size); } @cfg(not(misra)) fn _z_internal_realloc_bytes(ptr: void*, old_size: usize, new_size: usize) -> void* { let new_ptr = realloc(ptr, new_size); if (new_ptr != NULL && new_size > old_size) { memset((char*)new_ptr + old_size, 0, new_size - old_size); } return new_ptr; } @cfg(misra) fn _z_internal_free(ptr: void*) { (void)ptr; // No-op in MISRA arena mode } @cfg(not(misra)) fn _z_internal_free(ptr: void*) { if (ptr != NULL) { free(ptr); } } // --- Public Generic API --- // These are NOT attribute-guarded to avoid compiler template bugs. fn alloc() -> T* { return (T*)_z_internal_alloc_bytes(sizeof(T)); } fn zalloc() -> T* { return (T*)_z_internal_zalloc_bytes(sizeof(T)); } fn alloc_n(n: usize) -> T* { return (T*)_z_internal_alloc_bytes(sizeof(T) * n); } fn realloc_n(ptr: T*, old_n: usize, new_n: usize) -> T* { return (T*)_z_internal_realloc_bytes((void*)ptr, sizeof(T) * old_n, sizeof(T) * new_n); } fn dealloc(ptr: T*) { _z_internal_free((void*)ptr); } // --- Raw Byte API for C Interop --- fn alloc_bytes(size: usize) -> void* { return _z_internal_alloc_bytes(size); } fn zalloc_bytes(size: usize) -> void* { return _z_internal_zalloc_bytes(size); } // --- Box and Traits --- trait Drop { fn drop(self); } trait Copy {} trait Clone { fn clone(self) -> Self; } struct Box { ptr: T*; } impl Box { fn new() -> Self { return Self { ptr: alloc() }; } fn from_ptr(p: T*) -> Self { return Self { ptr: p }; } fn get(self) -> T* { return self.ptr; } fn is_null(self) -> bool { return self.ptr == NULL; } fn free(self) { if self.ptr != NULL { dealloc(self.ptr); self.ptr = NULL; } } } impl Drop for Box { fn drop(self) { self.free(); } } // Note: Slice is defined in std/slice.zc with iteration support fn mem_zero(ptr: T*, count: usize) { memset(ptr, 0, sizeof(T) * count); } fn mem_copy(dst: T*, src: T*, count: usize) { memcpy(dst, src, sizeof(T) * count); } fn swap(a: T*, b: T*) { let tmp = *a; *a = *b; *b = tmp; }