Browse Source

more refactoring. cleaning out container classes.

Lee Thomason 14 years ago
parent
commit
2c85a711f1
3 changed files with 236 additions and 109 deletions
  1. 57 7
      tinyxml2.cpp
  2. 170 102
      tinyxml2.h
  3. 9 0
      xmltest.cpp

+ 57 - 7
tinyxml2.cpp

@@ -95,6 +95,32 @@ const char* StrPair::GetStr()
 	return start;
 }
 
+/*
+const char* StringPool::Intern( const char* str )
+{
+	// Treat the array as a linear, inplace hash table.
+	// Nothing can get deleted, so that's handy.
+	if ( size > pool.Size()*3/4 ) {
+		DynArray< const char*, 20 > store;
+		for( int i=0; i<pool.Size(); ++i ) {
+			if ( pool[i] != 0 ) {
+				store.Push( pool[i] );
+			}
+		}
+		int newSize = pool.Size() * 2;
+		pool.PopArr( pool.Size() );
+
+		const char** mem = pool.PushArr( newSize );
+		memset( (void*)mem, 0, sizeof(char)*newSize );
+
+		while ( !store.Empty() ) {
+			Intern( store.Pop() );
+		}
+	}
+
+}
+*/
+
 
 // --------- XMLBase ----------- //
 // fixme: should take in the entity/newline flags as param
@@ -279,6 +305,20 @@ XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
 }
 
 
+XMLElement* XMLNode::FirstChildElement( const char* value )
+{
+	for( XMLNode* node=firstChild; node; node=node->next ) {
+		XMLElement* element = node->ToElement();
+		if ( element ) {
+			if ( !value || StringEqual( element->Name(), value ) ) {
+				return element;
+			}
+		}
+	}
+	return 0;
+}
+
+
 void XMLNode::Print( XMLStreamer* streamer )
 {
 	for( XMLNode* node = firstChild; node; node=node->next ) {
@@ -530,6 +570,14 @@ void XMLDocument::InitDocument()
 }
 
 
+XMLElement* XMLDocument::NewElement( const char* name )
+{
+	XMLElement* ele = new XMLElement( this );
+	ele->SetName( name );
+	return ele;
+}
+
+
 int XMLDocument::Parse( const char* p )
 {
 	ClearChildren();
@@ -568,6 +616,7 @@ void XMLDocument::SetError( int error, const char* str1, const char* str2 )
 }
 
 
+/*
 StringStack::StringStack()
 {
 	nPositive = 0;
@@ -602,6 +651,7 @@ const char* StringStack::Pop() {
 	mem.PopArr( strlen(p)+1 );
 	return p+1;
 }
+*/
 
 
 XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false )
@@ -664,11 +714,11 @@ void XMLStreamer::OpenElement( const char* name, bool textParent )
 	if ( elementJustOpened ) {
 		SealElement();
 	}
