inline stringMemory::stringMemory() : m_head(neoMalloc(kMemorySize)) { memset(m_head, 0, kMemorySize); m_head->setSize(kMemorySize); m_head->setFree(true); m_tail = nextRegion(m_head); }
// Given a region of free memory and a size, split it in half repeatedly until the // desired size is reached and return a pointer to that new free region. inline stringMemory::region *stringMemory::divideRegion(region *reg, size_t size) { assert(reg); while (reg->size() > size) { const size_t regionSize = reg->size() / 2; reg->setSize(regionSize); reg = nextRegion(reg); reg->setSize(regionSize); reg->setFree(true); } return reg; }
// Single level merge of adjacent free blocks bool stringMemory::mergeFree() { assert(m_head); region *reg = m_head; region *buddy = nextRegion(reg); bool modified = false; while (reg < m_tail && buddy < m_tail) { // Both region and buddy are free if (reg->free() && buddy->free() && reg->size() == buddy->size()) { reg->resize(); reg = nextRegion(buddy); if (reg < m_tail) buddy = nextRegion(reg); modified = true; } else if (reg->size() > buddy->size()) { // Buddy has been split up into smaller chunks reg = buddy; buddy = nextRegion(buddy); } else { // Jump ahead two regions reg = nextRegion(buddy); if (reg < m_tail) buddy = nextRegion(reg); } } return modified; }
inline stringMemory::~stringMemory() { #if !defined(NDEBUG) // walk the list to see if we leaked any memory bool leaks = false; for (region *reg = m_head; reg != m_tail; reg = nextRegion(reg)) { if (U_LIKELY(reg->free())) continue; leaks = true; break; } if (leaks) print(); #endif neoFree(m_head); }
// Finds a free block that matches the size passed in. // If it cannot find one of that size, but there is a larger free block, then // it divides the larger block into smaller ones. If there is no larger block // then it returns nullptr. // // Merges adjacent free blocks as it searches the list. stringMemory::region *stringMemory::findAvailable(size_t size) { U_ASSERT(m_head); region *reg = m_head; region *buddy = nextRegion(reg); region *closest = nullptr; if (buddy == m_tail && reg->free()) return divideRegion(reg, size); // Find minimum-sized match within the area of memory available size_t closestSize = 0; while (reg < m_tail && buddy < m_tail) { // When both the region and buddy are free if (reg->free() && buddy->free() && reg->size() == buddy->size()) { reg->resize(); const size_t regSize = reg->size(); if (size <= regSize && (!closest || regSize <= closest->size())) closest = reg; reg = nextRegion(buddy); if (reg < m_tail) buddy = nextRegion(reg); } else { if (closest) closestSize = closest->size(); const size_t regSize = reg->size(); if (reg->free() && size <= regSize && (!closest || regSize <= closestSize)) { closest = reg; closestSize = regSize; } const size_t buddySize = buddy->size(); if (buddy->free() && size <= buddySize && (!closest || buddySize <= closestSize)) { closest = buddy; closestSize = buddySize; } // Buddy has been split up into smaller chunks if (regSize > buddySize) { reg = buddy; buddy = nextRegion(buddy); } else { // Jump ahead two regions reg = nextRegion(buddy); if (reg < m_tail) buddy = nextRegion(reg); } } } if (closest) { if (closestSize == size) return closest; return divideRegion(closest, size); } return nullptr; }
void stringMemory::print() { auto escapeString = [](const char *str) { size_t length = strlen(str); // The longest possible sequence would be a string with "\x[0-F][0-F]" // for all, which is 4x as large as the input sequence, we also need + 1 // for null terminator. u::unique_ptr<char> data(new char[length * 4 + 1]); char *put = data.get(); for (const char *s = str; *s; s++) { unsigned char ch = *s; if (ch >= ' ' && ch <= '~' && ch != '\\' && ch != '"') *put++ = ch; else { *put++ = '\\'; switch (ch) { case '"': *put++ = '"'; break; case '\\': *put++ = '\\'; break; case '\t': *put++ = 't'; break; case '\r': *put++ = 'r'; break; case '\n': *put++ = 'n'; break; default: *put++ = '\\'; *put++ = 'x'; *put++ = "0123456789ABCDEF"[ch >> 4]; *put++ = "0123456789ABCDEF"[ch & 0xF]; break; } } } *put++ = '\0'; return data; }; for (region *reg = m_head; reg < m_tail; reg = nextRegion(reg)) { if (reg->free()) { printf("Free (%p) [ size: %" PRIu32 " ]\n", (void *)reg, reg->size()); } else { auto escape = escapeString((const char *)(reg + 1)); printf("Used (%p) [ size: %" PRIu32 " contents: \"%.50s...\" ]\n", (void *)reg, reg->size(), escape.get() ); } } }