#ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED /* TODO - const and non-const versions of API - memory pool the class construction - attribute accessors - node navigation - handles - visit pattern - change streamer? - make constructors protected - hide copy constructor - hide = operator - #define to remove mem-pooling, and make thread safe - UTF8 support: isAlpha, etc. (No reason to ever cast to base) XMLBase -> Utility XMLNode Document Pooled Element Text */ #include #include #include #include #if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) #ifndef DEBUG #define DEBUG #endif #endif #if defined(DEBUG) #if defined(_MSC_VER) #define TIXMLASSERT( x ) if ( !(x)) { _asm { int 3 } } //if ( !(x)) WinDebugBreak() #elif defined (ANDROID_NDK) #include #define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } #else #include #define TIXMLASSERT assert #endif #else #define TIXMLASSERT( x ) {} #endif namespace tinyxml2 { class XMLDocument; class XMLElement; class XMLAttribute; class XMLComment; class XMLNode; class XMLText; class XMLStreamer; class StrPair { public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02, TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_NAME = 0, ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, COMMENT = NEEDS_NEWLINE_NORMALIZATION, }; StrPair() : flags( 0 ), start( 0 ), end( 0 ) {} void Set( char* start, char* end, int flags ) { this->start = start; this->end = end; this->flags = flags | NEEDS_FLUSH; } const char* GetStr(); bool Empty() const { return start == end; } void SetInternedStr( const char* str ) { this->start = (char*) str; this->end = 0; this->flags = 0; } private: enum { NEEDS_FLUSH = 0x100 }; // After parsing, if *end != 0, it can be set to zero. int flags; char* start; char* end; }; template class DynArray { public: DynArray< T, INIT >() { mem = pool; allocated = INIT; size = 0; } ~DynArray() { if ( mem != pool ) { delete mem; } } void Push( T t ) { EnsureCapacity( size+1 ); mem[size++] = t; } T* PushArr( int count ) { EnsureCapacity( size+count ); T* ret = &mem[size]; size += count; return ret; } T Pop() { 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]; } int Size() const { return size; } const T* Mem() const { return mem; } T* Mem() { return mem; } private: void EnsureCapacity( int cap ) { if ( cap > allocated ) { int newAllocated = cap * 2; T* newMem = new T[newAllocated]; memcpy( newMem, mem, sizeof(T)*size ); // warning: not using constructors, only works for PODs if ( mem != pool ) delete [] mem; mem = newMem; allocated = newAllocated; } } T* mem; T pool[INIT]; int allocated; // objects allocated int size; // number objects in use }; class MemPool { public: MemPool() {} virtual ~MemPool() {} virtual int ItemSize() const = 0; virtual void* Alloc() = 0; virtual void Free( void* ) = 0; }; template< int SIZE > class MemPoolT : public MemPool { public: MemPoolT() : root( 0 ), nAlloc( 0 ) {} ~MemPoolT() { // Delete the blocks. for( int i=0; ichunk[i].next = &block->chunk[i+1]; } block->chunk[COUNT-1].next = 0; root = block->chunk; } void* result = root; root = root->next; ++nAlloc; return result; } virtual void Free( void* mem ) { if ( !mem ) return; --nAlloc; Chunk* chunk = (Chunk*)mem; memset( chunk, 0xfe, sizeof(Chunk) ); chunk->next = root; root = chunk; } private: enum { COUNT = 1024/SIZE }; union Chunk { Chunk* next; char mem[SIZE]; }; struct Block { Chunk chunk[COUNT]; }; DynArray< Block*, 10 > blockPtrs; Chunk* root; int nAlloc; }; /* class StringStack { public: StringStack(); virtual ~StringStack(); void Push( const char* str ); const char* Pop(); int NumPositive() const { return nPositive; } private: DynArray< char, 50 > mem; int nPositive; // number of strings with len > 0 }; */ /* class StringPool { public: enum { INIT_SIZE=20 }; StringPool() : size( 0 ) { const char** mem = pool.PushArr( INIT_SIZE ); memset( (void*)mem, 0, sizeof(char)*INIT_SIZE ); } ~StringPool() {} const char* Intern( const char* str ); private: // FNV hash int Hash( const char* s ) { #define FNV_32_PRIME ((int)0x01000193) int hval = 0; while (*s) { hval *= FNV_32_PRIME; hval ^= (int)*s++; } return hval; } int size; DynArray< const char*, INIT_SIZE > pool; // the hash table StringStack store; // memory for the interned strings }; */ class XMLBase { public: public: XMLBase() {} virtual ~XMLBase() {} static const char* SkipWhiteSpace( const char* p ) { while( isspace( *p ) ) { ++p; } return p; } static char* SkipWhiteSpace( char* p ) { while( isspace( *p ) ) { ++p; } return p; } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { int n = 0; if ( p == q ) { return true; } while( *p && *q && *p == *q && n elementPool; MemPoolT< sizeof(XMLAttribute) > attributePool; MemPoolT< sizeof(XMLText) > textPool; MemPoolT< sizeof(XMLComment) > commentPool; }; class XMLStreamer { public: XMLStreamer( FILE* file ); ~XMLStreamer() {} void OpenElement( const char* name, bool textParent ); void PushAttribute( const char* name, const char* value ); void CloseElement(); void PushText( const char* text ); void PushComment( const char* comment ); private: void SealElement(); void PrintSpace( int depth ); void PrintString( const char* ); // prints out, after detecting entities. bool TextOnStack() const { for( int i=0; i stack; DynArray< char, 10 > text; }; }; // tinyxml2 #endif // TINYXML2_INCLUDED