fix: memory and other improvements

This commit is contained in:
2026-03-06 22:09:11 +01:00
parent d682a49c77
commit 56fea336d6
14 changed files with 445 additions and 107 deletions
+4 -3
View File
@@ -18,7 +18,7 @@ namespace Montauk {
uint64_t numPages; uint64_t numPages;
}; };
static constexpr int MaxHeapAllocs = 128; static constexpr int MaxHeapAllocs = 512;
static HeapAlloc g_heapAllocs[Sched::MaxProcesses][MaxHeapAllocs]; static HeapAlloc g_heapAllocs[Sched::MaxProcesses][MaxHeapAllocs];
static int g_heapAllocCount[Sched::MaxProcesses]; static int g_heapAllocCount[Sched::MaxProcesses];
@@ -43,6 +43,7 @@ namespace Montauk {
uint64_t userVa = proc->heapNext; uint64_t userVa = proc->heapNext;
uint64_t numPages = size / 0x1000; uint64_t numPages = size / 0x1000;
// Allocate physical pages and map them into the process
for (uint64_t i = 0; i < numPages; i++) { for (uint64_t i = 0; i < numPages; i++) {
void* page = Memory::g_pfa->AllocateZeroed(); void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) return 0; if (page == nullptr) return 0;
@@ -90,7 +91,7 @@ namespace Montauk {
uint64_t va = g_heapAllocs[slot][idx].va; uint64_t va = g_heapAllocs[slot][idx].va;
uint64_t numPages = g_heapAllocs[slot][idx].numPages; uint64_t numPages = g_heapAllocs[slot][idx].numPages;
// Free each physical page and unmap the virtual address // Free physical pages in bulk and unmap virtual addresses
for (uint64_t i = 0; i < numPages; i++) { for (uint64_t i = 0; i < numPages; i++) {
uint64_t pageVa = va + i * 0x1000; uint64_t pageVa = va + i * 0x1000;
uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa); uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa);
@@ -100,7 +101,7 @@ namespace Montauk {
Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa); Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa);
} }
if (slot >= 0) Sched::g_allocatedPages[slot] -= numPages; Sched::g_allocatedPages[slot] -= numPages;
// Remove tracking entry by swapping with the last element // Remove tracking entry by swapping with the last element
g_heapAllocs[slot][idx] = g_heapAllocs[slot][g_heapAllocCount[slot] - 1]; g_heapAllocs[slot][idx] = g_heapAllocs[slot][g_heapAllocCount[slot] - 1];
+1 -1
View File
@@ -13,7 +13,7 @@ namespace Sched {
static constexpr int MaxProcesses = 256; static constexpr int MaxProcesses = 256;
static constexpr uint64_t StackPages = 4; // 16 KiB kernel stack per process static constexpr uint64_t StackPages = 4; // 16 KiB kernel stack per process
static constexpr uint64_t StackSize = StackPages * 0x1000; static constexpr uint64_t StackSize = StackPages * 0x1000;
static constexpr uint64_t UserStackPages = 4; // 16 KiB user stack static constexpr uint64_t UserStackPages = 8; // 32 KiB user stack
static constexpr uint64_t UserStackSize = UserStackPages * 0x1000; static constexpr uint64_t UserStackSize = UserStackPages * 0x1000;
static constexpr uint64_t UserStackTop = 0x7FFFFFF000ULL; // User stack top VA static constexpr uint64_t UserStackTop = 0x7FFFFFF000ULL; // User stack top VA
static constexpr uint64_t UserHeapBase = 0x40000000ULL; // User heap start VA static constexpr uint64_t UserHeapBase = 0x40000000ULL; // User heap start VA
+40 -6
View File
@@ -29,8 +29,18 @@ struct Canvas {
void fill(Color c) { void fill(Color c) {
uint32_t px = c.to_pixel(); uint32_t px = c.to_pixel();
uint64_t px2 = ((uint64_t)px << 32) | px;
int total = w * h; 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) { 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) { void fill_rect(int x, int y, int rw, int rh, Color c) {
uint32_t px = c.to_pixel(); 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 x0 = gui_max(x, 0), y0 = gui_max(y, 0);
int x1 = gui_min(x + rw, w), y1 = gui_min(y + rh, h); int x1 = gui_min(x + rw, w), y1 = gui_min(y + rh, h);
for (int dy = y0; dy < y1; dy++) for (int dy = y0; dy < y1; dy++) {
for (int dx = x0; dx < x1; dx++) uint32_t* row = pixels + dy * w;
pixels[dy * w + dx] = px; 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) { 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; if (y < 0 || y >= h) return;
uint32_t px = c.to_pixel(); uint32_t px = c.to_pixel();
int x0 = gui_max(x, 0), x1 = gui_min(x + len, w); int x0 = gui_max(x, 0), x1 = gui_min(x + len, w);
for (int dx = x0; dx < x1; dx++) uint32_t* row = pixels + y * w;
pixels[y * w + dx] = px; 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) { void vline(int x, int y, int len, Color c) {
+8 -4
View File
@@ -136,11 +136,15 @@ struct TrueTypeFont {
return gc; return gc;
} }
// Evict oldest cache (slot 0) and shift others down // Evict oldest cache (slot 0) — free its glyph bitmaps
GlyphCache evicted = caches[0]; 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++) for (int i = 0; i < 3; i++)
caches[i] = caches[i + 1]; montauk::memcpy(&caches[i], &caches[i + 1], sizeof(GlyphCache));
caches[3] = evicted; montauk::memset(&caches[3], 0, sizeof(GlyphCache));
init_cache(&caches[3], pixel_size); init_cache(&caches[3], pixel_size);
return &caches[3]; return &caches[3];
} }
+144 -38
View File
@@ -1,18 +1,18 @@
/* /*
* heap.h * heap.h
* Userspace heap allocator for MontaukOS programs * 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 #pragma once
#include <montauk/syscall.h> #include <montauk/syscall.h>
#include <montauk/string.h>
namespace montauk { namespace montauk {
namespace heap_detail { namespace heap_detail {
static constexpr uint64_t HEADER_MAGIC = 0x5A484541; // "ZHEA" static constexpr uint64_t HEADER_MAGIC = 0x5A484541; // "ZHEA"
static constexpr uint64_t FREED_MAGIC = 0xDEADFEEE;
struct Header { struct Header {
uint64_t magic; uint64_t magic;
@@ -24,29 +24,118 @@ namespace heap_detail {
FreeNode* next; 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 // Per-process heap state — must be `inline` (not `static`) so that all
// translation units in a multi-TU program share a single heap. // 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; inline bool g_initialized = false;
static inline Header* get_header(void* block) { static inline Header* get_header(void* block) {
return (Header*)((uint8_t*)block - sizeof(Header)); return (Header*)((uint8_t*)block - sizeof(Header));
} }
static inline void insert_free(void* ptr, uint64_t size) { // 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; auto* node = (FreeNode*)ptr;
node->size = size; node->size = size;
node->next = g_head.next;
g_head.next = node; 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) { static inline bool grow(uint64_t bytes) {
uint64_t pages = (bytes + 0xFFF) / 0x1000; 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); void* mem = montauk::alloc(pages * 0x1000);
if (mem == nullptr) return false; 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; return true;
} }
@@ -63,41 +152,36 @@ namespace heap_detail {
} }
uint64_t needed = size + sizeof(Header); uint64_t needed = size + sizeof(Header);
needed = (needed + 15) & ~15ULL; // 16-byte alignment needed = (needed + 15) & ~15ULL;
FreeNode* prev = &g_head; int idx = bucket_index(needed);
FreeNode* current = g_head.next;
while (current != nullptr) { if (idx >= 0) {
if (current->size >= needed) { // Small allocation — use segregated bucket (O(1))
uint64_t blockSize = current->size; if (g_buckets[idx] == nullptr && !refill_bucket(idx))
return nullptr;
// Unlink FreeNode* node = g_buckets[idx];
prev->next = current->next; g_buckets[idx] = node->next;
// Split if worthwhile Header* header = (Header*)node;
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->magic = HEADER_MAGIC;
header->size = size; header->size = size;
return (void*)((uint8_t*)header + sizeof(Header)); return (void*)((uint8_t*)header + sizeof(Header));
} }
prev = current; // Large allocation — search overflow list
current = current->next; void* block = take_from_overflow(needed);
if (block == nullptr) {
if (!grow(needed)) return nullptr;
block = take_from_overflow(needed);
if (block == nullptr) return nullptr;
} }
// No fit — grow and retry Header* header = (Header*)block;
if (!grow(needed)) header->magic = HEADER_MAGIC;
return nullptr; header->size = size;
return malloc(size); return (void*)((uint8_t*)header + sizeof(Header));
} }
inline void mfree(void* ptr) { inline void mfree(void* ptr) {
@@ -107,10 +191,25 @@ namespace heap_detail {
Header* header = get_header(ptr); 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); uint64_t blockSize = header->size + sizeof(Header);
blockSize = (blockSize + 15) & ~15ULL; 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) { inline void* realloc(void* ptr, uint64_t size) {
@@ -119,15 +218,22 @@ namespace heap_detail {
auto* header = heap_detail::get_header(ptr); auto* header = heap_detail::get_header(ptr);
uint64_t old = header->size; 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); void* newBlock = malloc(size);
if (newBlock == nullptr) return nullptr; if (newBlock == nullptr) return nullptr;
// Copy the smaller of old/new sizes
uint64_t copySize = (old < size) ? old : size; uint64_t copySize = (old < size) ? old : size;
uint8_t* dst = (uint8_t*)newBlock; memcpy(newBlock, ptr, copySize);
uint8_t* src = (uint8_t*)ptr;
for (uint64_t i = 0; i < copySize; i++)
dst[i] = src[i];
mfree(ptr); mfree(ptr);
return newBlock; return newBlock;
+41 -5
View File
@@ -39,22 +39,58 @@ namespace montauk {
inline void memcpy(void* dst, const void* src, uint64_t n) { inline void memcpy(void* dst, const void* src, uint64_t n) {
auto* d = (uint8_t*)dst; auto* d = (uint8_t*)dst;
auto* s = (const uint8_t*)src; 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) { inline void memmove(void* dst, const void* src, uint64_t n) {
auto* d = (uint8_t*)dst; auto* d = (uint8_t*)dst;
auto* s = (const uint8_t*)src; auto* s = (const uint8_t*)src;
if (d < s) { if (d < s || d >= s + n) {
for (uint64_t i = 0; i < n; i++) d[i] = s[i]; memcpy(dst, src, n);
} else { } 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) { inline void memset(void* dst, int val, uint64_t n) {
auto* d = (uint8_t*)dst; 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) { inline void strcpy(char* dst, const char* src) {
+183 -30
View File
@@ -66,27 +66,61 @@ int errno = 0;
void *memcpy(void *dest, const void *src, size_t n) { void *memcpy(void *dest, const void *src, size_t n) {
unsigned char *d = (unsigned char *)dest; unsigned char *d = (unsigned char *)dest;
const unsigned char *s = (const unsigned char *)src; const unsigned char *s = (const unsigned char *)src;
for (size_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 */
uint64_t *d8 = (uint64_t *)d;
const uint64_t *s8 = (const uint64_t *)s;
size_t words = n / 8;
for (size_t i = 0; i < words; i++) d8[i] = s8[i];
/* Remainder */
d = (unsigned char *)(d8 + words);
s = (const unsigned char *)(s8 + words);
for (size_t i = 0; i < (n & 7); i++) d[i] = s[i];
return dest; return dest;
} }
void *memset(void *s, int c, size_t n) { void *memset(void *s, int c, size_t n) {
unsigned char *p = (unsigned char *)s; unsigned char *p = (unsigned char *)s;
for (size_t i = 0; i < n; i++) unsigned char v = (unsigned char)c;
p[i] = (unsigned char)c;
/* Byte fill until 8-byte aligned */
while (n && ((uint64_t)p & 7)) { *p++ = v; n--; }
/* Bulk 8-byte fill */
uint64_t v8 = v;
v8 |= v8 << 8; v8 |= v8 << 16; v8 |= v8 << 32;
uint64_t *p8 = (uint64_t *)p;
size_t words = n / 8;
for (size_t i = 0; i < words; i++) p8[i] = v8;
/* Remainder */
p = (unsigned char *)(p8 + words);
for (size_t i = 0; i < (n & 7); i++) p[i] = v;
return s; return s;
} }
void *memmove(void *dest, const void *src, size_t n) { void *memmove(void *dest, const void *src, size_t n) {
unsigned char *d = (unsigned char *)dest; unsigned char *d = (unsigned char *)dest;
const unsigned char *s = (const unsigned char *)src; const unsigned char *s = (const unsigned char *)src;
if (s < d && d < s + n) { if (d < s || d >= s + n) {
for (size_t i = n; i > 0; i--) memcpy(dest, src, n);
d[i - 1] = s[i - 1];
} else { } else {
for (size_t i = 0; i < n; i++) /* Backward copy — bulk 8 bytes at a time from end */
d[i] = s[i]; d += n; s += n;
while (n && ((uint64_t)d & 7)) { *--d = *--s; n--; }
uint64_t *d8 = (uint64_t *)d;
const uint64_t *s8 = (const uint64_t *)s;
size_t words = n / 8;
for (size_t i = 1; i <= words; i++) d8[-(long)i] = s8[-(long)i];
d = (unsigned char *)(d8 - words);
s = (const unsigned char *)(s8 - words);
for (size_t i = 1; i <= (n & 7); i++) d[-(long)i] = s[-(long)i];
} }
return dest; return dest;
} }
@@ -229,6 +263,7 @@ int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }
======================================================================== */ ======================================================================== */
#define HEAP_MAGIC 0x5A484541ULL /* "ZHEA" */ #define HEAP_MAGIC 0x5A484541ULL /* "ZHEA" */
#define FREED_MAGIC 0xDEADFEEEULL
struct HeapHeader { struct HeapHeader {
uint64_t magic; uint64_t magic;
@@ -240,14 +275,78 @@ struct FreeNode {
struct FreeNode *next; struct FreeNode *next;
}; };
static struct FreeNode g_heapHead = { 0, NULL }; /* Segregated free lists: power-of-2 size classes for blocks <= 4096 bytes */
#define NUM_BUCKETS 8
static const uint64_t BUCKET_SIZES[NUM_BUCKETS] = {
32, 64, 128, 256, 512, 1024, 2048, 4096
};
static struct FreeNode *g_buckets[NUM_BUCKETS] = {};
static struct FreeNode g_overflow = { 0, NULL };
static int g_heapInit = 0; static int g_heapInit = 0;
static void heap_insert_free(void *ptr, uint64_t size) { static int heap_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 coalescing) */
static void heap_insert_overflow(void *ptr, uint64_t size) {
struct FreeNode *node = (struct FreeNode *)ptr; struct FreeNode *node = (struct FreeNode *)ptr;
node->size = size; node->size = size;
node->next = g_heapHead.next;
g_heapHead.next = node; struct FreeNode *prev = &g_overflow;
struct FreeNode *cur = g_overflow.next;
while (cur != NULL && cur < node) {
prev = cur;
cur = cur->next;
}
int merged_prev = 0;
if (prev != &g_overflow &&
(uint8_t *)prev + prev->size == (uint8_t *)node) {
prev->size += size;
node = prev;
merged_prev = 1;
}
if (cur != NULL &&
(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 >= needed from overflow. Splits remainder back. */
static void *heap_take_overflow(uint64_t needed) {
struct FreeNode *prev = &g_overflow;
struct FreeNode *cur = g_overflow.next;
while (cur != NULL) {
if (cur->size >= needed) {
uint64_t blockSize = cur->size;
prev->next = cur->next;
if (blockSize > needed + sizeof(struct FreeNode) + 16) {
heap_insert_overflow((uint8_t *)cur + needed, blockSize - needed);
}
return (void *)cur;
}
prev = cur;
cur = cur->next;
}
return NULL;
} }
static void heap_grow(uint64_t bytes) { static void heap_grow(uint64_t bytes) {
@@ -255,7 +354,29 @@ static void heap_grow(uint64_t bytes) {
if (pages < 4) pages = 4; if (pages < 4) pages = 4;
void *mem = (void *)_zos_syscall1(SYS_ALLOC, (long)(pages * 0x1000)); void *mem = (void *)_zos_syscall1(SYS_ALLOC, (long)(pages * 0x1000));
if (mem != NULL) if (mem != NULL)
heap_insert_free(mem, pages * 0x1000); heap_insert_overflow(mem, pages * 0x1000);
}
/* Refill a small-block bucket from overflow */
static int heap_refill_bucket(int idx) {
uint64_t bsize = BUCKET_SIZES[idx];
uint64_t chunk = (bsize < 4096) ? 4096 : bsize;
void *block = heap_take_overflow(chunk);
if (block == NULL) {
heap_grow(chunk);
block = heap_take_overflow(chunk);
if (block == NULL) return 0;
}
uint64_t count = chunk / bsize;
for (uint64_t i = 0; i < count; i++) {
struct FreeNode *node = (struct FreeNode *)((uint8_t *)block + i * bsize);
node->size = bsize;
node->next = g_buckets[idx];
g_buckets[idx] = node;
}
return 1;
} }
void *malloc(size_t size) { void *malloc(size_t size) {
@@ -267,39 +388,60 @@ void *malloc(size_t size) {
uint64_t needed = size + sizeof(struct HeapHeader); uint64_t needed = size + sizeof(struct HeapHeader);
needed = (needed + 15) & ~15ULL; needed = (needed + 15) & ~15ULL;
struct FreeNode *prev = &g_heapHead; int idx = heap_bucket_index(needed);
struct FreeNode *cur = g_heapHead.next;
while (cur != NULL) { if (idx >= 0) {
if (cur->size >= needed) { /* Small allocation — use segregated bucket (O(1)) */
uint64_t blockSize = cur->size; if (g_buckets[idx] == NULL && !heap_refill_bucket(idx))
prev->next = cur->next; return NULL;
if (blockSize > needed + sizeof(struct FreeNode) + 16) { struct FreeNode *node = g_buckets[idx];
void *rest = (void *)((uint8_t *)cur + needed); g_buckets[idx] = node->next;
heap_insert_free(rest, blockSize - needed);
}
struct HeapHeader *hdr = (struct HeapHeader *)cur; struct HeapHeader *hdr = (struct HeapHeader *)node;
hdr->magic = HEAP_MAGIC; hdr->magic = HEAP_MAGIC;
hdr->size = size; hdr->size = size;
return (void *)((uint8_t *)hdr + sizeof(struct HeapHeader)); return (void *)((uint8_t *)hdr + sizeof(struct HeapHeader));
} }
prev = cur;
cur = cur->next; /* Large allocation — search overflow list */
void *block = heap_take_overflow(needed);
if (block == NULL) {
heap_grow(needed);
block = heap_take_overflow(needed);
if (block == NULL) return NULL;
} }
heap_grow(needed); struct HeapHeader *hdr = (struct HeapHeader *)block;
return malloc(size); hdr->magic = HEAP_MAGIC;
hdr->size = size;
return (void *)((uint8_t *)hdr + sizeof(struct HeapHeader));
} }
void free(void *ptr) { void free(void *ptr) {
if (ptr == NULL) return; if (ptr == NULL) return;
struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader)); struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader));
if (hdr->magic == FREED_MAGIC) return; /* double-free */
if (hdr->magic != HEAP_MAGIC) return; /* corrupt */
hdr->magic = FREED_MAGIC;
uint64_t blockSize = hdr->size + sizeof(struct HeapHeader); uint64_t blockSize = hdr->size + sizeof(struct HeapHeader);
blockSize = (blockSize + 15) & ~15ULL; blockSize = (blockSize + 15) & ~15ULL;
heap_insert_free((void *)hdr, blockSize);
int idx = heap_bucket_index(blockSize);
if (idx >= 0) {
/* Small block — push onto bucket (O(1)) */
struct FreeNode *node = (struct FreeNode *)hdr;
node->size = BUCKET_SIZES[idx];
node->next = g_buckets[idx];
g_buckets[idx] = node;
} else {
/* Large block — sorted insert with coalescing */
heap_insert_overflow((void *)hdr, blockSize);
}
} }
void *calloc(size_t nmemb, size_t size) { void *calloc(size_t nmemb, size_t size) {
@@ -316,6 +458,17 @@ void *realloc(void *ptr, size_t size) {
struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader)); struct HeapHeader *hdr = (struct HeapHeader *)((uint8_t *)ptr - sizeof(struct HeapHeader));
uint64_t old = hdr->size; uint64_t old = hdr->size;
/* Compute actual block size (accounting for bucket rounding) */
uint64_t oldBlock = (old + sizeof(struct HeapHeader) + 15) & ~15ULL;
int idx = heap_bucket_index(oldBlock);
if (idx >= 0) oldBlock = BUCKET_SIZES[idx];
uint64_t newNeed = (size + sizeof(struct HeapHeader) + 15) & ~15ULL;
if (newNeed <= oldBlock) {
hdr->size = size;
return ptr;
}
void *newp = malloc(size); void *newp = malloc(size);
if (newp == NULL) return NULL; if (newp == NULL) return NULL;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -82,7 +82,7 @@ void gui::desktop_init(DesktopState* ds) {
ds->settings.ui_scale = 1; ds->settings.ui_scale = 1;
// Try to load default wallpaper // Try to load default wallpaper
wallpaper_load(&ds->settings, "0:/home/stephen-walker-DaC8D3USffk-unsplash.jpg", wallpaper_load(&ds->settings, "0:/home/kristaps-ungurs-llezNN2OGEY-unsplash.jpg",
ds->screen_w, ds->screen_h); ds->screen_w, ds->screen_h);
montauk::win_setscale(1); montauk::win_setscale(1);
+7 -2
View File
@@ -15,6 +15,7 @@ endif
# ---- Paths ---- # ---- Paths ----
LIBC_LIB := ../../lib/libc
PROG_INC := ../../include PROG_INC := ../../include
LINK_LD := ../../link.ld LINK_LD := ../../link.ld
BINDIR := ../../bin BINDIR := ../../bin
@@ -59,6 +60,10 @@ LDFLAGS := \
-z max-page-size=0x1000 \ -z max-page-size=0x1000 \
-T $(LINK_LD) -T $(LINK_LD)
# ---- Libraries ----
LIBS := $(LIBC_LIB)/liblibc.a
# ---- Source files ---- # ---- Source files ----
SRCS := main.cpp stb_truetype_impl.cpp SRCS := main.cpp stb_truetype_impl.cpp
@@ -72,9 +77,9 @@ TARGET := $(BINDIR)/os/fontpreview.elf
all: $(TARGET) all: $(TARGET)
$(TARGET): $(OBJS) $(LINK_LD) Makefile $(TARGET): $(OBJS) $(LIBS) $(LINK_LD) Makefile
mkdir -p $(BINDIR)/os mkdir -p $(BINDIR)/os
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
$(OBJDIR)/%.o: %.cpp Makefile $(OBJDIR)/%.o: %.cpp Makefile
mkdir -p $(OBJDIR) mkdir -p $(OBJDIR)
+1 -2
View File
@@ -1,7 +1,6 @@
/* /*
* main.cpp * main.cpp
* MontaukOS Image Viewer - standalone Window Server process * MontaukOS Image Viewer
* Displays images with zoom, pan, fit-to-window, and 1:1 modes
* Copyright (c) 2026 Daniel Hammer * Copyright (c) 2026 Daniel Hammer
*/ */