fix: memory and other improvements
This commit is contained in:
@@ -29,8 +29,18 @@ struct Canvas {
|
||||
|
||||
void fill(Color c) {
|
||||
uint32_t px = c.to_pixel();
|
||||
uint64_t px2 = ((uint64_t)px << 32) | px;
|
||||
int total = w * h;
|
||||
for (int i = 0; i < total; i++) pixels[i] = px;
|
||||
int i = 0;
|
||||
// Align to 8-byte boundary if needed
|
||||
if (total > 0 && ((uint64_t)pixels & 4)) {
|
||||
pixels[0] = px;
|
||||
i = 1;
|
||||
}
|
||||
uint64_t* dst64 = (uint64_t*)(pixels + i);
|
||||
int pairs = (total - i) / 2;
|
||||
for (int p = 0; p < pairs; p++) dst64[p] = px2;
|
||||
if ((total - i) & 1) pixels[total - 1] = px;
|
||||
}
|
||||
|
||||
void put_pixel(int x, int y, Color c) {
|
||||
@@ -40,11 +50,25 @@ struct Canvas {
|
||||
|
||||
void fill_rect(int x, int y, int rw, int rh, Color c) {
|
||||
uint32_t px = c.to_pixel();
|
||||
uint64_t px2 = ((uint64_t)px << 32) | px;
|
||||
int x0 = gui_max(x, 0), y0 = gui_max(y, 0);
|
||||
int x1 = gui_min(x + rw, w), y1 = gui_min(y + rh, h);
|
||||
for (int dy = y0; dy < y1; dy++)
|
||||
for (int dx = x0; dx < x1; dx++)
|
||||
pixels[dy * w + dx] = px;
|
||||
for (int dy = y0; dy < y1; dy++) {
|
||||
uint32_t* row = pixels + dy * w;
|
||||
int dx = x0;
|
||||
// Align to 8-byte boundary
|
||||
if (dx < x1 && ((uint64_t)(row + dx) & 4)) {
|
||||
row[dx] = px;
|
||||
dx++;
|
||||
}
|
||||
// Bulk 2-pixel writes
|
||||
uint64_t* dst64 = (uint64_t*)(row + dx);
|
||||
int pairs = (x1 - dx) / 2;
|
||||
for (int p = 0; p < pairs; p++) dst64[p] = px2;
|
||||
dx += pairs * 2;
|
||||
// Remainder
|
||||
if (dx < x1) row[dx] = px;
|
||||
}
|
||||
}
|
||||
|
||||
void fill_rounded_rect(int x, int y, int rw, int rh, int radius, Color c) {
|
||||
@@ -77,8 +101,18 @@ struct Canvas {
|
||||
if (y < 0 || y >= h) return;
|
||||
uint32_t px = c.to_pixel();
|
||||
int x0 = gui_max(x, 0), x1 = gui_min(x + len, w);
|
||||
for (int dx = x0; dx < x1; dx++)
|
||||
pixels[y * w + dx] = px;
|
||||
uint32_t* row = pixels + y * w;
|
||||
int dx = x0;
|
||||
if (dx < x1 && ((uint64_t)(row + dx) & 4)) {
|
||||
row[dx] = px;
|
||||
dx++;
|
||||
}
|
||||
uint64_t px2 = ((uint64_t)px << 32) | px;
|
||||
uint64_t* dst64 = (uint64_t*)(row + dx);
|
||||
int pairs = (x1 - dx) / 2;
|
||||
for (int p = 0; p < pairs; p++) dst64[p] = px2;
|
||||
dx += pairs * 2;
|
||||
if (dx < x1) row[dx] = px;
|
||||
}
|
||||
|
||||
void vline(int x, int y, int len, Color c) {
|
||||
|
||||
@@ -136,11 +136,15 @@ struct TrueTypeFont {
|
||||
return gc;
|
||||
}
|
||||
|
||||
// Evict oldest cache (slot 0) and shift others down
|
||||
GlyphCache evicted = caches[0];
|
||||
// Evict oldest cache (slot 0) — free its glyph bitmaps
|
||||
for (int g = 0; g < 256; g++) {
|
||||
if (caches[0].glyphs[g].bitmap)
|
||||
montauk::mfree(caches[0].glyphs[g].bitmap);
|
||||
}
|
||||
// Shift remaining caches down
|
||||
for (int i = 0; i < 3; i++)
|
||||
caches[i] = caches[i + 1];
|
||||
caches[3] = evicted;
|
||||
montauk::memcpy(&caches[i], &caches[i + 1], sizeof(GlyphCache));
|
||||
montauk::memset(&caches[3], 0, sizeof(GlyphCache));
|
||||
init_cache(&caches[3], pixel_size);
|
||||
return &caches[3];
|
||||
}
|
||||
|
||||
+154
-48
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* heap.h
|
||||
* Userspace heap allocator for MontaukOS programs
|
||||
* Free-list allocator backed by SYS_ALLOC page requests.
|
||||
* Adapted from the kernel HeapAllocator.
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/string.h>
|
||||
|
||||
namespace montauk {
|
||||
namespace heap_detail {
|
||||
|
||||
static constexpr uint64_t HEADER_MAGIC = 0x5A484541; // "ZHEA"
|
||||
static constexpr uint64_t FREED_MAGIC = 0xDEADFEEE;
|
||||
|
||||
struct Header {
|
||||
uint64_t magic;
|
||||
@@ -24,29 +24,118 @@ namespace heap_detail {
|
||||
FreeNode* next;
|
||||
};
|
||||
|
||||
// Segregated free lists: power-of-2 size classes for blocks <= 4096 bytes.
|
||||
// Blocks larger than 4096 go to the overflow list.
|
||||
static constexpr int NUM_BUCKETS = 8;
|
||||
static constexpr uint64_t BUCKET_SIZES[NUM_BUCKETS] = {
|
||||
32, 64, 128, 256, 512, 1024, 2048, 4096
|
||||
};
|
||||
|
||||
// Per-process heap state — must be `inline` (not `static`) so that all
|
||||
// translation units in a multi-TU program share a single heap.
|
||||
inline FreeNode g_head{0, nullptr};
|
||||
inline FreeNode* g_buckets[NUM_BUCKETS] = {};
|
||||
inline FreeNode g_overflow{0, nullptr};
|
||||
inline bool g_initialized = false;
|
||||
|
||||
static inline Header* get_header(void* block) {
|
||||
return (Header*)((uint8_t*)block - sizeof(Header));
|
||||
}
|
||||
|
||||
static inline void insert_free(void* ptr, uint64_t size) {
|
||||
auto* node = (FreeNode*)ptr;
|
||||
node->size = size;
|
||||
node->next = g_head.next;
|
||||
g_head.next = node;
|
||||
// Determine which bucket a block size belongs to, or -1 for overflow
|
||||
static inline int bucket_index(uint64_t blockSize) {
|
||||
if (blockSize <= 32) return 0;
|
||||
if (blockSize <= 64) return 1;
|
||||
if (blockSize <= 128) return 2;
|
||||
if (blockSize <= 256) return 3;
|
||||
if (blockSize <= 512) return 4;
|
||||
if (blockSize <= 1024) return 5;
|
||||
if (blockSize <= 2048) return 6;
|
||||
if (blockSize <= 4096) return 7;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Insert into overflow list (sorted by address, with adjacent-block coalescing)
|
||||
static inline void insert_overflow(void* ptr, uint64_t size) {
|
||||
auto* node = (FreeNode*)ptr;
|
||||
node->size = size;
|
||||
|
||||
FreeNode* prev = &g_overflow;
|
||||
FreeNode* cur = g_overflow.next;
|
||||
while (cur != nullptr && cur < node) {
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
bool merged_prev = false;
|
||||
if (prev != &g_overflow &&
|
||||
(uint8_t*)prev + prev->size == (uint8_t*)node) {
|
||||
prev->size += size;
|
||||
node = prev;
|
||||
merged_prev = true;
|
||||
}
|
||||
|
||||
if (cur != nullptr &&
|
||||
(uint8_t*)node + node->size == (uint8_t*)cur) {
|
||||
node->size += cur->size;
|
||||
node->next = cur->next;
|
||||
if (!merged_prev) prev->next = node;
|
||||
} else if (!merged_prev) {
|
||||
node->next = cur;
|
||||
prev->next = node;
|
||||
}
|
||||
}
|
||||
|
||||
// Take a block of at least `needed` bytes from the overflow list.
|
||||
// Splits remainder back into overflow if worthwhile.
|
||||
static inline void* take_from_overflow(uint64_t needed) {
|
||||
FreeNode* prev = &g_overflow;
|
||||
FreeNode* cur = g_overflow.next;
|
||||
|
||||
while (cur != nullptr) {
|
||||
if (cur->size >= needed) {
|
||||
uint64_t blockSize = cur->size;
|
||||
prev->next = cur->next;
|
||||
|
||||
if (blockSize > needed + sizeof(FreeNode) + 16) {
|
||||
insert_overflow((uint8_t*)cur + needed, blockSize - needed);
|
||||
}
|
||||
return (void*)cur;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool grow(uint64_t bytes) {
|
||||
uint64_t pages = (bytes + 0xFFF) / 0x1000;
|
||||
if (pages < 4) pages = 4; // grow at least 16 KiB at a time
|
||||
if (pages < 4) pages = 4;
|
||||
|
||||
void* mem = montauk::alloc(pages * 0x1000);
|
||||
if (mem == nullptr) return false;
|
||||
insert_free(mem, pages * 0x1000);
|
||||
insert_overflow(mem, pages * 0x1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Refill a small-block bucket by carving a page-sized chunk from overflow
|
||||
static inline bool refill_bucket(int idx) {
|
||||
uint64_t bsize = BUCKET_SIZES[idx];
|
||||
uint64_t chunk = (bsize < 4096) ? 4096 : bsize;
|
||||
|
||||
void* block = take_from_overflow(chunk);
|
||||
if (block == nullptr) {
|
||||
if (!grow(chunk)) return false;
|
||||
block = take_from_overflow(chunk);
|
||||
if (block == nullptr) return false;
|
||||
}
|
||||
|
||||
uint64_t count = chunk / bsize;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
auto* node = (FreeNode*)((uint8_t*)block + i * bsize);
|
||||
node->size = bsize;
|
||||
node->next = g_buckets[idx];
|
||||
g_buckets[idx] = node;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,41 +152,36 @@ namespace heap_detail {
|
||||
}
|
||||
|
||||
uint64_t needed = size + sizeof(Header);
|
||||
needed = (needed + 15) & ~15ULL; // 16-byte alignment
|
||||
needed = (needed + 15) & ~15ULL;
|
||||
|
||||
FreeNode* prev = &g_head;
|
||||
FreeNode* current = g_head.next;
|
||||
int idx = bucket_index(needed);
|
||||
|
||||
while (current != nullptr) {
|
||||
if (current->size >= needed) {
|
||||
uint64_t blockSize = current->size;
|
||||
if (idx >= 0) {
|
||||
// Small allocation — use segregated bucket (O(1))
|
||||
if (g_buckets[idx] == nullptr && !refill_bucket(idx))
|
||||
return nullptr;
|
||||
|
||||
// Unlink
|
||||
prev->next = current->next;
|
||||
FreeNode* node = g_buckets[idx];
|
||||
g_buckets[idx] = node->next;
|
||||
|
||||
// Split if worthwhile
|
||||
if (blockSize > needed + sizeof(FreeNode) + 16) {
|
||||
void* rest = (void*)((uint8_t*)current + needed);
|
||||
uint64_t restSize = blockSize - needed;
|
||||
insert_free(rest, restSize);
|
||||
}
|
||||
|
||||
// Write allocation header
|
||||
Header* header = (Header*)current;
|
||||
header->magic = HEADER_MAGIC;
|
||||
header->size = size;
|
||||
|
||||
return (void*)((uint8_t*)header + sizeof(Header));
|
||||
}
|
||||
|
||||
prev = current;
|
||||
current = current->next;
|
||||
Header* header = (Header*)node;
|
||||
header->magic = HEADER_MAGIC;
|
||||
header->size = size;
|
||||
return (void*)((uint8_t*)header + sizeof(Header));
|
||||
}
|
||||
|
||||
// No fit — grow and retry
|
||||
if (!grow(needed))
|
||||
return nullptr;
|
||||
return malloc(size);
|
||||
// Large allocation — search overflow list
|
||||
void* block = take_from_overflow(needed);
|
||||
if (block == nullptr) {
|
||||
if (!grow(needed)) return nullptr;
|
||||
block = take_from_overflow(needed);
|
||||
if (block == nullptr) return nullptr;
|
||||
}
|
||||
|
||||
Header* header = (Header*)block;
|
||||
header->magic = HEADER_MAGIC;
|
||||
header->size = size;
|
||||
return (void*)((uint8_t*)header + sizeof(Header));
|
||||
}
|
||||
|
||||
inline void mfree(void* ptr) {
|
||||
@@ -107,27 +191,49 @@ namespace heap_detail {
|
||||
|
||||
Header* header = get_header(ptr);
|
||||
|
||||
if (header->magic == FREED_MAGIC) return; // double-free
|
||||
if (header->magic != HEADER_MAGIC) return; // corrupt
|
||||
header->magic = FREED_MAGIC;
|
||||
|
||||
uint64_t blockSize = header->size + sizeof(Header);
|
||||
blockSize = (blockSize + 15) & ~15ULL;
|
||||
|
||||
insert_free((void*)header, blockSize);
|
||||
int idx = bucket_index(blockSize);
|
||||
|
||||
if (idx >= 0) {
|
||||
// Small block — push onto bucket (O(1))
|
||||
auto* node = (FreeNode*)header;
|
||||
node->size = BUCKET_SIZES[idx];
|
||||
node->next = g_buckets[idx];
|
||||
g_buckets[idx] = node;
|
||||
} else {
|
||||
// Large block — sorted insert with coalescing
|
||||
insert_overflow((void*)header, blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
inline void* realloc(void* ptr, uint64_t size) {
|
||||
if (ptr == nullptr) return malloc(size);
|
||||
|
||||
auto* header = heap_detail::get_header(ptr);
|
||||
uint64_t old = header->size;
|
||||
auto* header = heap_detail::get_header(ptr);
|
||||
uint64_t old = header->size;
|
||||
|
||||
// Compute actual block size (accounting for bucket rounding)
|
||||
uint64_t oldBlock = (old + sizeof(heap_detail::Header) + 15) & ~15ULL;
|
||||
int idx = heap_detail::bucket_index(oldBlock);
|
||||
if (idx >= 0) oldBlock = heap_detail::BUCKET_SIZES[idx];
|
||||
|
||||
uint64_t newNeed = (size + sizeof(heap_detail::Header) + 15) & ~15ULL;
|
||||
if (newNeed <= oldBlock) {
|
||||
header->size = size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* newBlock = malloc(size);
|
||||
if (newBlock == nullptr) return nullptr;
|
||||
|
||||
// Copy the smaller of old/new sizes
|
||||
uint64_t copySize = (old < size) ? old : size;
|
||||
uint8_t* dst = (uint8_t*)newBlock;
|
||||
uint8_t* src = (uint8_t*)ptr;
|
||||
for (uint64_t i = 0; i < copySize; i++)
|
||||
dst[i] = src[i];
|
||||
memcpy(newBlock, ptr, copySize);
|
||||
|
||||
mfree(ptr);
|
||||
return newBlock;
|
||||
|
||||
@@ -39,22 +39,58 @@ namespace montauk {
|
||||
inline void memcpy(void* dst, const void* src, uint64_t n) {
|
||||
auto* d = (uint8_t*)dst;
|
||||
auto* s = (const uint8_t*)src;
|
||||
for (uint64_t i = 0; i < n; i++) d[i] = s[i];
|
||||
|
||||
// Byte copy until 8-byte aligned
|
||||
while (n && ((uint64_t)d & 7)) { *d++ = *s++; n--; }
|
||||
|
||||
// Bulk 8-byte copy
|
||||
auto* d8 = (uint64_t*)d;
|
||||
auto* s8 = (const uint64_t*)s;
|
||||
uint64_t words = n / 8;
|
||||
for (uint64_t i = 0; i < words; i++) d8[i] = s8[i];
|
||||
|
||||
// Remainder
|
||||
d = (uint8_t*)(d8 + words);
|
||||
s = (const uint8_t*)(s8 + words);
|
||||
for (uint64_t i = 0; i < (n & 7); i++) d[i] = s[i];
|
||||
}
|
||||
|
||||
inline void memmove(void* dst, const void* src, uint64_t n) {
|
||||
auto* d = (uint8_t*)dst;
|
||||
auto* s = (const uint8_t*)src;
|
||||
if (d < s) {
|
||||
for (uint64_t i = 0; i < n; i++) d[i] = s[i];
|
||||
if (d < s || d >= s + n) {
|
||||
memcpy(dst, src, n);
|
||||
} else {
|
||||
for (uint64_t i = n; i > 0; i--) d[i-1] = s[i-1];
|
||||
// Backward copy — bulk 8 bytes at a time from end
|
||||
d += n; s += n;
|
||||
while (n && ((uint64_t)d & 7)) { *--d = *--s; n--; }
|
||||
auto* d8 = (uint64_t*)d;
|
||||
auto* s8 = (const uint64_t*)s;
|
||||
uint64_t words = n / 8;
|
||||
for (uint64_t i = 1; i <= words; i++) d8[-i] = s8[-i];
|
||||
d = (uint8_t*)(d8 - words);
|
||||
s = (const uint8_t*)(s8 - words);
|
||||
for (uint64_t i = 1; i <= (n & 7); i++) d[-i] = s[-i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void memset(void* dst, int val, uint64_t n) {
|
||||
auto* d = (uint8_t*)dst;
|
||||
for (uint64_t i = 0; i < n; i++) d[i] = (uint8_t)val;
|
||||
uint8_t v = (uint8_t)val;
|
||||
|
||||
// Byte fill until 8-byte aligned
|
||||
while (n && ((uint64_t)d & 7)) { *d++ = v; n--; }
|
||||
|
||||
// Bulk 8-byte fill
|
||||
uint64_t v8 = v;
|
||||
v8 |= v8 << 8; v8 |= v8 << 16; v8 |= v8 << 32;
|
||||
auto* d8 = (uint64_t*)d;
|
||||
uint64_t words = n / 8;
|
||||
for (uint64_t i = 0; i < words; i++) d8[i] = v8;
|
||||
|
||||
// Remainder
|
||||
d = (uint8_t*)(d8 + words);
|
||||
for (uint64_t i = 0; i < (n & 7); i++) d[i] = v;
|
||||
}
|
||||
|
||||
inline void strcpy(char* dst, const char* src) {
|
||||
|
||||
Reference in New Issue
Block a user