MongoDB源码阅读之BSON源码分析
1. BSON源码结构
涉及BSON的源码有:
builder.h 包含bson所需的内存管理类和将bson对象转成内存的工具方法 bsontypes.h 定义了bson所需的数据类型列表 oid.h 定义Object ID的数据结构及实现 bsonelement.h 定义了bson的节点 bsonobj.h bson对象(主要对象,提供了数据的基本操作) bsonmisc.h 定义了与bson相关的助手函数(流输入/输出) bsonobjbuilder.h 创建bsonObj的类,用到了builder中定义的内存管理类 bsonobjiterator.h 提供了一个类似STL的接口是element的iterator封装(只实现了基本接口) |
2. Builder的基本结构
以上是builder相关的类图
_BufBuilder是所有builder的基础,通过对模板的实例化,生产出BufBuilder和StackBufBuilder两种Buffer Builder类。BSONObjBuilder将不同数据类型根据Key/Value形式填入BufBuilder。BuilderObj包含了一个BSONObjBuilder和一个fieldName(Key值),并提供了以fieldName为Key向BSONObjBuilder输送数据的方法。其中对应的Array版本,添加了对std::vector和std::list的支持。
3. Buffer Builder(builder.h for BufBuilder)
在builder中提供了两种内存的申请方式:
a) TrivialAllocator
public:
void* Malloc(size_t sz) { return malloc(sz); }
void* Realloc(void *p, size_t sz) { return realloc(p, sz); }
void Free(void *p) { free(p); }
};
从源码上看就是调用了系统的内存申请方式。
b) StackAllocator
建立了一个buffer缓存,防止反复创建,之后的操作在缓存中进行。如果需要的内存大于缓存大小则调用系统函数分配新的内存空间。利用这种内存申请方式减少内存碎片。
public:
enum { SZ = 512 }; //默认缓存大小为512bytes
void* Malloc(size_t sz) {
if( sz <= SZ ) return buf; //优先使用缓存
return malloc(sz); //若缓存不能够提供需求则直接调用系统函数申请内存
}
//重新分配内存
void* Realloc(void *p, size_t sz) {
if( p == buf ) { //使用的是缓存内存
if( sz <= SZ ) return buf; //需要的内存小于缓存,直接返回缓存
void *d = malloc(sz); //所需内存大于缓存,直接调用系统函数申请内存
if ( d == 0 ) //没申请出内存说明内存不足,抛出错误提示
msgasserted( 15912 , "out of memory StackAllocator::Realloc" );
memcpy(d, p, SZ);
return d;
}
return realloc(p, sz); //如果已经是用系统函数申请的内存直接调用系统函数重新分配
}
void Free(void *p) {
if( p != buf )
free(p);
}
private:
char buf[SZ];
};
c) _BufBuilder模板类
BSON利用模板创建了一个可替换内存申请方式的buffer builder。
Builder本身是一个以内存块为基础的,各种数据向内存尾部添加,当内存大小不够时,再申请比当前大小为2倍的大小。
template< class Allocator >
class _BufBuilder {
// non-copyable, non-assignable(禁止复制)
_BufBuilder( const _BufBuilder& );
_BufBuilder& operator=( const _BufBuilder& );
Allocator al; //为内存申请类创建实例
public:
//……
}
向内存尾部添加数据,举例:
*((unsigned char*)grow(sizeof(unsigned char))) = j;
}
void appendChar(char j) {
*((char*)grow(sizeof(char))) = j;
}
void appendNum(char j) {
*((char*)grow(sizeof(char))) = j;
}
void appendNum(short j) {
*((short*)grow(sizeof(short))) = j;
}
void appendNum(int j) {
*((int*)grow(sizeof(int))) = j;
}
void appendNum(unsigned j) {
*((unsigned*)grow(sizeof(unsigned))) = j;
}
//……
//grow判断内存大小长度
/* returns the pre-grow write position */
inline char* grow(int by) {
int oldlen = l;
l += by;
if ( l > size ) { //当长度不够时,扩展长度
grow_reallocate();
}
return data + oldlen;
}
private:
/* "slow" portion of 'grow()' */
void grow_reallocate() {
int a = 64; //起始长度64
while( a < l ) //以64二倍的长度找到合适的长度
a = a * 2;
if ( a > BufferMaxSize ) { //buf长度大于最大长度,抛出错误
std::stringstream ss;
ss << "BufBuilder attempted to grow() to " << a << " bytes, past the 64MB limit.";
msgasserted(13548, ss.str().c_str());
}
data = (char *) al.Realloc(data, a); //从内存类中重新申请内存
if ( data == NULL )
msgasserted( 16070 , "out of memory BufBuilder::grow_reallocate" );
size = a;
}
d) BSONObjBuilder
BSONObjBuilder以BufBuilder为基础,按照数据类型、数据名称、数据值的序列将数据项排列进BufBuilder。
BSONObjBuilder& append(const StringData& fieldName, bool val) {
_b.appendNum((char) Bool); //存入数据类型
_b.appendStr(fieldName); //存入Key值
_b.appendNum((char) (val?1:0)); //存入数据
return *this;
}
/** Append a 32 bit integer element */
BSONObjBuilder& append(const StringData& fieldName, int n) {
_b.appendNum((char) NumberInt);
_b.appendStr(fieldName);
_b.appendNum(n);
return *this;
}
/** Append a 32 bit unsigned element - cast to a signed int. */
BSONObjBuilder& append(const StringData& fieldName, unsigned n) {
return append(fieldName, (int) n);
}
/** Append a NumberLong */
BSONObjBuilder& append(const StringData& fieldName, long long n) {
_b.appendNum((char) NumberLong);
_b.appendStr(fieldName);
_b.appendNum(n);
return *this;
}
4. BSONObj的数据读取
BSONElement是用来从数据流中按照数据类型、名称(Key)、数据获得数据。BSON是对BSONElement的调用方,通过BSONElement以轮询的方式对Key值比较读取数据。猜测MongoDB用这么笨拙的方法对基础数据进行操作是考虑跨文件搜索数据时可以以任意一段数据为开始查找数据。