From bc5901dd28b9a277d20c640bed519457c67beb5c Mon Sep 17 00:00:00 2001 From: "arseny.kapoulkine" Date: Sun, 29 Aug 2010 15:19:22 +0000 Subject: strcpy_insitu improvements: empty string forces deallocation, memory is reclaimed if waste is too great (small string is copied to the large buffer) git-svn-id: http://pugixml.googlecode.com/svn/trunk@654 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index c9b0046..c6f74e7 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1327,12 +1327,40 @@ namespace } #endif + inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target) + { + assert(target); + size_t target_length = impl::strlen(target); + + // always reuse document buffer memory if possible + if (!allocated) return target_length >= length; + + // reuse heap memory if waste is not too great + const size_t reuse_threshold = 32; + + return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); + } + bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) { size_t source_length = impl::strlen(source); - if (dest && impl::strlen(dest) >= source_length) + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; + + if (header & header_mask) alloc->deallocate_string(dest); + + // mark the string as not allocated + dest = 0; + header &= ~header_mask; + + return true; + } + else if (dest && strcpy_insitu_allow(source_length, header & header_mask, dest)) { + // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, (source_length + 1) * sizeof(char_t)); return true; @@ -1341,13 +1369,17 @@ namespace { xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; + // allocate new buffer char_t* buf = alloc->allocate_string(source_length + 1); if (!buf) return false; + // copy the string (including zero terminator) memcpy(buf, source, (source_length + 1) * sizeof(char_t)); + // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); + // the string is now allocated, so set the flag dest = buf; header |= header_mask; -- cgit v1.2.3