jes/src-c/core/GapBuffer.cc
2018-07-25 20:47:02 -04:00

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);
}
}
}