#include "GapBuffer.h" #include "System.h" #include GapBuffer::GapBuffer() { int num_pages = (64u * 1024u + System::page_size - 1u) >> System::page_size_log; m_buffer = (uint8_t *)System::alloc_pages(num_pages); m_buffer_size = System::page_size; m_size = 0u; m_gap_position = 0u; } GapBuffer::GapBuffer(uint8_t * buffer, size_t buffer_size, size_t size) { m_buffer = buffer; m_buffer_size = buffer_size; m_size = size; m_gap_position = size; } GapBuffer::~GapBuffer() { System::free_pages(m_buffer, m_buffer_size >> System::page_size_log); } void GapBuffer::insert(size_t position, const uint8_t * data, size_t length) { ensure_free(length); move_gap(position); memcpy(&m_buffer[m_gap_position], data, length); m_gap_position += length; m_size += length; } void GapBuffer::erase(size_t position, size_t length) { if ((position < m_size) && ((position + length) <= m_size)) { if ((position + length) == m_gap_position) { m_gap_position -= length; m_size -= length; } else { move_gap(position); m_size -= length; } } } void GapBuffer::clear() { m_size = 0u; m_gap_position = 0u; } #ifdef ENABLE_TESTING std::string GapBuffer::get_string() { compact(); return std::string((char *)m_buffer, m_size); } void GapBuffer::set_string(const std::string & s) { clear(); insert(0u, (const uint8_t *)s.c_str(), s.size()); } #endif void GapBuffer::ensure_free(size_t length) { if (gap_size() < length) { /* We're out of space. Allocate more and move data. */ size_t new_size = (m_buffer_size + (128u * 1024u)) & System::page_base_mask; size_t new_num_pages = new_size >> System::page_size_log; uint8_t * new_buffer = (uint8_t *)System::alloc_pages(new_num_pages); memcpy(new_buffer, m_buffer, m_gap_position); memcpy(&new_buffer[m_gap_position], &m_buffer[m_gap_position + gap_size()], m_size - m_gap_position); /* Free the old buffer */ System::free_pages(m_buffer, m_buffer_size >> System::page_size_log); m_buffer = new_buffer; m_buffer_size = new_size; m_gap_position = m_size; } } void GapBuffer::move_gap(size_t position) { if (position != m_gap_position) { if (position < m_gap_position) { memmove(&m_buffer[position + gap_size()], &m_buffer[position], m_gap_position - position); } else { memmove(&m_buffer[m_gap_position], &m_buffer[m_gap_position + gap_size()], position - m_gap_position); } m_gap_position = position; } } void GapBuffer::copy_to(size_t source_position, size_t length, GapBuffer & other, size_t destination_position) { if ((source_position < m_size) && (length > 0u) && ((source_position + length) <= m_size)) { if ((m_gap_position <= source_position) || (m_gap_position >= (source_position + length))) { /* The gap is before or after the range of data to copy. */ other.insert(destination_position, address(source_position), length); } else { /* The gap is in the middle of the range of data to copy. */ size_t pre_gap_size = m_gap_position - source_position; other.insert(destination_position, address(source_position), pre_gap_size); other.insert(destination_position + pre_gap_size, address(source_position + pre_gap_size), length - pre_gap_size); } } }