-	if ( text.NumPositive() == 0 ) {
+	if ( !TextOnStack() ) {
 		PrintSpace( depth );
 	}
 	stack.Push( name );
-	text.Push( textParent ? "T" : "" );
+	text.Push( textParent ? 'T' : 'e' );
 
 	// fixme: can names have entities?
 	fprintf( fp, "<%s", name );
@@ -690,22 +740,22 @@ void XMLStreamer::CloseElement()
 {
 	--depth;
 	const char* name = stack.Pop();
-	int wasPositive = text.NumPositive();
+	bool wasText = TextOnStack();
 	text.Pop();
 
 	if ( elementJustOpened ) {
 		fprintf( fp, "/>" );
-		if ( text.NumPositive() == 0 ) {
+		if ( !wasText ) {
 			fprintf( fp, "\n" );
 		}
 	}
 	else {
-		if ( wasPositive == 0 ) {
+		if ( !wasText ) {
 			PrintSpace( depth );
 		}
 		// fixme can names have entities?
 		fprintf( fp, "</%s>", name );
-		if ( text.NumPositive() == 0 ) {
+		if ( !TextOnStack() ) {
 			fprintf( fp, "\n" );
 		}
 	}
@@ -717,7 +767,7 @@ void XMLStreamer::SealElement()
 {
 	elementJustOpened = false;
 	fprintf( fp, ">" );
-	if ( text.NumPositive() == 0 ) {
+	if ( !TextOnStack() ) {
 		fprintf( fp, "\n" );
 	}
 }

+ 170 - 102
tinyxml2.h

@@ -1,9 +1,23 @@
 #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
+*/
+
 #include <limits.h>
 #include <ctype.h>
 #include <stdio.h>
+#include <memory.h>
 
 #if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)
 	#ifndef DEBUG
@@ -38,18 +52,6 @@ class XMLText;
 
 class XMLStreamer;
 
-/*
-// internal - move to separate namespace
-struct CharBuffer
-{
-	size_t  length;
-	char	mem[1];
-
-	static CharBuffer* Construct( const char* in );
-	static void Free( CharBuffer* );
-};
-*/
-
 class StrPair
 {
 public:
@@ -70,6 +72,8 @@ public:
 	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
@@ -82,6 +86,121 @@ private:
 };
 
 
+template <class T, int INIT>
+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 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:
@@ -125,10 +244,16 @@ public:
 	XMLNode* InsertEndChild( XMLNode* addThis );
 	virtual void Print( XMLStreamer* streamer );
 
+	const char* Value() const			{ return value.GetStr(); }
+	void SetValue( const char* val )	{ value.SetInternedStr( val ); }
+
 	virtual XMLElement* ToElement()		{ return 0; }
 	virtual XMLText*	ToText()		{ return 0; }
 	virtual XMLComment* ToComment()		{ return 0; }
 
+	XMLNode* FirstChild()	{ return firstChild; }
+	XMLElement* FirstChildElement( const char* value=0 );
+
 	// fixme: guarentee null terminator to avoid internal checks
 	virtual char* ParseDeep( char* );
 
@@ -143,6 +268,7 @@ protected:
 	XMLDocument*	document;
 	XMLNode*		parent;
 	bool			isTextParent;
+	mutable StrPair			value;
 
 	XMLNode*		firstChild;
 	XMLNode*		lastChild;
@@ -157,29 +283,31 @@ private:
 
 class XMLText : public XMLNode
 {
+	friend class XMLBase;
+	friend class XMLDocument;
 public:
-	XMLText( XMLDocument* doc )	: XMLNode( doc )	{}
 	virtual ~XMLText()								{}
-
 	virtual void Print( XMLStreamer* streamer );
 	const char* Value() { return value.GetStr(); }
+	void SetValue( const char* );
+
 	virtual XMLText*	ToText()		{ return this; }
 
 	char* ParseDeep( char* );
 
 protected:
+	XMLText( XMLDocument* doc )	: XMLNode( doc )	{}
 
 private:
-	StrPair value;
 };
 
 
 class XMLComment : public XMLNode
 {
+	friend class XMLBase;
+	friend class XMLDocument;
 public:
-	XMLComment( XMLDocument* doc );
 	virtual ~XMLComment();
-
 	virtual void Print( XMLStreamer* );
 	virtual XMLComment*	ToComment()		{ return this; }
 
@@ -188,9 +316,10 @@ public:
 	char* ParseDeep( char* );
 
 protected:
+	XMLComment( XMLDocument* doc );
+
 
 private:
-	StrPair value;
 };
 
 
@@ -213,24 +342,29 @@ private:
 
 class XMLElement : public XMLNode
 {
+	friend class XMLBase;
+	friend class XMLDocument;
 public:
-	XMLElement( XMLDocument* doc );
 	virtual ~XMLElement();
 
-	const char* Name() { return name.GetStr(); }
+	const char* Name() const		{ return Value(); }
+	void SetName( const char* str )	{ SetValue( str ); }
+
 	virtual void Print( XMLStreamer* );
 
 	virtual XMLElement* ToElement() { return this; }
-	virtual bool IsClosingElement() const { return closing; }
 
+	// internal:
+	virtual bool IsClosingElement() const { return closing; }
 	char* ParseDeep( char* p );
 
 protected:
+	XMLElement( XMLDocument* doc );
 
 private:
 	char* ParseAttributes( char* p, bool *closedElement );
 
-	StrPair name;
+	mutable StrPair name;
 	bool closing;
 	XMLAttribute* rootAttribute;
 	XMLAttribute* lastAttribute;
@@ -249,6 +383,8 @@ public:
 
 	void Print( XMLStreamer* streamer=0 );
 
+	XMLElement* NewElement( const char* name );
+
 	enum {
 		NO_ERROR = 0,
 		ERROR_ELEMENT_MISMATCH,
@@ -262,6 +398,8 @@ public:
 	const char* GetErrorStr1() const { return errorStr1; }
 	const char* GetErrorStr2() const { return errorStr2; }
 
+//	const char* Intern( const char* );
+
 private:
 	XMLDocument( const XMLDocument& );	// intentionally not implemented
 	void InitDocument();
@@ -270,84 +408,7 @@ private:
 	const char* errorStr1;
 	const char* errorStr2;
 	char* charBuffer;
-};
-
-
-template <class T, int INIT>
-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;
-	}
-	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 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
+	//StringStack stringPool;
 };
 
 
@@ -368,6 +429,13 @@ private:
 	void SealElement();
 	void PrintSpace( int depth );
 	void PrintString( const char* );	// prints out, after detecting entities.
+	bool TextOnStack() const { 
+		for( int i=0; i<text.Size(); ++i ) { 
+			if ( text[i] == 'T' ) 
+				return true; 
+		} 
+		return false; 
+	}
 
 	FILE* fp;
 	int depth;
@@ -377,8 +445,8 @@ private:
 	};
 	bool entityFlag[ENTITY_RANGE];
 
-	DynArray< const char*, 40 > stack;
-	StringStack text;
+	DynArray< const char*, 10 > stack;
+	DynArray< char, 10 > text;
 };
 
 

+ 9 - 0
xmltest.cpp

@@ -52,5 +52,14 @@ int main( int argc, const char* argv )
 			printf( "----------------------------------------------\n" );
 		}
 	}
+	{
+		static const char* test = "<element>Text before.</element>";
+		XMLDocument doc;
+		doc.Parse( test );
+		XMLElement* root = doc.FirstChildElement();
+		XMLElement* newElement = doc.NewElement( "Subelement" );
+		root->InsertEndChild( newElement );
+		doc.Print();
+	}
 	return 0;
 }