B+树实现

BPlusTree 是个模板类
用法举例:
/* 
 * @param bkSize 区块大小,及每个数据块的大小,建议与硬盘的区块大小相同(一般为512或4096),此值不能过小否则会导致初始化失败.
 * @param filePath b+树关联的文件位置.
 * @param kMax key的最大值
 */
BPlusTree<int, int> bpt(512, "test", INT_MAX);
bpt.add(1, 1); //第一个参数是key第二个参数是value
int value;
if(bpt.get(1, &value))
    cout<<"value: "<<value<<endl;
else
    cout<<"未找到key: "<<1<<endl;
if(bpt.del(1))
    cout<<"删除key: "<<1<<"成功"<<endl;
else
    cout<<
"删除key: "<<1<<"失败"<<endl;
cout<<"B+树内包含"<<bpt.size()<<"个元素"<<endl;
bpt.flush();//此函数其实是调用fstream的flush函数,将缓冲区的内容真正写入硬盘中. 

//
//
// BPlusTree.h//
//
/* @author xiao
 * 
 */

#include "algorithm.h"

#include <fstream>
#include <cstring>
#include <stack>
#include <vector>

#ifdef MEM_DEBUG
#define memcpy(X, Y, Z) mcpy((X), (Y), (Z), __LINE__, buf1, buf2, buf3, blockSize)
#define memmove(X, Y, Z) mcpy((X), (Y), (Z), __LINE__, buf1, buf2, buf3, blockSize)
#endif


template<typename TK, typename TV>
class BPlusTree
{
private:
typedef unsigned int UINT;
typedef char* PCHAR;

struct KIndex
{
TK key;
std::ios::pos_type child;
};
struct KVPair
{
TK key;
TV value;
};
//根节点
struct Root
{
UINT number; //总数量
UINT floor; //总层数
std::ios::pos_type fNode; //头节点位置
};
//每个区块的头
struct NodeHead
{
UINT number; //本区块内容数量
};

typedef KIndex* PKIndex;
typedef NodeHead* PNodeHead;
typedef KVPair* PKVPair;
private:
Root rMsg;

UINT blockSize; //区块大小
UINT keyNumberPerNode; //索引区块每个区块索引数量
UINT valueNumberPerLeaf; //内容区块每个区块内容数量
bool success; //是否成功
std::fstream fileStream;

PCHAR buf1;
PCHAR buf2;
PCHAR buf3;

TK keyMax;
std::stack<std::ios::pos_type, std::vector<std::ios::pos_type>> posStack;
std::stack<int, std::vector<int>> pointStack;

private:
void initialise();
void setNodeWriteBuf(PCHAR buf, PKIndex p, UINT n);
void setLeafWriteBuf(PCHAR buf, PKVPair p, UINT n);
void readBlock(PCHAR buf, std::ios::pos_type pos);
void writeBlock(PCHAR buf, std::ios::pos_type pos);
void flushRootMsg();
void clearStack();
std::ios::pos_type append(PCHAR buf);
bool addIndex(PCHAR buf1, PCHAR buf2, PKIndex index);

public:
operator bool();
/* 
 * @param bkSize 区块大小,及每个数据块的大小,建议与硬盘的区块大小相同(一般为512或4096),此值不能过小否则会导致初始化失败.
 * @param filePath b+树关联的文件位置.
 * @param kMax key的最大值
 */
BPlusTree(UINT bkSize, char* filePath, TK kMax);
~BPlusTree();

bool isSuccess();

void add(TK key, TV value); //增加
void flush(); //将内容从缓冲区刷入硬盘
bool get(TK key, TV* value); //取得key的value 放入value指针所指区域,如果失败,返回false;
bool del(TK key); //删除key的value 如果失败返回false;
unsigned int size(); //返回总数
};

