130 lines
3.6 KiB
C++
130 lines
3.6 KiB
C++
#include "GapBuffer.h"
|
|
#include "System.h"
|
|
#include <string.h>
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|