// // Created by 马也驰 on 2024/12/9. // #ifndef LEVELDB_SLOTPAGE_H #define LEVELDB_SLOTPAGE_H #include #include #include #include #include #include #include #define BITMAP_SIZE 8192 #define BITS_PER_BYTE 8 #define POSMASK(off) (0x01 << (BITS_PER_BYTE-(off)-1)) #define SETBIT(byte, off) (*(byte) |= POSMASK(off)) #define RESETBIT(byte, off) (*(byte) &= (0xff ^ POSMASK(off))) #define HASFREESLOT(byte) ((byte) != ~0x0) struct slot_content { uint32_t vlog_num; uint32_t value_offset; slot_content(uint32_t vn, uint32_t vo) { vlog_num = vn; value_offset = vo; } }; class SlotCache { // slot number -> slot content #define BLOCK_NUM 16 #define BLOCK_SIZE 4096 #define SLOT_PER_BLOCK (BLOCK_SIZE/sizeof(slot_content)) #define SLOT_OFFSET_IN_BLOCK(slot_num) ((slot_num)%SLOT_PER_BLOCK) public: SlotCache() { for (auto i = 0; i < BLOCK_NUM; i++) { block_cache[i] = static_cast(malloc(BLOCK_SIZE)); access_time[i] = 0; info[i] = block_info(); } } void get_slot(std::string &slotpage_fname, size_t slot_num, struct slot_content *sc) { auto block_num = slotnum_hash2_blocknum(slot_num); auto blockcache_num = block_num % BLOCK_NUM; mtx.lock(); if (info[blockcache_num].slotpage_fname != slotpage_fname) { // cache miss write_back_block(blockcache_num); read_in_block(blockcache_num, slotpage_fname, block_num); } read_slot(sc, blockcache_num, SLOT_OFFSET_IN_BLOCK(slot_num)); access_time[blockcache_num]++; mtx.unlock(); } void set_slot(std::string &slotpage_fname, size_t slot_num, struct slot_content *sc) { auto block_num = slotnum_hash2_blocknum(slot_num); auto blockcache_num = block_num % BLOCK_NUM; mtx.lock(); if (info[blockcache_num].slotpage_fname != slotpage_fname) { write_back_block(blockcache_num); read_in_block(blockcache_num, slotpage_fname, block_num); } set_slot(sc, blockcache_num, SLOT_OFFSET_IN_BLOCK(slot_num)); access_time[blockcache_num]++; info[blockcache_num].is_dirty = true; mtx.unlock(); } private: struct block_info { std::string slotpage_fname; size_t block_num; bool is_dirty; block_info() {} block_info(std::string &slotpage_fname, size_t block_num, bool is_dirty=false) { this->slotpage_fname = slotpage_fname; this->block_num = block_num; this->is_dirty = is_dirty; } }; private: inline static size_t slotnum_hash2_blocknum(size_t slot_num) { return slot_num * sizeof(slot_content) / BLOCK_SIZE; } void write_back_block(size_t blockcache_num) { auto &fname = info[blockcache_num].slotpage_fname; auto write_pos = info[blockcache_num].block_num * BLOCK_SIZE; auto slotpage_handler = std::fstream(fname, std::ios::in | std::ios::out); slotpage_handler.seekp(write_pos); slotpage_handler.write( reinterpret_cast(block_cache[blockcache_num]), BLOCK_SIZE); } void read_in_block(size_t blockcache_num, std::string &slotpage_fname, size_t block_num) { auto slotpage_handler = std::fstream(slotpage_fname, std::ios::in | std::ios::out); slotpage_handler.seekp(block_num*BLOCK_SIZE); slotpage_handler.read(reinterpret_cast(block_cache[blockcache_num]), BLOCK_SIZE); access_time[blockcache_num] = 0; info[blockcache_num] = block_info(slotpage_fname, block_num); } inline void read_slot(struct slot_content *sc, size_t blockcache_num, size_t slot_num_offset) { auto src = &block_cache[blockcache_num][slot_num_offset]; memcpy(sc, src, sizeof(slot_content)); } inline void set_slot(struct slot_content *sc, size_t blockcache_num, size_t slot_num_offset) { auto src = &block_cache[blockcache_num][slot_num_offset]; memcpy(src, sc, sizeof(slot_content)); } private: std::mutex mtx; struct slot_content *block_cache[BLOCK_NUM]; size_t access_time[BLOCK_NUM]; struct block_info info[BLOCK_NUM]; }; class BitMap { // in memory bitmap public: BitMap() { char *bitmap = static_cast(malloc(BITMAP_SIZE*sizeof(char))); bitmaps_.push_back(bitmap); size = BITMAP_SIZE; first_empty_slot = 0; } void dealloc_slot(size_t slot_num) { mtx.lock(); const size_t byte = slot2byte(slot_num); const size_t off = slot2offset(slot_num); char *target_byte = get_bitmap_byte(byte); assert(*target_byte & POSMASK(off)); RESETBIT(target_byte, off); // set_bitmap_byte(byte, target_byte); first_empty_slot = first_empty_slot < slot_num ? first_empty_slot:slot_num; mtx.unlock(); } size_t alloc_slot() { mtx.lock(); size_t target_slot = first_empty_slot; char *start_byte = get_bitmap_byte(slot2byte(first_empty_slot)); const size_t off = slot2offset(first_empty_slot); SETBIT(start_byte, off); if (HASFREESLOT(*start_byte)) { auto bit_off = find_first_free_slot_inbyte(*start_byte); first_empty_slot += bit_off - off; } else { size_t i; for (i = slot2byte(first_empty_slot)+1; i < size; i++) { char *byte = get_bitmap_byte(i); if (HASFREESLOT(*byte)) { auto bit_off = find_first_free_slot_inbyte(*byte); first_empty_slot = byte2slot(i) + bit_off; } } // scale the bitmap if (i >= size) { alloc_new_bitmap(); char *byte = get_bitmap_byte(i); SETBIT(byte, 0); first_empty_slot = byte2slot(i) + 1; } } mtx.unlock(); return target_slot; } private: static inline size_t slot2byte(size_t slot_num) { return slot_num / BITS_PER_BYTE; } static inline size_t byte2slot(uint8_t byte_num) { return byte_num * BITS_PER_BYTE; } static inline size_t slot2offset(size_t slot_num) { return slot_num % BITS_PER_BYTE; } inline char *get_bitmap_byte(size_t byte_num) { char *bitmap = bitmaps_.at(byte_num / BITMAP_SIZE); return &bitmap[byte_num % BITMAP_SIZE]; } // inline void set_bitmap_byte(size_t byte_num, char byte) { // char *bitmap = bitmaps_.at(byte_num / BITMAP_SIZE); // bitmap[byte_num % BITMAP_SIZE] = byte; // } static inline size_t find_first_free_slot_inbyte(uint8_t byte) { uint32_t rbyte = __builtin_bswap32((uint32_t)byte) >> 24; // 使用 32 位变量 return BITS_PER_BYTE - __builtin_ctz(~rbyte) - 1; // 使用 GCC 内建函数找到最低位 1 的位置 } void alloc_new_bitmap() { char *bitmap = static_cast(malloc(BITMAP_SIZE*sizeof(char))); bitmaps_.push_back(bitmap); size += BITMAP_SIZE; } private: std::vector bitmaps_; size_t size; size_t first_empty_slot; std::mutex mtx; }; // read and write slot in disk // first version with no cache class SlotPage { public: SlotPage(std::string &dbname) { slotpage_fname = slotpage_handler_name(dbname); bitmap = new BitMap(); } public: void get_slot(size_t slot_num, struct slot_content *sc) { slotcache->get_slot(slotpage_fname, slot_num, sc); } void set_slot(size_t slot_num, struct slot_content *sc) { slotcache->set_slot(slotpage_fname, slot_num, sc); } size_t alloc_slot() { return bitmap->alloc_slot(); } void dealloc_slot(size_t slot_num) { bitmap->dealloc_slot(slot_num); } private: static std::string slotpage_handler_name(std::string &dbname) { return "./" + dbname + "_slotpage"; } inline static size_t slotpage_pos(size_t slot_num) { return slot_num * sizeof(slot_content); } private: std::string slotpage_fname; BitMap *bitmap; static SlotCache *slotcache; }; SlotCache *SlotPage::slotcache = new SlotCache(); #endif // LEVELDB_SLOTPAGE_H