|
|
@ -0,0 +1,236 @@ |
|
|
|
// |
|
|
|
// Created by 马也驰 on 2024/12/9. |
|
|
|
// |
|
|
|
|
|
|
|
#ifndef LEVELDB_SLOTPAGE_H |
|
|
|
#define LEVELDB_SLOTPAGE_H |
|
|
|
|
|
|
|
#include <cassert> |
|
|
|
#include <cstdint> |
|
|
|
#include <cstdlib> |
|
|
|
#include <vector> |
|
|
|
#include <iostream> |
|
|
|
#include <fstream> |
|
|
|
#include <mutex> |
|
|
|
|
|
|
|
#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<struct slot_content*>(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<const char*>(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<char*>(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<char*>(malloc(BITMAP_SIZE*sizeof(char))); |
|
|
|
bitmaps_.push_back(bitmap); |
|
|
|
size = BITMAP_SIZE; |
|
|
|
first_empty_slot = 0; |
|
|
|
} |
|
|
|
|
|
|
|
void dealloc_slot(size_t slot_num) { |
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
size_t alloc_slot() { |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
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<char*>(malloc(BITMAP_SIZE*sizeof(char))); |
|
|
|
bitmaps_.push_back(bitmap); |
|
|
|
size += BITMAP_SIZE; |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
std::vector<char *> bitmaps_; |
|
|
|
size_t size; |
|
|
|
size_t first_empty_slot; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 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 |