1313namespace memory {
1414
1515// chunk and block are synonymous
16- typedef struct chunk {
17- void * page; // page we belong w/i
18- bool in_use;
19- void * data; // idk what datatype to make this but this is the body of the chunk
20- // we don't need to store what segment it belongs to b/c
21- // that can be easily deduced w/ `segment_base = ptr & ~(SEGMENT_SIZE - 1)`
22- } chunk_t ;
23-
24- typedef struct guard_chunk {
25- int64_t guard; // software tagging mechanism
26- // turn this into a class if necessary, and give it an 'integrity_check()' function.
16+ class Chunk {
17+ private:
18+ Page* page; // page we belong w/i
19+ uint32_t data_size : 31 ; // usable size of data region (max ~2MiB)
20+ uint32_t in_use : 1 ; // packed into the same 8 bytes as data_size
21+ void * data; // body of the chunk (user memory)
22+ // we don't need to store what segment it belongs to b/c
23+ // that can be easily deduced w/ `segment_base = ptr & ~(SEGMENT_SIZE - 1)`
24+
25+ public:
26+ Chunk () : page(nullptr ), in_use(false ), data(nullptr ), data_size(0 ) {}
27+ Chunk (Page* owning_page, void * data_ptr, size_t size)
28+ : page(owning_page), in_use(false ), data(data_ptr), data_size(size) {}
29+
30+ void * get_page () const { return page; }
31+ bool is_in_use () const { return in_use; }
32+ void * get_data () const { return data; } // this is what should be returned when "malloc" is called.
33+ // this way they aren't overwriting the metadata, only the data
34+ // but we also have to make sure input is < data_size.
35+ size_t get_data_size () const { return data_size; }
36+ bool set_data () {
37+ bool is_changed{false };
38+
39+ }
40+
41+ void mark_used () { in_use = true ; }
42+ void mark_free () { in_use = false ; }
43+ };
44+
45+ // Placed at page boundaries: one before the first chunk, one after the last.
46+ // Page layout: |GuardChunk|chunk|chunk|...|chunk|GuardChunk|
47+ class GuardChunk {
48+ private:
49+ int64_t pattern; // known canary value, made at page init
50+
51+ public:
52+ GuardChunk () : pattern(0 ) {}
53+ GuardChunk (int64_t val) : pattern(val) {}
54+
55+ void set (int64_t val) { pattern = val; }
56+
57+ // Check if the guard is still intact. Call on free() or during
58+ // validation to detect buffer overflows from neighboring chunks.
59+ bool integrity_check (int64_t expected) const {
60+ // If false, an overflow from an adjacent chunk corrupted the boundary.
61+ INTEGRITY_CHECK (pattern == expected, " *** canary smashing detected *** " );
62+ return true ;
63+ }
2764};
2865
2966typedef struct tc_page_s {
@@ -50,7 +87,7 @@ typedef struct thread_cache {
508710 2 uint16_t capacity
518812 2 uint16_t reserved
528914 2 padding (align next pointer to 8)
53- 16 8 chunk_t * free
90+ 16 8 Chunk * free
549124 2 uint16_t used
559226 6 padding (align size_t to 8)
569332 8 size_t chunk_sz
@@ -65,11 +102,11 @@ class Page {
65102 // if owned by a different thread, you shouldn't
66103 // be able to alloc memory from that page.
67104 uint32_t slot_count; // slots in this page
68- uint32_t slot_off; // dist from start of containg page.
69105 uint8_t is_committed:1 ; // 'true' if the page vmem is commited
70106
71- uint16_t capacity; // num chunks committed to phys
107+ uint16_t num_committed; // num chunks committed to phys
72108 uint16_t reserved; // num chunks reserved in vmem
109+ uint16_t capacity; // maximum # of chunks in this page. floor(page_size/chunk_size)
73110 // page_flags?
74111
75112 std::atomic<bool > free_bm; // bitmap of available free blocks for regular allocation
@@ -78,38 +115,78 @@ class Page {
78115 // and then randomly pick one of those indices. This would likely
79116 // be easier to implement.
80117
81- std::vector<chunk *> freelist; // freelist for tcache to improve locality. used in free's fast path
118+ std::vector<Chunk *> freelist; // freelist for tcache to improve locality. used in free's fast path
82119 // if used by slow path, choose random chunk using free_bm
83120 uint16_t used; // num chunks being used
84121 size_t chunk_sz; // size of the chunks in this slot.
85- chunk_t * page_start; // pointer to end of guard page in front chunks
122+ Chunk * page_start; // pointer to first chunk (after front guard)
86123 // uintptr_t keys[2]; // two random keys to encode the free lists
87124
125+ // ── Page boundary guards ──
126+ // Layout: |front_guard|chunk|chunk|...|chunk|back_guard|
127+ GuardChunk front_guard; // sits before first chunk
128+ GuardChunk back_guard; // sits after last chunk
129+
88130 page_status_t status; // FULL, ACTIVE, or EMPTY
89131 uint64_t prng_state; // PRNG state for random free-slot selection (non-threaded pages)
90132
133+ public:
91134 Page () = default ;
92135 ~Page () = default ;
93- public:
94- static Page& instance () {
95- static Page page;
96- return page;
97- }
136+
137+ // Construct a page with its core layout parameters.
138+ // guard_canary: value to stamp into front/back guards at init.
139+ Page (size_t chunk_size, uint16_t max_capacity, int64_t guard_canary)
140+ : slot_count(0 ), is_committed(0 ),
141+ num_committed (0 ), reserved(0 ), capacity(max_capacity),
142+ free_bm(false ), used(0 ), chunk_sz(chunk_size),
143+ page_start(nullptr ),
144+ front_guard(guard_canary), back_guard(guard_canary),
145+ status(EMPTY), prng_state(0 ) {}
146+
98147 void * find_space (); // this will likely be moved but my idea is that if we need to use realloc then we'd have to go back to the segment level and see if they have what we need. Could also be used for free() since it should be the segment that controls which pages get freed, not the page itself.
99148 void tc_free_push (); // push chunk onto thread's free list,
100149
101150 // ── Free-path accessors (used by free.cpp) ──
102151
103152 // Get the base address of this page's chunk data region
104- void * get_page_start () const {
105- // TODO: return page_start
106- return nullptr ;
153+ Chunk* get_page_start () const {
154+ // as a security measure we should assert that this pointer is w/i the page
155+ return page_start;
156+ }
157+
158+ // Set page_start to the address of the first instantiated Chunk.
159+ // Called once during page initialization after chunks are laid out.
160+ void set_page_start (Chunk* first_chunk) {
161+ // TODO: optionally validate that first_chunk is within this page's
162+ // committed region before accepting it
163+ page_start = first_chunk;
107164 }
108165
109- // Get the chunk size for this page
166+ // ── Guard accessors ──
167+
168+ // Initialize both boundary guards with a canary value (call at page init).
169+ void init_guards (int64_t canary_val) {
170+ front_guard.set (canary_val);
171+ back_guard.set (canary_val);
172+ }
173+
174+ // Verify both guards are intact. Returns false if either is corrupted.
175+ bool check_guards (int64_t expected) const {
176+ // TODO: return front_guard.integrity_check(expected)
177+ // && back_guard.integrity_check(expected)
178+ (void )expected;
179+ return false ;
180+ }
181+
182+ GuardChunk& get_front_guard () { return front_guard; }
183+ GuardChunk& get_back_guard () { return back_guard; }
184+ const GuardChunk& get_front_guard () const { return front_guard; }
185+ const GuardChunk& get_back_guard () const { return back_guard; }
186+
187+ // Get the chunk size used in this page
110188 size_t get_chunk_size () const {
111- // TODO: return chunk_sz
112- return 0 ;
189+ return chunk_sz;
113190 }
114191
115192 // Get how many chunks are currently in use
@@ -309,7 +386,7 @@ class Heap {
309386 size of obj they're encapsulating.
310387 ^ back to the free pages, scrap what i just said. I think we should also track the size w/i the metadata(for the page)
311388 and this will remain regardless of free'd status. This should be track to quickly re-allocate pages. <- update: we already
312- do this inside of chunk_t , that is mainly for the "freed" chunks
389+ do this inside of Chunk , that is mainly for the "freed" chunks
313390
314391 when it comes to accessing this page/slot metadata from outside of the corresponding chunk,
315392 instead of doing next and prev ptrs for the other slots, lets just make a vector corresponding to the segment.
@@ -349,4 +426,8 @@ class Heap {
349426 trivial getters, then do slot_index_of and find_page_for since
350427 those are critical for the free dispatch in free.cpp.
351428 ═══════════════════════════════════════════════════════════════
352- */
429+ */
430+
431+
432+ // side note: I heard vector<bool> is not real and actually just uses bytes behind the scene
433+ // so consider switching this to a uint# if size is known, otherwise continue w/ bools.
0 commit comments