Memory management
Quadrate provides manual memory management through the mem module.
The mem module
use mem
The mem module provides functions for allocating, freeing, and manipulating memory.
Allocating memory
Use mem::alloc to allocate bytes:
use mem
fn main() {
1024 mem::alloc! -> buf // Allocate 1024 bytes
defer {
buf mem::free // Free when function exits
}
// Use buffer...
}
Always pair alloc with free. Use defer to ensure cleanup.
Reading and writing memory
Write byte
use mem
fn main() {
10 mem::alloc! -> buf
65 buf 0 mem::set_byte // Write 'A' at offset 0
66 buf 1 mem::set_byte // Write 'B' at offset 1
buf 0 mem::get_byte print nl // 65
buf 1 mem::get_byte print nl // 66
buf mem::free
}
Write integers
use mem
fn main() {
8 mem::alloc! -> buf
42 buf 0 mem::set_i64
buf 0 mem::get_i64 print nl // 42
buf mem::free
}
Memory copy
Copy memory between buffers:
use mem
fn main() {
10 mem::alloc! -> src
10 mem::alloc! -> dst
defer {
src mem::free dst mem::free
}
// Fill source
0 10 1 for i {
i src i mem::set_byte
}
// Copy to destination
dst src 10 mem::copy
// Verify
dst 5 mem::get_byte print nl // 5
}
Memory fill
Fill memory with a value:
use mem
fn main() {
100 mem::alloc! -> buf
defer {
buf mem::free
}
0 buf 100 mem::fill // Zero all bytes
buf 50 mem::get_byte print nl // 0
}
Resizing memory
Use mem::realloc to resize:
use mem
fn main() {
10 mem::alloc! -> buf
// Grow buffer
buf 100 mem::realloc! -> buf
// Use larger buffer...
buf mem::free
}
Common patterns
Dynamic string buffer
use mem
struct StringBuffer {
data:ptr
len:i64
capacity:i64
}
fn sb_new(capacity:i64 -- sb:ptr) {
-> capacity // bind parameter
capacity mem::alloc! -> data
StringBuffer {
data = data
len = 0
capacity = capacity
}
}
fn sb_free(sb:ptr -- ) {
-> sb // bind parameter
sb @data mem::free
}
fn sb_append_byte(sb:ptr byte:i64 -- ) {
-> byte -> sb
sb @len sb @capacity >= if {
// Need to grow
sb @data sb @capacity 2 * mem::realloc sb.data
sb @capacity 2 * sb.capacity
}
byte sb @data sb @len mem::set_byte
sb @len 1 + sb.len
}
fn main() {
16 sb_new -> sb
defer {
sb sb_free
}
// Append "Hello"
sb 72 sb_append_byte // H
sb 101 sb_append_byte // e
sb 108 sb_append_byte // l
sb 108 sb_append_byte // l
sb 111 sb_append_byte // o
// Print length and contents
"len: " print sb @len print nl
sb @data sb @len mem::to_string print nl // Hello
}
Memory pool
use mem
struct Pool {
memory:ptr
size:i64
used:i64
}
fn pool_new(size:i64 -- pool:ptr) {
-> size // bind parameter
size mem::alloc! -> memory
Pool {
memory = memory
size = size
used = 0
}
}
fn pool_alloc(pool:ptr bytes:i64 -- offset:i64) {
-> bytes -> pool
pool @used bytes + pool @size <= if {
pool @used -> offset
pool @used bytes + pool.used
offset
} else {
-1 // Out of memory
}
}
fn pool_reset(pool:ptr -- ) {
-> pool // bind parameter
0 pool.used
}
fn pool_free(pool:ptr -- ) {
-> pool // bind parameter
pool @memory mem::free
}
fn main() {
1024 pool_new -> pool
defer {
pool pool_free
}
// Allocate from pool (returns offset)
pool 100 pool_alloc -> off1
pool 200 pool_alloc -> off2
"off1: " print off1 print nl // 0
"off2: " print off2 print nl // 100
"pool used: " print pool @used print nl // 300
// Write to allocated regions
42 pool @memory off1 mem::set_i64
99 pool @memory off2 mem::set_i64
// Read back
pool @memory off1 mem::get_i64 print nl // 42
pool @memory off2 mem::get_i64 print nl // 99
// Reset pool for reuse
pool pool_reset
"after reset: " print pool @used print nl // 0
}
What's next?
Learn about FFI (C Interop) to call C functions from Quadrate.