Better attribute system. Removes redundant list searching of LinkAttribute. Faster parsing on initial read. Added badly needed missing test cases.
diff --git a/tinyxml2.cpp b/tinyxml2.cpp
index 4ce9b32..fbeca4e 100644
--- a/tinyxml2.cpp
+++ b/tinyxml2.cpp
@@ -1092,34 +1092,33 @@
}
-
XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
{
- XMLAttribute* attrib = FindAttribute( name );
+ XMLAttribute* last = 0;
+ XMLAttribute* attrib = 0;
+ for( attrib = rootAttribute;
+ attrib;
+ last = attrib, attrib = attrib->next )
+ {
+ if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
+ break;
+ }
+ }
if ( !attrib ) {
attrib = new (document->attributePool.Alloc() ) XMLAttribute();
attrib->memPool = &document->attributePool;
- LinkAttribute( attrib );
+ if ( last ) {
+ last->next = attrib;
+ }
+ else {
+ rootAttribute = attrib;
+ }
attrib->SetName( name );
}
return attrib;
}
-void XMLElement::LinkAttribute( XMLAttribute* attrib )
-{
- if ( rootAttribute ) {
- XMLAttribute* end = rootAttribute;
- while ( end->next )
- end = end->next;
- end->next = attrib;
- }
- else {
- rootAttribute = attrib;
- }
-}
-
-
void XMLElement::DeleteAttribute( const char* name )
{
XMLAttribute* prev = 0;
@@ -1142,6 +1141,7 @@
char* XMLElement::ParseAttributes( char* p )
{
const char* start = p;
+ XMLAttribute* prevAttribute = 0;
// Read the attributes.
while( p ) {
@@ -1162,7 +1162,19 @@
document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
return 0;
}
- LinkAttribute( attrib );
+ // There is a minor bug here: if the attribute in the source xml
+ // document is duplicated, it will not be detected and the
+ // attribute will be doubly added. However, tracking the 'prevAttribute'
+ // avoids re-scanning the attribute list. Preferring performance for
+ // now, may reconsider in the future.
+ if ( prevAttribute ) {
+ prevAttribute->next = attrib;
+ prevAttribute = attrib;
+ }
+ else {
+ rootAttribute = attrib;
+ prevAttribute = rootAttribute;
+ }
}
// end of the tag
else if ( *p == '/' && *(p+1) == '>' ) {
diff --git a/tinyxml2.h b/tinyxml2.h
index 76e47ac..4ebb87f 100644
--- a/tinyxml2.h
+++ b/tinyxml2.h
@@ -24,25 +24,13 @@
#ifndef TINYXML2_INCLUDED
#define TINYXML2_INCLUDED
-#if 1
- #include <cctype>
- #include <climits>
- #include <cstdio>
- #include <cstring>
- #include <cstdarg>
-#else
- // Not completely sure all the interesting systems
- // can handle the new headers; can switch this if
- // there is an include problem.
- #include <limits.h>
- #include <ctype.h>
- #include <stdio.h>
- #include <memory.h> // Needed by mac.
-#endif
-
+#include <cctype>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <cstdarg>
/*
- TODO: add 'lastAttribute' for faster parsing.
TODO: intern strings instead of allocation.
*/
/*
@@ -978,10 +966,13 @@
XMLAttribute* FindAttribute( const char* name );
XMLAttribute* FindOrCreateAttribute( const char* name );
- void LinkAttribute( XMLAttribute* attrib );
+ //void LinkAttribute( XMLAttribute* attrib );
char* ParseAttributes( char* p );
int closingType;
+ // The attribute list is ordered; there is no 'lastAttribute'
+ // because the list needs to be scanned for dupes before adding
+ // a new attribute.
XMLAttribute* rootAttribute;
};
diff --git a/xmltest.cpp b/xmltest.cpp
index 0991e2d..a88d1bc 100644
--- a/xmltest.cpp
+++ b/xmltest.cpp
@@ -760,6 +760,32 @@
}
XMLTest( "Error in snprinf handling.", true, doc.Error() );
}
+
+ {
+ // Attribute ordering.
+ static const char* xml = "<element attrib1=\"1\" attrib2=\"2\" attrib3=\"3\" />";
+ XMLDocument doc;
+ doc.Parse( xml );
+ XMLElement* ele = doc.FirstChildElement();
+
+ const XMLAttribute* a = ele->FirstAttribute();
+ XMLTest( "Attribute order", "1", a->Value() );
+ a = a->Next();
+ XMLTest( "Attribute order", "2", a->Value() );
+ a = a->Next();
+ XMLTest( "Attribute order", "3", a->Value() );
+ XMLTest( "Attribute order", "attrib3", a->Name() );
+
+ ele->DeleteAttribute( "attrib2" );
+ a = ele->FirstAttribute();
+ XMLTest( "Attribute order", "1", a->Value() );
+ a = a->Next();
+ XMLTest( "Attribute order", "3", a->Value() );
+
+ ele->DeleteAttribute( "attrib1" );
+ ele->DeleteAttribute( "attrib3" );
+ XMLTest( "Attribute order (empty)", false, ele->FirstAttribute() ? true : false );
+ }
// -------- Handles ------------
{