Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 96 additions & 59 deletions include/slotsboxmalloc/slotsboxobj.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,24 @@ size_t sbo_meta_size(size_t data_size, size_t per_slot_pool);
#include <blockmalloc/blockmalloc.h>

/* ================================================================
* free_bitmap helpers (byte-oriented)
* free_bitmap helpers (64-bit word + byte-oriented)
* ================================================================ */

static inline uint64_t sbo_bm_load(const uint8_t *bm) {
uint64_t v = 0;
for (int i = 0; i < SBO_BITMAP_B; i++)
v |= (uint64_t)bm[i] << (8 * i);
return v;
}
static inline void sbo_bm_store(uint8_t *bm, uint64_t v) {
for (int i = 0; i < SBO_BITMAP_B; i++)
bm[i] = (uint8_t)(v >> (8 * i));
}
static inline uint64_t sbo_bm_range_mask(int start, int count) {
if (count <= 0 || start < 0 || start >= SBO_N) return 0;
if (count >= SBO_N - start) return UINT64_MAX << start;
return ((UINT64_C(1) << count) - 1) << start;
}
static inline bool sbo_bm_get(uint8_t *bm, int i) {
return (bm[i >> 3] >> (i & 7)) & 1;
}
Expand All @@ -176,24 +191,17 @@ static inline void sbo_bm_set(uint8_t *bm, int i) {
static inline void sbo_bm_clear(uint8_t *bm, int i) {
bm[i >> 3] &= (uint8_t)~(1 << (i & 7));
}
static inline bool sbo_bm_test_range(uint8_t *bm, int start, int count) {
for (int i = start; i < start + count; i++)
if (!sbo_bm_get(bm, i)) return false;
return true;
}
static inline void sbo_bm_fill(uint8_t *bm) {
memset(bm, 0xFF, SBO_BITMAP_B);
}
static inline void sbo_bm_clear_range(uint8_t *bm, int start, int count) {
for (int i = start; i < start + count; i++) sbo_bm_clear(bm, i);
sbo_bm_store(bm, sbo_bm_load(bm) & ~sbo_bm_range_mask(start, count));
}
static inline void sbo_bm_set_range(uint8_t *bm, int start, int count) {
for (int i = start; i < start + count; i++) sbo_bm_set(bm, i);
sbo_bm_store(bm, sbo_bm_load(bm) | sbo_bm_range_mask(start, count));
}
static inline int sbo_bm_popcount(uint8_t *bm) {
int n = 0;
for (int i = 0; i < SBO_N; i++) if (sbo_bm_get(bm, i)) n++;
return n;
return __builtin_popcountll(sbo_bm_load(bm));
}

/* ================================================================
Expand Down Expand Up @@ -226,13 +234,29 @@ static inline int32_t sbo_self_id(sbo_meta_t *meta, sbo_box_t *box) {
* 容量查询
* ================================================================ */

static inline uint8_t sbo_bm_continuous_max(uint64_t m) {
if (!m) return 0;
uint64_t u = m + (m & (~m + 1ULL));
if (__builtin_expect((u & (u - 1ULL)) == 0, 1))
return (uint8_t)(SBO_N - __builtin_clzll(m) - __builtin_ctzll(m));
uint64_t m2 = m & (m >> 1);
uint64_t m4 = m2 & (m2 >> 2);
uint64_t m8 = m4 & (m4 >> 4);
uint64_t m16 = m8 & (m8 >> 8);
uint64_t m32 = m16 & (m16 >> 16);
uint8_t best = 1;
uint64_t x = m, y;
y = (x >> 32) & m32; if (y) { x = y; best += 32; }
y = (x >> 16) & m16; if (y) { x = y; best += 16; }
y = (x >> 8) & m8; if (y) { x = y; best += 8; }
y = (x >> 4) & m4; if (y) { x = y; best += 4; }
y = (x >> 2) & m2; if (y) { x = y; best += 2; }
y = (x >> 1) & m; if (y) { best += 1; }
return best;
}

static inline uint8_t sbo_continuous_max(sbo_box_t *box) {
uint8_t cur = 0, best = 0;
for (int i = 0; i < SBO_N; i++) {
if (sbo_bm_get(box->free_bitmap, i)) cur++;
else { if (cur > best) best = cur; cur = 0; }
}
return cur > best ? cur : best;
return sbo_bm_continuous_max(sbo_bm_load(box->free_bitmap));
}

static inline sbo_usage_t sbo_box_max_cap(sbo_box_t *box) {
Expand All @@ -258,7 +282,8 @@ static inline void sbo_update_parent(sbo_meta_t *meta, sbo_box_t *box,
}
if (child_c) {
sbo_usage_t nm = {0, 0};
for (int i = 0; i < SBO_N; i++) {
int lim = box->box_boundary < SBO_N ? box->box_boundary : SBO_N;
for (int i = 0; i < lim; i++) {
if (box->slots[i].state != SBO_BOX) continue;
sbo_box_t *ch = sbo_child(meta, box, box->children[i]);
if (!ch) continue;
Expand Down Expand Up @@ -300,45 +325,53 @@ static inline void sbo_box_format(sbo_meta_t *meta, sbo_box_t *box,
* ================================================================ */

static inline int8_t sbo_alloc_box_slot(sbo_meta_t *meta, sbo_box_t *box) {
for (int p = box->box_boundary; p <= box->obj_boundary; p++) {
if (sbo_bm_get(box->free_bitmap, p)) {
sbo_bm_clear(box->free_bitmap, p);
box->slots[p].state = SBO_BOX;
box->box_boundary = (int16_t)(p + 1);
return (int8_t)p;
}
}
return -1;
(void)meta;
int lo = box->box_boundary > 0 ? box->box_boundary : 0;
int hi = box->obj_boundary < SBO_N - 1 ? box->obj_boundary : SBO_N - 1;
if (lo > hi || lo >= SBO_N || hi < 0) return -1;
uint64_t mask = (UINT64_MAX << lo) & (UINT64_MAX >> (SBO_N - 1 - hi));
uint64_t runs = sbo_bm_load(box->free_bitmap) & mask;
if (!runs) return -1;
int p = __builtin_ctzll(runs);
sbo_bm_clear(box->free_bitmap, p);
box->slots[p].state = SBO_BOX;
box->box_boundary = (int16_t)(p + 1);
return (int8_t)p;
}

/* ================================================================
* alloc_obj_slots — 从右侧分配 m 个连续对象 slot
* ================================================================ */

static inline int8_t sbo_alloc_obj_slots(sbo_meta_t *meta, sbo_box_t *box, uint8_t m) {
if (m == 0 || m > SBO_MULT_MAX) return -1;
// 从 N-1 向左扫描全范围: 覆盖 free 产生的右侧空洞
for (int p = SBO_N - 1; p >= box->box_boundary; p--) {
int start = p - m + 1;
if (start < box->box_boundary) break;
if (sbo_bm_test_range(box->free_bitmap, start, m)) {
sbo_bm_clear_range(box->free_bitmap, start, m);
box->slots[start].state = SBO_OBJ_START;
for (int j = start + 1; j < start + m; j++)
box->slots[j].state = SBO_OBJ_CONT;
box->obj_boundary = (int16_t)(start - 1);
uint8_t nc = sbo_continuous_max(box);
if (box->max_obj_cap != nc) {
box->max_obj_cap = nc;
if (sbo_self_id(meta, box) != 0) {
sbo_box_t *pp = sbo_child(meta, box, box->parent);
if (pp) sbo_update_parent(meta, pp, false, true);
}
}
return (int8_t)start;
if (m == 0 || m > SBO_MULT_MAX || m > box->max_obj_cap || box->box_boundary >= SBO_N)
return -1;
int lo = box->box_boundary > 0 ? box->box_boundary : 0;
uint64_t bm = sbo_bm_load(box->free_bitmap);
uint64_t runs = bm & (UINT64_MAX << lo);
/* 倍增移位与运算定位长度为 m 的连续空闲段起点,保留最右适配策略 */
for (int width = 1; width < m;) {
int step = width < m - width ? width : m - width;
runs &= runs >> step;
width += step;
}
if (!runs) return -1;
int start = 63 - __builtin_clzll(runs);
bm &= ~sbo_bm_range_mask(start, m);
sbo_bm_store(box->free_bitmap, bm);
box->slots[start].state = SBO_OBJ_START;
for (int j = start + 1; j < start + m; j++)
box->slots[j].state = SBO_OBJ_CONT;
box->obj_boundary = (int16_t)(start - 1);
uint8_t nc = sbo_bm_continuous_max(bm);
if (box->max_obj_cap != nc) {
box->max_obj_cap = nc;
if (sbo_self_id(meta, box) != 0) {
sbo_box_t *pp = sbo_child(meta, box, box->parent);
if (pp) sbo_update_parent(meta, pp, false, true);
}
}
return -1;
return (int8_t)start;
}

/* ================================================================
Expand All @@ -348,14 +381,14 @@ static inline int8_t sbo_alloc_obj_slots(sbo_meta_t *meta, sbo_box_t *box, uint8
static inline void sbo_free_obj_slots(sbo_meta_t *meta, sbo_box_t *box, uint8_t start) {
int p = start;
box->slots[p].state = SBO_FREE;
sbo_bm_set(box->free_bitmap, p);
p++;
while (p < SBO_N && box->slots[p].state == SBO_OBJ_CONT) {
box->slots[p].state = SBO_FREE;
sbo_bm_set(box->free_bitmap, p);
p++;
}
uint8_t nc = sbo_continuous_max(box);
uint64_t bm = sbo_bm_load(box->free_bitmap) | sbo_bm_range_mask(start, p - start);
sbo_bm_store(box->free_bitmap, bm);
uint8_t nc = sbo_bm_continuous_max(bm);
if (box->max_obj_cap != nc) {
box->max_obj_cap = nc;
if (sbo_self_id(meta, box) != 0) {
Expand All @@ -376,21 +409,25 @@ static inline uint64_t sbo_find_alloc(sbo_meta_t *meta, sbo_box_t *box,
if (!box || box->state != SBO_BOX) return SBO_ALLOC_FAIL;

if (usage.level == box->objlevel) {
if (usage.multiple > box->max_obj_cap) return SBO_ALLOC_FAIL;
int8_t slot = sbo_alloc_obj_slots(meta, box, usage.multiple);
if (slot < 0) return SBO_ALLOC_FAIL;
return sbo_offset_raw(box->objlevel, (uint8_t)slot);
}

if (usage.level < box->objlevel) {
// 先遍历已有子 box
for (int i = 0; i < SBO_N; i++) {
if (box->children[i] < 0) continue;
sbo_box_t *ch = sbo_child(meta, box, box->children[i]);
if (!ch) continue;
if (sbo_usage_cmp(sbo_box_and_child_max_cap(ch), usage) < 0) continue;
uint64_t sub = sbo_find_alloc(meta, ch, usage);
if (sub != SBO_ALLOC_FAIL)
return sbo_offset_raw(box->objlevel, (uint8_t)i) + sub;
if (sbo_usage_cmp(box->child_max_cap, usage) >= 0) {
int lim = box->box_boundary < SBO_N ? box->box_boundary : SBO_N;
for (int i = 0; i < lim; i++) {
if (box->children[i] < 0) continue;
sbo_box_t *ch = sbo_child(meta, box, box->children[i]);
if (!ch) continue;
if (sbo_usage_cmp(sbo_box_and_child_max_cap(ch), usage) < 0) continue;
uint64_t sub = sbo_find_alloc(meta, ch, usage);
if (sub != SBO_ALLOC_FAIL)
return sbo_offset_raw(box->objlevel, (uint8_t)i) + sub;
}
}
// 无已有子 box 满足 → 从左侧分配新子 box slot
int8_t slot = sbo_alloc_box_slot(meta, box);
Expand Down
154 changes: 154 additions & 0 deletions tutorial/19_bitmap_oracle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <blockmalloc/blockmalloc.h>
#include <slotsboxmalloc/slotsboxobj.h>

static void *meta;
static sbo_box_t *root;
static uint64_t cases;

static uint8_t longest(uint64_t bits) {
uint8_t best = 0, run = 0;
for (int i = 0; i < 64; i++) {
run = bits & (UINT64_C(1) << i) ? (uint8_t)(run + 1) : 0;
if (run > best) best = run;
}
return best;
}

static int rightmost(uint64_t bits, int boundary, int count) {
for (int end = 63; end >= boundary; end--) {
int start = end - count + 1;
if (start < boundary) break;
int free_run = 1;
for (int i = start; i <= end; i++) {
if (!(bits & (UINT64_C(1) << i))) { free_run = 0; break; }
}
if (free_run) return start;
}
return -1;
}

static void fail(uint64_t bits, int boundary, int count, const char *what) {
fprintf(stderr, "%s: bits=%016" PRIx64 " boundary=%d count=%d\n",
what, bits, boundary, count);
exit(1);
}

static void check_with_hint(uint64_t bits, int boundary, int count, sbo_usage_t hint) {
root->box_boundary = (int16_t)boundary;
root->obj_boundary = 63;
root->max_obj_cap = longest(bits);
root->child_max_cap = hint;
for (int i = 0; i < 8; i++) root->free_bitmap[i] = (uint8_t)(bits >> (8 * i));
for (int i = 0; i < 64; i++)
root->slots[i].state = bits & (UINT64_C(1) << i) ? SBO_FREE : SBO_OBJ_START;

sbo_box_t expected = *root;
int start = rightmost(bits, boundary, count);
uint64_t offset = sbo_alloc(meta, (size_t)count * 8);
uint64_t want = start < 0 ? UINT64_MAX : (uint64_t)start * 8;
if (offset != want) fail(bits, boundary, count, "rightmost placement");
if (start >= 0) {
uint64_t mask = (UINT64_MAX >> (64 - count)) << start;
for (int i = start; i < start + count; i++) {
expected.free_bitmap[i >> 3] &= (uint8_t)~(1U << (i & 7));
expected.slots[i].state = i == start ? SBO_OBJ_START : SBO_OBJ_CONT;
}
expected.obj_boundary = (int16_t)(start - 1);
expected.max_obj_cap = longest(bits & ~mask);
if (sbo_allocated_size(meta, offset) != (uint64_t)count * 8)
fail(bits, boundary, count, "allocated size");
}
if (memcmp(root, &expected, sizeof expected)) fail(bits, boundary, count, "allocation state");
if (start >= 0) {
sbo_free(meta, offset);
for (int i = start; i < start + count; i++) {
expected.free_bitmap[i >> 3] |= (uint8_t)(1U << (i & 7));
expected.slots[i].state = SBO_FREE;
}
expected.max_obj_cap = longest(bits);
if (memcmp(root, &expected, sizeof expected)) fail(bits, boundary, count, "free state");
}
cases++;
}

static void all_counts(uint64_t bits) {
sbo_usage_t zero_hint = {0, 0};
for (int boundary = 0; boundary <= 64; boundary++)
for (int count = 1; count <= 63; count++)
check_with_hint(bits, boundary, count, zero_hint);
}

static void check_multilevel_propagation(void) {
/* 2-level pool (32KB = 8 * 64^2): verify depth-1 child boxes (parent == 0)
* propagate capacity changes to root and seamlessly spill to sibling boxes. */
size_t data_size = 8ULL * 64 * 64;
size_t head_size = sbo_meta_size(data_size, 64 * 1024);
void *m2 = calloc(1, head_size);
if (!m2 || sbo_init(m2, head_size, data_size) != 0) {
fprintf(stderr, "multilevel init failed\n");
exit(1);
}
uint64_t offs[128];
for (int i = 0; i < 128; i++) {
offs[i] = sbo_alloc(m2, 8);
if (offs[i] == UINT64_MAX || sbo_allocated_size(m2, offs[i]) != 8) {
fprintf(stderr, "multilevel alloc failed at %d\n", i);
exit(1);
}
}
for (int i = 0; i < 128; i += 2) sbo_free(m2, offs[i]);
for (int i = 0; i < 128; i += 2) {
uint64_t re = sbo_alloc(m2, 8);
if (re == UINT64_MAX) {
fprintf(stderr, "multilevel realloc failed at %d\n", i);
exit(1);
}
offs[i] = re;
}
for (int i = 0; i < 128; i++) sbo_free(m2, offs[i]);
free(m2);
}

int main(void) {
meta = calloc(1, 4096);
if (!meta || sbo_init(meta, 4096, 512) != 0) return 1;
sbo_meta_t *m = meta;
root = (sbo_box_t *)((uint8_t *)(m + 1)
+ m->root_slots * (sizeof(blocks_meta_t) + sizeof(sbo_lock_t))
+ m->sizeof_block_head);

/* Exercise leaf capacity guard even when subtree hint is optimistic. */
sbo_usage_t opt_hint = {2, 1};
for (uint64_t bits = 0; bits < 256; bits++) {
for (int shift = 0; shift <= 56; shift += 28)
for (int boundary = 0; boundary <= 64; boundary++)
for (int count = 1; count <= 9; count++)
check_with_hint(bits << shift, boundary, count, opt_hint);
}
all_counts(0);
all_counts(UINT64_MAX);
all_counts(UINT64_C(0xAAAAAAAAAAAAAAAA));
all_counts(UINT64_C(0x5555555555555555));
for (int bit = 0; bit < 64; bit++) {
all_counts(UINT64_C(1) << bit);
all_counts(~(UINT64_C(1) << bit));
}
uint64_t seed = UINT64_C(0x937bf272d9ea1563);
for (int i = 0; i < 64; i++) {
seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17;
uint64_t bits = seed;
if (i % 3 == 0) bits &= UINT64_C(0x5555555555555555);
else if (i % 3 == 1) bits |= UINT64_C(0xAAAAAAAAAAAAAAAA);
all_counts(bits);
}
free(meta);

check_multilevel_propagation();

printf("PASS bitmap regression: %" PRIu64 " cases\n", cases);
return 0;
}
5 changes: 5 additions & 0 deletions tutorial/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,8 @@ add_test(NAME 17_data_integrity COMMAND 17_data_integrity)
add_executable(18_capacity 18_capacity.c)
target_link_libraries(18_capacity slotsboxmalloc)
add_test(NAME 18_capacity COMMAND 18_capacity)

add_executable(19_bitmap_oracle 19_bitmap_oracle.c)
target_link_libraries(19_bitmap_oracle slotsboxmalloc)
add_test(NAME 19_bitmap_oracle COMMAND 19_bitmap_oracle)

Loading