gStore/KVstore/ISArray/ISBlockManager.cpp

382 lines
9.5 KiB
C++

/*=======================================================================
* File name: ISBlockManager.cpp
* Author: Zongyue Qin
* Mail: qinzongyue@pku.edu.cn
* Last Modified: 2018-02-08
* Description:
* Implementation of class ISBlockManager
* ======================================================================*/
#include "ISBlockManager.h"
using namespace std;
ISBlockManager::ISBlockManager()
{
cur_block_num = 0;
index_len_map.clear();
len_index_map.clear();
FreeBlockList = NULL;
ValueFile = NULL;
BlockToWrite = NULL;
}
ISBlockManager::ISBlockManager(string& _filename, string& _mode, unsigned _keynum )
{
//dir_path = _dir_path;
FreeBlockList_path = _filename + "_FreeBlockList";
ValueFile_path = _filename + "_ValueFile";
if (_mode == "build")
{
FreeBlockList = fopen(FreeBlockList_path.c_str(), "w+b");
ValueFile = fopen(ValueFile_path.c_str(), "w+b");
}
else if (_mode == "open")
{
FreeBlockList = fopen(FreeBlockList_path.c_str(), "r+b");
ValueFile = fopen(ValueFile_path.c_str(), "r+b");
}
else
{
cout << "Error in BLockManager: Invalid Mode" << endl;
return;
}
if (FreeBlockList == NULL || ValueFile == NULL)
{
cout << "Error in ISBlockManager: Open error" << endl;
return;
}
index_len_map.clear();
len_index_map.clear();
BlockToWrite = NULL;
if (_mode == "build")
{
cur_block_num = 0;
unsigned SETBLOCKNUM = 1 << 8;
unsigned requestnum = (unsigned) max(SETBLOCKNUM, _keynum);
AllocBlock(requestnum);
//index_len_map.insert(make_pair(1, requestnum));
//len_index_map.insert(make_pair(requestnum, 1));
}
else // open mode
{
// read cur_block_num
int fd = fileno(FreeBlockList);
pread(fd, &cur_block_num, 1 * sizeof(unsigned), 0);
// read FreeBlockList and establish 2 map
unsigned tmp_index = 0, tmp_len;
off_t offset = 1;
pread(fd, &tmp_index, 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
while (tmp_index > 0)
{
//cout << _filename << ": free block " << tmp_index << " offset " << offset << endl;
pread(fd, &tmp_len, 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
index_len_map.insert(make_pair(tmp_index, tmp_len));
len_index_map.insert(make_pair(tmp_len, tmp_index));
pread(fd, &tmp_index, 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
}
}
}
void
ISBlockManager::SaveFreeBlockList()
{
// cout << "Save Free blocks " << FreeBlockList_path << endl;
map <unsigned, unsigned>::iterator it;
off_t offset = 0;
int fd = fileno(FreeBlockList);
pwrite(fd, &cur_block_num, 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
for(it = index_len_map.begin(); it != index_len_map.end(); it++)
{
// cout << "block " << it->first << " length " << it->second << " offset " << offset << endl;
pwrite(fd, &(it->first), 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
pwrite(fd, &(it->second), 1 * sizeof(unsigned), offset * sizeof(unsigned));
offset++;
//fwrite(&(it->first), sizeof(unsigned), 1, FreeBlockList);
//fwrite(&(it->second), sizeof(unsigned), 1, FreeBlockList);
}
unsigned tmp = 0;
pwrite(fd, &tmp, 1 * sizeof(unsigned), offset * sizeof(unsigned));
//fwrite(&tmp, sizeof(unsigned), 1, FreeBlockList);
}
bool
ISBlockManager::ReadValue(unsigned _blk_index, char *&_str, unsigned &_len)
{
if (_blk_index <= 0)
{
cout << "ReadValue Error: Block index <= 0." << endl;
_str = NULL;
_len = 0;
return false;
}
unsigned next_blk = _blk_index; // index of next block
unsigned Len_left; // how many bits left to read
int fd = fileno(ValueFile);
// fseek(ValueFile, (off_t)BLOCK_SIZE * (_blk_index - 1) + sizeof(unsigned), SEEK_SET);
off_t offset = (off_t)BLOCK_SIZE * (_blk_index - 1) + sizeof(unsigned);
pread(fd, &Len_left, 1 * sizeof(unsigned), offset);
// fread(&Len_left, sizeof(unsigned), 1, ValueFile);
_str = new char [Len_left];
_len = Len_left;
//cout << "Read Value: _len = " << _len << endl;
//char *pstr = _str; // pointer to where data in next reading is stored
if (_str == NULL)
{
cout << "ISBlockManager Error: fail when new" << endl;
return false;
}
do
{
unsigned Bits2read = BLOCK_DATA_SIZE < Len_left ? BLOCK_DATA_SIZE : Len_left;
offset = (off_t)BLOCK_SIZE * (next_blk - 1);
pread(fd, &next_blk, 1 * sizeof(unsigned), offset);
// fseek(ValueFile, (off_t)BLOCK_SIZE * (next_blk - 1), SEEK_SET);
// fread(&next_blk, sizeof(unsigned), 1, ValueFile);
offset += sizeof(unsigned) + sizeof(unsigned);
pread(fd, _str + (_len - Len_left), Bits2read * sizeof(char), offset);
// pread(fd, pstr, Bits2read * sizeof(char), offset);
// fseek(ValueFile, sizeof(unsigned), SEEK_CUR);
// fread(pstr, sizeof(char), Bits2read, ValueFile);
// pstr += Bits2read;
Len_left -= Bits2read;
}while(next_blk > 0 && Len_left > 0);
//cout << "Read Value Check: _len = " << _len << endl;
return true;
}
// Alloc free blocks, here len means number of blocks
bool
ISBlockManager::AllocBlock(unsigned len)
{
if (len == 0)
return true;
unsigned SETBLOCKINC = 1 << 8;
//TODO merge if block ahead is free
unsigned AllocLen = (unsigned) max((unsigned)len, SETBLOCKINC);
unsigned index = cur_block_num + 1;
index_len_map[index] = AllocLen;
len_index_map.insert(make_pair(AllocLen, index));
cur_block_num += AllocLen;
return true;
}
// Get Free Blocks which can fit _len bits and records them in BlockToWrite
bool
ISBlockManager::getWhereToWrite(unsigned _len)
{
// try to find suitable free blocks in current free block lists
// AllocNum is number of blocks which can fit in _len bits
unsigned AllocNum = (unsigned) ((_len + BLOCK_DATA_SIZE - 1) / BLOCK_DATA_SIZE);
// map <unsigned, unsigned>::iterator it = len_index_map.upper_bound(AllocNum - 1);
set <pair<unsigned, unsigned> >::iterator it = len_index_map.lower_bound(make_pair(AllocNum, (unsigned)0));
if (it != len_index_map.end())
{
// prepare BLockToWrite
if (BlockToWrite != NULL)
{
// delete BlockToWrite;
BlockInfo *p=BlockToWrite;
BlockInfo *nextp=p->next;
while(p!=NULL)
{
nextp=p->next;
delete p;
p=nextp;
}
BlockToWrite = NULL;
}
unsigned BaseIndex = it->second;
for(unsigned i = AllocNum; i > 0; i--)
{
BlockInfo *p = new BlockInfo(BaseIndex + i - 1, BlockToWrite);
BlockToWrite = p;
}
// modify 2 maps
unsigned newlen = it->first - AllocNum;
if (newlen > 0)
{
unsigned newindex = BaseIndex + AllocNum;
index_len_map[newindex] = newlen;
len_index_map.insert(make_pair(newlen, newindex));
}
len_index_map.erase(it);
index_len_map.erase(BaseIndex);
return true;
}
// if can't. alloc new free blocks
else
{
//TODO maybe use fragment?
AllocBlock(AllocNum);
return getWhereToWrite(_len);
}
}
unsigned
ISBlockManager::WriteValue(const char *_str, const unsigned _len)
{
if (!getWhereToWrite(_len))
{
return false;
}
// write _str
int fd = fileno(ValueFile);
BlockInfo *p = BlockToWrite;
char *pstr = (char *)_str; // pointer to buffer of where to write next
unsigned len_left = _len; // how many bytes left to write
while (p != NULL)
{
BlockInfo *nextp = p->next;
unsigned Bits2Write = BLOCK_DATA_SIZE < len_left ? BLOCK_DATA_SIZE:len_left;
off_t offset = (off_t)(BLOCK_SIZE) * (p->num - 1);
unsigned NextIndex = 0;
if (nextp != NULL)
{
NextIndex = nextp->num;
}
// write down next block's index
pwrite(fd, &NextIndex, 1 * sizeof(unsigned), offset);
offset += sizeof(unsigned);
// write down how many bits left
pwrite(fd, &len_left, 1 * sizeof(unsigned), offset);
offset += sizeof(unsigned);
// write down value
pwrite(fd, pstr, Bits2Write * sizeof(char), offset);
len_left -= Bits2Write;
pstr += Bits2Write;
p = nextp;
}
if (len_left > 0)
{
cout << "Get Where To Write error: space is not enough" << endl;
return 0;
}
return BlockToWrite->num;
}
// the key is deleted, so free series of blocks, maybe useless if using SSD
bool
ISBlockManager::FreeBlocks(const unsigned index)
{
if (index == 0) return true;
unsigned _index = index;
unsigned next_index;
int fd = fileno(ValueFile);
unsigned curlen = 1;
unsigned cur_index = index;
while (_index > 0)
{
off_t offset = (off_t)(_index - 1) * BLOCK_SIZE;
pread(fd, &next_index, 1 * sizeof(unsigned), offset);
if (next_index - index == 1) // continuous
{
curlen++;
}
else // not continuous
{
// merge if block before or after it is free
map <unsigned, unsigned>::iterator it;
it = index_len_map.lower_bound(cur_index);
if(it != index_len_map.begin())
{
--it;
if (it->first + it->second == cur_index) // block before is free
{
cur_index = it->first;
curlen += it->second;
auto tmp_key = it->second, tmp_val = it->first;
index_len_map.erase(it);
//len_index_map.erase(make_pair(it->second,it->first));
len_index_map.erase(make_pair(tmp_key, tmp_val));
}
}
it = index_len_map.upper_bound(cur_index);
if(it != index_len_map.end())
{
if (curlen + cur_index == it->first) // block after is free
{
curlen += it->second;
auto tmp_key = it->second, tmp_val = it->first;
index_len_map.erase(it);
//len_index_map.erase(make_pair(it->second,it->first));
len_index_map.erase(make_pair(tmp_key, tmp_val));
}
}
index_len_map.insert(make_pair(cur_index, curlen));
len_index_map.insert(make_pair(curlen, cur_index));
curlen = 0;
cur_index = next_index;
}
_index = next_index;
}
return true;
}
ISBlockManager::~ISBlockManager()
{
if (BlockToWrite != NULL)
{
delete BlockToWrite;
BlockToWrite = NULL;
}
fclose(FreeBlockList);
fclose(ValueFile);
}