diff --git a/include/slotsboxmalloc/slotsboxobj.h b/include/slotsboxmalloc/slotsboxobj.h index 15e0467..ce2b787 100644 --- a/include/slotsboxmalloc/slotsboxobj.h +++ b/include/slotsboxmalloc/slotsboxobj.h @@ -164,9 +164,24 @@ size_t sbo_meta_size(size_t data_size, size_t per_slot_pool); #include /* ================================================================ - * 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; } @@ -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)); } /* ================================================================ @@ -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) { @@ -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; @@ -300,15 +325,18 @@ 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; } /* ================================================================ @@ -316,29 +344,34 @@ static inline int8_t sbo_alloc_box_slot(sbo_meta_t *meta, sbo_box_t *box) { * ================================================================ */ 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; } /* ================================================================ @@ -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) { @@ -376,6 +409,7 @@ 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); @@ -383,14 +417,17 @@ static inline uint64_t sbo_find_alloc(sbo_meta_t *meta, sbo_box_t *box, 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); diff --git a/tutorial/19_bitmap_oracle.c b/tutorial/19_bitmap_oracle.c new file mode 100644 index 0000000..fadcbb8 --- /dev/null +++ b/tutorial/19_bitmap_oracle.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/tutorial/CMakeLists.txt b/tutorial/CMakeLists.txt index 52ff288..6fbd166 100644 --- a/tutorial/CMakeLists.txt +++ b/tutorial/CMakeLists.txt @@ -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) +