接下来分析写文件,这个简单,使用visitor设计模式来实现,visitor接口如下,注意Element的visit接口,传入了XMLAttribute指针 来遍历Attribute
class XMLVisitor
{
public:
virtual ~XMLVisitor() {}
/// Visit a document.
virtual bool VisitEnter( const XMLDocument& /*doc*/ ) {
return true;
}
/// Visit a document.
virtual bool VisitExit( const XMLDocument& /*doc*/ ) {
return true;
}
/// Visit an element.
virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) {
return true;
}
/// Visit an element.
virtual bool VisitExit( const XMLElement& /*element*/ ) {
return true;
}
/// Visit a declaration.
virtual bool Visit( const XMLDeclaration& /*declaration*/ ) {
return true;
}
/// Visit a text node.
virtual bool Visit( const XMLText& /*text*/ ) {
return true;
}
/// Visit a comment node.
virtual bool Visit( const XMLComment& /*comment*/ ) {
return true;
}
/// Visit an unknown node.
virtual bool Visit( const XMLUnknown& /*unknown*/ ) {
return true;
}
};
然后给XMLNode增加接口,
virtual bool Accept( XMLVisitor* visitor ) const = 0;
子类实现:
bool XMLText::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
bool XMLComment::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
bool XMLUnknown::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
return visitor->Visit( *this );
}
bool XMLElement::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
if ( !node->Accept( visitor ) ) {
break;
}
}
}
return visitor->VisitExit( *this );
}
bool XMLDocument::Accept( XMLVisitor* visitor ) const
{
TIXMLASSERT( visitor );
if ( visitor->VisitEnter( *this ) ) {
for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
if ( !node->Accept( visitor ) ) {
break;
}
}
}
return visitor->VisitExit( *this );
}
为了输出一份漂亮的xml,我们需要保存int _depth;来决定使用多少个空格来对齐。
visitor适合类型结构固定的情况,接下来就是实现Visitor模式,使用c语言变参函数完成prit函数,在windows平台,首先使用_vscprintf获取format后字符长度,缓冲区增加,使用vsnprintf_s将结果输出到buffer中,_buffer是DynArray< char, 20 >类型,暂时可以理解为std::vector<char>,
void XMLPrinter::Print( const char* format, ... )
{
va_list va;
va_start( va, format );
if ( _fp ) {
vfprintf( _fp, format, va );
}
else {
const int len = TIXML_VSCPRINTF( format, va );
// Close out and re-start the va-args
va_end( va );
TIXMLASSERT( len >= 0 );
va_start( va, format );
TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
TIXML_VSNPRINTF( p, len+1, format, va );
}
va_end( va );
}
有了Printf函数,我们就可以方便的打印文档,先看Text,_textDepth正常情况下是-1,只有输出Text时,才置为_depth-1;
void XMLPrinter::PushText( const char* text, bool cdata )
{
_textDepth = _depth-1;
SealElementIfJustOpened();
if ( cdata ) {
Print( "<![CDATA[%s]]>", text );
}
else {
PrintString( text, true );
}
}
首先确定Element关闭了:
void XMLPrinter::SealElementIfJustOpened()
{
if ( !_elementJustOpened ) {
return;
}
_elementJustOpened = false;
Print( ">" );
}
至于PrintString,则是为了打印转义字符:
void XMLPrinter::PrintString( const char* p, bool restricted )
{
// Look for runs of bytes between entities to print.
const char* q = p;
if ( _processEntities ) {
const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
while ( *q ) {
TIXMLASSERT( p <= q );
// Remember, char is sometimes signed. (How many times has that bitten me?)
if ( *q > 0 && *q < ENTITY_RANGE ) {
// Check for entities. If one is found, flush
// the stream up until the entity, write the
// entity, and keep looking.
if ( flag[(unsigned char)(*q)] ) {
while ( p < q ) {
const size_t delta = q - p;
// %.*s accepts type int as "precision"
const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
Print( "%.*s", toPrint, p );//特殊用法,toPrint制定字符串的宽度
p += toPrint;
}
bool entityPatternPrinted = false;
for( int i=0; i<NUM_ENTITIES; ++i ) {//找到相应的转义字符,输出
if ( entities[i].value == *q ) {
Print( "&%s;", entities[i].pattern );
entityPatternPrinted = true;
break;
}
}
if ( !entityPatternPrinted ) {
// TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
TIXMLASSERT( false );
}
++p;
}
}
++q;
TIXMLASSERT( p <= q );
}
}
// Flush the remaining string. This will be the entire
// string if an entity wasn't found.
TIXMLASSERT( p <= q );
if ( !_processEntities || ( p < q ) ) {
Print( "%s", p );
}
}
XMLDeclaration,需要文件BOM头就输出,
void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
{
if ( writeBOM ) {
static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
Print( "%s", bom );
}
if ( writeDec ) {
PushDeclaration( "xml version=\"1.0\"" );
}
}
PushDeclaration:
void XMLPrinter::PushDeclaration( const char* value )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<?%s?>", value );
}
XMLAttribute:
void XMLPrinter::PushAttribute( const char* name, const char* value )
{
TIXMLASSERT( _elementJustOpened );
Print( " %s=\"", name );
PrintString( value, false );
Print( "\"" );
}
XMLComment:
void XMLPrinter::PushComment( const char* comment )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<!--%s-->", comment );
}
Declaration:
void XMLPrinter::PushDeclaration( const char* value )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<?%s?>", value );
}
XMLUnknown
void XMLPrinter::PushUnknown( const char* value )
{
SealElementIfJustOpened();
if ( _textDepth < 0 && !_firstElement && !_compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
_firstElement = false;
Print( "<!%s>", value );
}
EnterElement:
void XMLPrinter::OpenElement( const char* name, bool compactMode )
{
SealElementIfJustOpened();
_stack.Push( name );
if ( _textDepth < 0 && !_firstElement && !compactMode ) {
Print( "\n" );
}
if ( !compactMode ) {
PrintSpace( _depth );
}
Print( "<%s", name );
_elementJustOpened = true;
_firstElement = false;
++_depth;
}
Exit Element:
void XMLPrinter::CloseElement( bool compactMode )
{
--_depth;
const char* name = _stack.Pop();
if ( _elementJustOpened ) {
Print( "/>" );
}
else {
if ( _textDepth < 0 && !compactMode) {
Print( "\n" );
PrintSpace( _depth );
}
Print( "</%s>", name );
}
if ( _textDepth == _depth ) {
_textDepth = -1;
}
if ( _depth == 0 && !compactMode) {
Print( "\n" );
}
_elementJustOpened = false;
}
这样visitor模式基本代码都编码完毕:
bool XMLPrinter::VisitEnter( const XMLDocument& doc )
{
_processEntities = doc.ProcessEntities();
if ( doc.HasBOM() ) {
PushHeader( true, false );
}
return true;
}
bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
{
const XMLElement* parentElem = 0;
if ( element.Parent() ) {
parentElem = element.Parent()->ToElement();
}
const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
OpenElement( element.Name(), compactMode );
while ( attribute ) {
PushAttribute( attribute->Name(), attribute->Value() );
attribute = attribute->Next();
}
return true;
}
bool XMLPrinter::VisitExit( const XMLElement& element )
{
CloseElement( CompactMode(element) );
return true;
}
bool XMLPrinter::Visit( const XMLText& text )
{
PushText( text.Value(), text.CData() );
return true;
}
bool XMLPrinter::Visit( const XMLComment& comment )
{
PushComment( comment.Value() );
return true;
}
bool XMLPrinter::Visit( const XMLDeclaration& declaration )
{
PushDeclaration( declaration.Value() );
return true;
}
bool XMLPrinter::Visit( const XMLUnknown& unknown )
{
PushUnknown( unknown.Value() );
return true;
}
给XMLDocument增加函数SaveFile
XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
{
// Clear any error from the last save, otherwise it will get reported
// for *this* call.
SetError(XML_SUCCESS, 0, 0);
XMLPrinter stream( fp, compact );
Print( &stream );
return _errorID;
}XMLError XMLDocument::SaveFile( const char* filename, bool compact )
{
FILE* fp = callfopen( filename, "w" );
if ( !fp ) {
SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
return _errorID;
}
SaveFile(fp, compact);
fclose( fp );
return _errorID;
}
至此,xml存储也分析完毕。
接下来对内存池和DynArray分析,首先DynArray和vector的代码差不多,如果分配的内存到头,则将需求的内存*2作为新的内存,复制,并将原来的内存释放。
template <class T, int INITIAL_SIZE>
class DynArray
{
public:
DynArray() {
_mem = _pool;//将指针指向预先分配的内存
_allocated = INITIAL_SIZE;
_size = 0;
}
~DynArray() {
if ( _mem != _pool ) {
delete [] _mem;
}
}
void Clear() {
_size = 0;
}
void Push( T t ) {
TIXMLASSERT( _size < INT_MAX );
EnsureCapacity( _size+1 );
_mem[_size++] = t;
}
<span style="white-space:pre"> </span>//push Array,多分配count个内存,并将内存地址返回
T* PushArr( int count ) {
TIXMLASSERT( count >= 0 );
TIXMLASSERT( _size <= INT_MAX - count );
EnsureCapacity( _size+count );
T* ret = &_mem[_size];
_size += count;
return ret;
}
T Pop() {
TIXMLASSERT( _size > 0 );
return _mem[--_size];
}
void PopArr( int count ) {
TIXMLASSERT( _size >= count );
_size -= count;
}
bool Empty() const {
return _size == 0;
}
T& operator[](int i) {
TIXMLASSERT( i>= 0 && i < _size );
return _mem[i];
}
const T& operator[](int i) const {
TIXMLASSERT( i>= 0 && i < _size );
return _mem[i];
}
const T& PeekTop() const {
TIXMLASSERT( _size > 0 );
return _mem[ _size - 1];
}
int Size() const {
TIXMLASSERT( _size >= 0 );
return _size;
}
int Capacity() const {
TIXMLASSERT( _allocated >= INITIAL_SIZE );
return _allocated;
}
const T* Mem() const {
TIXMLASSERT( _mem );
return _mem;
}
T* Mem() {
TIXMLASSERT( _mem );
return _mem;
}
private:
DynArray( const DynArray& ); // not supported
void operator=( const DynArray& ); // not supported
void EnsureCapacity( int cap ) {
TIXMLASSERT( cap > 0 );
if ( cap > _allocated ) {//如果大于容量
TIXMLASSERT( cap <= INT_MAX / 2 );
int newAllocated = cap * 2;//(⊙o⊙)…内存池常用分配办法
T* newMem = new T[newAllocated];
memcpy( newMem, _mem, sizeof(T)*_size ); // 只能是POD类型
if ( _mem != _pool ) {
delete [] _mem;
}
_mem = newMem;
_allocated = newAllocated;
}
}
T* _mem;//指向内存的指针
T _pool[INITIAL_SIZE];//预先分配的内存
int _allocated; // capacity
int _size; // 当前使用个数
};
MemPoolT,如果内存分配了,只有等析构才能回收所有的内存,对于一般应用而言,对的
template< int ITEM_SIZE >
class MemPoolT : public MemPool
{
public:
MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {}
~MemPoolT() {
Clear();
}
void Clear() {
// Delete the blocks.
while( !_blockPtrs.Empty()) {
Block* b = _blockPtrs.Pop();
delete b;
}
_root = 0;
_currentAllocs = 0;
_nAllocs = 0;
_maxAllocs = 0;
_nUntracked = 0;
}
virtual int ItemSize() const {
return ITEM_SIZE;
}
int CurrentAllocs() const {
return _currentAllocs;
}
virtual void* Alloc() {
if ( !_root ) {//如果链表首元素为空
// Need a new block.
Block* block = new Block();
_blockPtrs.Push( block );
Item* blockItems = block->items;
for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
blockItems[i].next = &(blockItems[i + 1]);
}
blockItems[ITEMS_PER_BLOCK - 1].next = 0;
_root = blockItems;
}
Item* const result = _root;
TIXMLASSERT( result != 0 );
_root = _root->next;
++_currentAllocs;
if ( _currentAllocs > _maxAllocs ) {
_maxAllocs = _currentAllocs;
}
++_nAllocs;
++_nUntracked;
return result;
}
virtual void Free( void* mem ) {
if ( !mem ) {
return;
}
--_currentAllocs;
Item* item = static_cast<Item*>( mem );
#ifdef DEBUG
memset( item, 0xfe, sizeof( *item ) );
#endif
item->next = _root;
_root = item;
}
void Trace( const char* name ) {
printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs,
ITEM_SIZE, _nAllocs, _blockPtrs.Size() );
}
void SetTracked() {
--_nUntracked;
}
int Untracked() const {
return _nUntracked;
}
// This number is perf sensitive. 4k seems like a good tradeoff on my machine.
// The test file is large, 170k.
// Release: VS2010 gcc(no opt)
// 1k: 4000
// 2k: 4000
// 4k: 3900 21000
// 16k: 5200
// 32k: 4300
// 64k: 4000 21000
// Declared public because some compilers do not accept to use ITEMS_PER_BLOCK
// in private part if ITEMS_PER_BLOCK is private
enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE };//内存页在x86下为4K,这样个数的Item正好够一个物理页
private:
MemPoolT( const MemPoolT& ); // not supported
void operator=( const MemPoolT& ); // not supported
//注意为单向列表,union是为了节约内存
union Item {
Item* next;
char itemData[ITEM_SIZE];
};
struct Block {
Item items[ITEMS_PER_BLOCK];
};
DynArray< Block*, 10 > _blockPtrs;//相当于管理页的指针,内存是一页一页的添加。提前分配10个页面
Item* _root;//单向链表首元素
int _currentAllocs;
int _nAllocs;
int _maxAllocs;
int _nUntracked;
};