template<typename TK, typename TV>
void BPlusTree<TK, TV>::initialise()
{
fileStream.clear();
fileStream.seekp(0, std::ios::beg);
std::ios::pos_type root = fileStream.tellp();
fileStream.write(buf1, blockSize);

rMsg.fNode = fileStream.tellp();
rMsg.floor = 1;
fileStream.write(buf1, blockSize);

KIndex index;
index.key = keyMax;

index.child = fileStream.tellp();
this->setNodeWriteBuf(buf1, 0, 0);
fileStream.write(buf1, blockSize);

fileStream.clear();
fileStream.seekg(0, std::ios::beg);
fileStream.write((PCHAR)&rMsg, sizeof(rMsg));

fileStream.seekp(rMsg.fNode);
this->setNodeWriteBuf(buf1, &index, 1);
fileStream.write(buf1, blockSize);
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::setNodeWriteBuf(PCHAR buf, PKIndex p, UINT n)
{
PNodeHead head;
head = (PNodeHead)buf;
head->number = n;
PKIndex pointer = (PKIndex)(buf + sizeof(NodeHead));
for(UINT i=0;i<n;++i)
{
*pointer = p[i];
++pointer;
}
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::setLeafWriteBuf(PCHAR buf, PKVPair p, UINT n)
{
PNodeHead head;
head = (PNodeHead)buf;
head->number = n;
PKVPair pointer = (PKVPair)(buf + sizeof(NodeHead));
for(int i=0;i<n;++i)
{
*pointer = p[i];
++pointer;
}
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::readBlock(PCHAR buf, std::ios::pos_type pos)
{
fileStream.clear();
fileStream.seekp(pos);
fileStream.read(buf, blockSize);
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::writeBlock(PCHAR buf, std::ios::pos_type pos)
{
fileStream.clear();
fileStream.seekp(pos);
fileStream.write(buf, blockSize);
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::flushRootMsg()
{
fileStream.clear();
fileStream.seekg(0, std::ios::beg);
fileStream.write((PCHAR)&rMsg, sizeof(rMsg));
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::clearStack()
{
while(!posStack.empty())
posStack.pop();
while(!pointStack.empty())
pointStack.pop();
}

template<typename TK, typename TV>
std::ios::pos_type BPlusTree<TK, TV>::append(PCHAR buf)
{
fileStream.clear();
fileStream.seekp(0, std::ios::end);
std::ios::pos_type pos = fileStream.tellp();
fileStream.write(buf, blockSize);
return pos;
}

template<typename TK, typename TV>
bool BPlusTree<TK, TV>::addIndex(PCHAR buf1, PCHAR buf2, PKIndex index)
{
PNodeHead head = (PNodeHead)buf1;
PKIndex indexHead = (PKIndex)(buf1 + sizeof(NodeHead));
int h = pointStack.top();
pointStack.pop();
if(head->number < keyNumberPerNode)
{
std::memmove(indexHead + h + 1, indexHead + h, (head->number - h) * sizeof(KIndex));
indexHead[h] = *index;
++head->number;
return false;
}
else
{
PNodeHead buf2Head = (PNodeHead)buf2;
PKIndex buf2Index = (PKIndex)(buf2 + sizeof(NodeHead));
int moveNumber = head->number / 2;
if(h == head->number)
buf2Index[moveNumber] = *index;
else
{
buf2Index[moveNumber] = indexHead[head->number - 1];
//以下内容可优化

std::memmove(indexHead + h + 1, indexHead + h, (head->number - h - 1) * sizeof(KIndex));
indexHead[h] = *index;
}
std::memcpy(buf2Index, indexHead + head->number - moveNumber, moveNumber * sizeof(KIndex));

head->number -= moveNumber;
buf2Head->number = moveNumber + 1;
index->key = buf2Index->key;
index->child = this->append(buf2);
return true;
}
}

/* public function
 * @author xiao
 */

template<typename TK, typename TV>
BPlusTree<TK, TV>::operator bool()
{
return success;
}

template<typename TK, typename TV>
BPlusTree<TK, TV>::BPlusTree(UINT bkSize, char* filePath, TK kMax)
{
keyMax = kMax;
buf1 = new char[bkSize];
std::memset(buf1, 0, bkSize);
buf2 = new char[bkSize];
std::memset(buf2, 0, bkSize);
buf3 = new char[bkSize];
std::memset(buf3, 0, bkSize);

fileStream.open(filePath, ios::binary | ios::in | ios::out);

if(!fileStream)
{
std::ofstream of(filePath);
of.close();
fileStream.clear();
fileStream.close();
fileStream.open(filePath, ios::binary | ios::in | ios::out);
}

if(!fileStream)
{
success = false;
return;
}

blockSize = bkSize;
keyNumberPerNode = (blockSize - sizeof(NodeHead)) / (sizeof(KIndex));
valueNumberPerLeaf = (blockSize - sizeof(NodeHead)) / (sizeof(KVPair));
if(blockSize < sizeof(NodeHead) || blockSize < sizeof(Root) || keyNumberPerNode < 3 || valueNumberPerLeaf < 3)
{
success = false;
return;
}
rMsg.number = 0;

fileStream.read((PCHAR)&rMsg, sizeof(rMsg));

if(fileStream.gcount() != sizeof(rMsg))
this->initialise();
}

template<typename TK, typename TV>
BPlusTree<TK, TV>::~BPlusTree()
{
delete []buf1;
delete []buf2;
delete []buf3;
}

template<typename TK, typename TV>
bool BPlusTree<TK, TV>::isSuccess()
{
return success;
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::add(TK key, TV value)
{
std::ios::pos_type pos = rMsg.fNode;
PKIndex curIndex;
for(UINT i=0;i<rMsg.floor;++i)
{
posStack.push(pos);
this->readBlock(buf1, pos);
PNodeHead head = (PNodeHead)buf1;
curIndex = (PKIndex)(buf1 + sizeof(NodeHead));
bool isEqual;
int h = binarySearch((PCHAR)(&curIndex->key), 0, head->number - 1, key, sizeof(KIndex), isEqual);
if(isEqual)
h += 1;
if(h == 0)
{
curIndex->key = key;
this->writeBlock(buf1, pos);
pointStack.push(1);
}
else
{
curIndex += (h - 1);
pointStack.push(h);
}
pos = curIndex->child;
}
this->readBlock(buf2, curIndex->child);
PNodeHead kvHead = (PNodeHead)buf2;
PKVPair kvPair = (PKVPair)(buf2 + sizeof(NodeHead));
bool isEqual;
int h = binarySearch((PCHAR)&kvPair->key, 0, kvHead->number - 1, key, sizeof(KVPair), isEqual);
if(isEqual)
{
kvPair[h].value = value;
this->writeBlock(buf2, curIndex->child);
this->clearStack();
return;
}
if(kvHead->number < valueNumberPerLeaf)
{
std::memmove(kvPair + h + 1, kvPair + h, (kvHead->number - h) * sizeof(KVPair));
++kvHead->number;
kvPair[h].key = key;
kvPair[h].value = value;
this->writeBlock(buf2, curIndex->child);
}
else
{
UINT moveNumber = valueNumberPerLeaf / 2;
KIndex newIndex;
PKVPair buf3KV = (PKVPair)(buf3 + sizeof(NodeHead));
if(h == kvHead->number)
{
buf3KV[moveNumber].key = key;
buf3KV[moveNumber].value = value;
}
else
{
buf3KV[moveNumber] = kvPair[kvHead->number - 1];
//以下内容可优化

std::memmove(kvPair + h + 1, kvPair + h, (kvHead->number - h - 1) * sizeof(KVPair));
kvPair[h].key = key;
kvPair[h].value = value;
}
std::memcpy(buf3KV, kvPair + kvHead->number - moveNumber, moveNumber * sizeof(KVPair));

PNodeHead buf3Head = (PNodeHead)buf3;
buf3Head->number = moveNumber + 1;
newIndex.key = buf3KV->key;
newIndex.child = this->append(buf3);

kvHead->number -= moveNumber;
this->writeBlock(buf2, curIndex->child);

std::ios::pos_type pos = posStack.top();
posStack.pop();
if(this->addIndex(buf1, buf3, &newIndex))
{
this->writeBlock(buf1, pos);
bool changeRoot = true;
while(!posStack.empty())
{
pos = posStack.top();
posStack.pop();
this->readBlock(buf1, pos);
if(!this->addIndex(buf1, buf3, &newIndex))
{
changeRoot = false;
this->writeBlock(buf1, pos);
break;
}
this->writeBlock(buf1, pos);
}
if(changeRoot)
{
PNodeHead buf2Head = (PNodeHead)buf2;
PKIndex buf1Index = (PKIndex)(buf1 + sizeof(NodeHead));
PKIndex buf2Index = (PKIndex)(buf2 + sizeof(NodeHead));
buf2Head->number = 2;
buf2Index[0].key = buf1Index->key;
buf2Index[0].child = pos;
buf2Index[1] = newIndex;
rMsg.fNode = this->append(buf2);
++rMsg.floor;
}
}
else
{
this->writeBlock(buf1, pos);
}
}

++rMsg.number;
this->flushRootMsg();
this->clearStack();
}

template<typename TK, typename TV>
void BPlusTree<TK, TV>::flush()
{
fileStream.flush();
}

template<typename TK, typename TV>
bool BPlusTree<TK, TV>::get(TK key, TV* value)
{
std::ios::pos_type pos = rMsg.fNode;
PKIndex preIndex, curIndex;
for(UINT i=0;i<rMsg.floor;++i)
{
this->readBlock(buf1, pos);
PNodeHead head = (PNodeHead)buf1;
curIndex = preIndex = (PKIndex)(buf1 + sizeof(NodeHead));
UINT j;
if(key < preIndex->key)
{
return false;
}
else
{
for(j=1;j<head->number;++j)
{
preIndex = curIndex;
++curIndex;
if(key < curIndex->key)
{
curIndex = preIndex;
break;
}
}
}
pos = curIndex->child;
}
this->readBlock(buf2, curIndex->child);
PNodeHead kvHead = (PNodeHead)buf2;
PKVPair kvPair = (PKVPair)(buf2 + sizeof(NodeHead));
bool isEqual;
int h = binarySearch((PCHAR)&kvPair->key, 0, kvHead->number - 1, key, sizeof(KVPair), isEqual);
if(isEqual)
{
*value = kvPair[h].value;
return true;
}
return false;
}

template<typename TK, typename TV>
bool BPlusTree<TK, TV>::del(TK key)
{
std::ios::pos_type pos = rMsg.fNode;
PKIndex curIndex;
for(UINT i=0;i<rMsg.floor;++i)
{
this->readBlock(buf1, pos);
PNodeHead head = (PNodeHead)buf1;
curIndex = (PKIndex)(buf1 + sizeof(NodeHead));
bool isEqual;
int h = binarySearch((PCHAR)(&curIndex->key), 0, head->number - 1, key, sizeof(KIndex), isEqual);
if(isEqual)
++h;
if(h == 0)
return false;
curIndex += (h - 1);
pos = curIndex->child;
}
this->readBlock(buf2, curIndex->child);
PNodeHead kvHead = (PNodeHead)buf2;
PKVPair kvPair = (PKVPair)(buf2 + sizeof(NodeHead));
bool isEqual;
int h = binarySearch((PCHAR)&kvPair->key, 0, kvHead->number - 1, key, sizeof(KVPair), isEqual);
if(!isEqual)
{
this->clearStack();
return false;
}

std::memmove(kvPair + h, kvPair + h + 1, (kvHead->number - h - 1) * sizeof(KVPair));
--kvHead->number;
this->writeBlock(buf2, curIndex->child);

--rMsg.number;
this->flushRootMsg();
this->clearStack();
return true;
}

template<typename TK, typename TV>
unsigned int BPlusTree<TK, TV>::size()
{
return rMsg.number;
}  

//
//algorithm.h//
//

 
#include <iostream>

template<typename T>
int binarySearch(char* kp, int head, int end, T key, unsigned int padding, bool& isEqual)
{
while(head <= end)
{
int middle = (head + end) / 2;
T *pk = (T*)(kp + middle* padding);
if(*pk < key)
head = middle + 1;
if(*pk > key)
end = middle - 1;
if(*pk == key)
{
isEqual = true;
return middle;
}
}
isEqual = false;
return head;
}

template<typename T>
bool between(T a, T b, T v)
{
return v>=a && v<b;
}

#ifdef MEM_DEBUG
namespace std
{
void mcpy(void* dst, void* src, int size, int line, char* buf1, char* buf2, char* buf3, unsigned int blockSize);
}
#endif


///
algorithm.cpp/
//  

 
#include "algorithm.h"

#ifdef MEM_DEBUG
namespace std
{
void mcpy(void* dst, void* src, int size, int line, char* buf1, char* buf2, char* buf3, unsigned int blockSize)
{
char *d = (char*)dst;
char *s = (char*)src;
d += size;
if((!between(buf1, buf1 + blockSize, d)) && (!between(buf2, buf2 + blockSize, d)) && (!between(buf3, buf3 + blockSize, d)))
{
std::cout<<"第N行问题: "<<line<<std::endl;
}
std::memmove(dst, src, size);
}
}
#endif



//如果定义全局宏MEM_DEBUG就会对所有的memcpy和memmove函数进行溢出检查(只对BPlusTree类有效) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值