diff --git a/CMakeLists.txt b/CMakeLists.txt index 0261048..57f7e20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,19 +23,9 @@ include(GNUInstallDirs) set(GENERIC_LIB_VERSION "4.0.1") set(GENERIC_LIB_SOVERSION "4") - -################################ -# Add common source - -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/.") - ################################ # Add definitions -if(MSVC) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) -endif(MSVC) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") ################################ @@ -65,11 +55,29 @@ set_target_properties(tinyxml2 PROPERTIES VERSION "${GENERIC_LIB_VERSION}" SOVERSION "${GENERIC_LIB_SOVERSION}") + if(DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") - target_include_directories(tinyxml2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/.") + target_include_directories(tinyxml2 PUBLIC + $ + $) + + if(MSVC) + target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS) + endif(MSVC) +else() + include_directories(${PROJECT_SOURCE_DIR}) + + if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + endif(MSVC) endif() +# export targets for find_package config mode +export(TARGETS tinyxml2 + FILE ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake) + install(TARGETS tinyxml2 + EXPORT ${CMAKE_PROJECT_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -83,11 +91,30 @@ set_target_properties(tinyxml2_static PROPERTIES SOVERSION "${GENERIC_LIB_SOVERSION}") set_target_properties( tinyxml2_static PROPERTIES OUTPUT_NAME tinyxml2 ) +target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS) + if(DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") - target_include_directories(tinyxml2_static INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/.") + target_include_directories(tinyxml2_static PUBLIC + $ + $) + + if(MSVC) + target_compile_definitions(tinyxml2 PUBLIC -D_CRT_SECURE_NO_WARNINGS) + endif(MSVC) +else() + include_directories(${PROJECT_SOURCE_DIR}) + + if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + endif(MSVC) endif() +# export targets for find_package config mode +export(TARGETS tinyxml2_static + FILE ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake) + install(TARGETS tinyxml2_static + EXPORT ${CMAKE_PROJECT_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -131,3 +158,14 @@ configure_file( add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + +file(WRITE + ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake + "include(\${CMAKE_CURRENT_LIST_DIR}/${CMAKE_PROJECT_NAME}Targets.cmake)\n") + +install(FILES + ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake + DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}) + +install(EXPORT ${CMAKE_PROJECT_NAME}Targets + DESTINATION lib/cmake/${CMAKE_PROJECT_NAME}) \ No newline at end of file diff --git a/tinyxml2.cpp b/tinyxml2.cpp index ded9d36..0168d9b 100755 --- a/tinyxml2.cpp +++ b/tinyxml2.cpp @@ -809,9 +809,11 @@ void XMLNode::Unlink( XMLNode* child ) if ( child->_prev ) { child->_prev->_next = child->_next; + child->_prev = 0; } if ( child->_next ) { child->_next->_prev = child->_prev; + child->_next = 0; } child->_parent = 0; } @@ -823,6 +825,9 @@ void XMLNode::DeleteChild( XMLNode* node ) TIXMLASSERT( node->_document == _document ); TIXMLASSERT( node->_parent == this ); Unlink( node ); + TIXMLASSERT(node->_prev == 0); + TIXMLASSERT(node->_next == 0); + TIXMLASSERT(node->_parent == 0); DeleteNode( node ); } @@ -1067,11 +1072,16 @@ char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) return 0; } -void XMLNode::DeleteNode( XMLNode* node ) +/*static*/ void XMLNode::DeleteNode( XMLNode* node ) { if ( node == 0 ) { return; } + TIXMLASSERT(node->_document); + if (!node->ToDocument()) { + node->_document->MarkInUse(node); + } + MemPool* pool = node->_memPool; node->~XMLNode(); pool->Free( node ); @@ -1082,10 +1092,13 @@ void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const TIXMLASSERT( insertThis ); TIXMLASSERT( insertThis->_document == _document ); - if ( insertThis->_parent ) + if (insertThis->_parent) { insertThis->_parent->Unlink( insertThis ); - else + } + else { + insertThis->_document->MarkInUse(insertThis); insertThis->_memPool->SetTracked(); + } } const XMLElement* XMLNode::ToElementWithName( const char* name ) const @@ -1991,9 +2004,24 @@ XMLDocument::~XMLDocument() } +void XMLDocument::MarkInUse(XMLNode* node) +{ + TIXMLASSERT(node->_parent == 0); + + for (int i = 0; i < _unlinked.Size(); ++i) { + if (node == _unlinked[i]) { + _unlinked.SwapRemove(i); + break; + } + } +} + void XMLDocument::Clear() { DeleteChildren(); + while( _unlinked.Size()) { + DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. + } #ifdef DEBUG const bool hadError = Error(); diff --git a/tinyxml2.h b/tinyxml2.h index c9b7124..f7fc21f 100755 --- a/tinyxml2.h +++ b/tinyxml2.h @@ -264,6 +264,13 @@ public: return _allocated; } + void SwapRemove(int i) { + TIXMLASSERT(i >= 0); + TIXMLASSERT(i < _size); + _mem[i] = _mem[_size - 1]; + --_size; + } + const T* Mem() const { TIXMLASSERT( _mem ); return _mem; @@ -1826,6 +1833,9 @@ public: // internal char* Identify( char* p, XMLNode** node ); + // internal + void MarkInUse(XMLNode*); + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { return 0; } @@ -1846,6 +1856,13 @@ private: int _errorLineNum; char* _charBuffer; int _parseCurLineNum; + // Memory tracking does add some overhead. + // However, the code assumes that you don't + // have a bunch of unlinked nodes around. + // Therefore it takes less memory to track + // in the document vs. a linked list in the XMLNode, + // and the performance is the same. + DynArray _unlinked; MemPoolT< sizeof(XMLElement) > _elementPool; MemPoolT< sizeof(XMLAttribute) > _attributePool; @@ -1868,6 +1885,8 @@ inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& poo NodeType* returnNode = new (pool.Alloc()) NodeType( this ); TIXMLASSERT( returnNode ); returnNode->_memPool = &pool; + + _unlinked.Push(returnNode); return returnNode; } @@ -2201,6 +2220,7 @@ public: void ClearBuffer() { _buffer.Clear(); _buffer.Push(0); + _firstElement = true; } protected: diff --git a/xmltest.cpp b/xmltest.cpp index ee8592a..18d09ad 100644 --- a/xmltest.cpp +++ b/xmltest.cpp @@ -1721,6 +1721,35 @@ int main( int argc, const char ** argv ) } } + { + // Evil memory leaks. + // If an XMLElement (etc) is allocated via NewElement() (etc.) + // and NOT added to the XMLDocument, what happens? + // + // Previously (buggy): + // The memory would be free'd when the XMLDocument is + // destructed. But the destructor wasn't called, so that + // memory allocated by the XMLElement would not be free'd. + // In practice this meant strings allocated by the XMLElement + // would leak. An edge case, but annoying. + // Now: + // The destructor is called. But the list of unlinked nodes + // has to be tracked. This has a minor performance impact + // that can become significant if you have a lot. (But why + // would you do that?) + // The only way to see this bug is in a leak tracker. This + // is compiled in by default on Windows Debug. + { + XMLDocument doc; + doc.NewElement("LEAK 1"); + } + { + XMLDocument doc; + XMLElement* ele = doc.NewElement("LEAK 2"); + doc.DeleteNode(ele); + } + } + // ----------- Line Number Tracking -------------- { struct TestUtil: XMLVisitor