From 2db942f47ddc0cac0ca3f70b14e8715f72de40f5 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Tue, 10 Nov 2020 19:56:58 -0500 Subject: [PATCH] Replace GapBuffer with TextBuffer. --- src/jes/core/gapbuffer.d | 229 -------------------------------------- src/jes/core/textbuffer.d | 168 ++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 229 deletions(-) delete mode 100644 src/jes/core/gapbuffer.d create mode 100644 src/jes/core/textbuffer.d diff --git a/src/jes/core/gapbuffer.d b/src/jes/core/gapbuffer.d deleted file mode 100644 index 1497df0..0000000 --- a/src/jes/core/gapbuffer.d +++ /dev/null @@ -1,229 +0,0 @@ -module jes.core.gapbuffer; - -import core.stdc.string; - -/** - * A GapBuffer object maintains a buffer in memory with a gap in the middle - * where quick insertions can be done. - * - * The GapBuffer grows automatically as data is added. - */ -class GapBuffer -{ - /** Number of bytes to initially allocate for an empty gap buffer. */ - enum size_t INIT_ALLOC = 8u; - - /** Number of extra bytes to allocate when growing. */ - enum size_t EXTRA_ALLOC = 2000u; - - /** Size of data. */ - private size_t m_size; - - /** Data. */ - private ubyte[] m_data; - - /** Gap position. */ - private size_t m_gap_position; - - /** - * Construct a GapBuffer object that is initially empty. - * - * @param alloc Number of bytes to initially allocate. - */ - this(size_t alloc = INIT_ALLOC) - { - m_data = new ubyte[alloc]; - } - - /** - * Construct a GapBuffer object using the dynamic array passed in. - * - * @param data Initial data to use to populate the gap buffer. - */ - this(ubyte[] data) - { - m_data = data; - m_size = data.length; - m_gap_position = m_size; - } - - /** - * Get the address of a byte in the gap buffer. - * - * @param offset Offset of data in the gap buffer. - */ - ubyte * address(size_t offset) - { - if (offset < m_gap_position) - { - return &m_data[offset]; - } - else - { - return &m_data[offset + gap_size()]; - } - } - - /** - * Move the gap to the end of the buffer so that all data is congruent. - */ - void compact() - { - move_gap(m_size); - } - - /** - * Ensure that there are at least length bytes free. - * - * @param length The number of bytes that must be free. - */ - private void ensure_free(size_t length) - { - if (gap_size() < length) - { - m_data.length = m_size + length + EXTRA_ALLOC; - } - } - - /** - * Erase data from the gap buffer. - * - * @param position The index of the data to remove. - * @param length The length of the data to remove. - */ - void erase(size_t position, size_t length) - { - if ((position < m_size) && ((position + length) <= m_size)) - { - move_gap(position); - m_size -= length; - } - } - - /** - * Calculate the size of the gap in the gap buffer. - */ - private size_t gap_size() const - { - return m_data.length - m_size; - } - - /** - * Insert data into the gap buffer. - * - * @param position The index at which to insert the data. - * @param data The data to insert. - * @param length The length of the data to insert. - */ - void insert(size_t position, const ubyte * data, size_t length) - { - if (position <= m_size) - { - ensure_free(length); - move_gap(position); - memcpy(&m_data[position], data, length); - m_gap_position += length; - m_size += length; - } - } - - /** - * Move the gap in the gap buffer to the requested position. - * - * @param position The buffer index to move the gap to. - */ - private void move_gap(size_t position) - { - if (position < m_gap_position) - { - memmove(&m_data[position + gap_size()], &m_data[position], m_gap_position - position); - m_gap_position = position; - } - else if (position > m_gap_position) - { - memmove(&m_data[m_gap_position], &m_data[m_gap_position + gap_size()], position - m_gap_position); - m_gap_position = position; - } - } - - /** - * Get the size of the data in the gap buffer. - */ - size_t size() const - { - return m_size; - } - - version(unittest) invariant - { - assert(m_data !is null); - assert(m_size <= m_data.length); - assert(m_gap_position <= m_size); - } - - unittest - { - GapBuffer gb = new GapBuffer(); - assert(gb.size() == 0u); - ubyte[20] dat = 5u; - gb.insert(0u, dat.ptr, dat.length); - assert(gb.size() == 20u); - for (size_t i = 0u; i < 20u; i++) - { - assert(*gb.address(i) == 5u); - } - dat[] = 10u; - gb.insert(5u, dat.ptr, dat.length); - assert(gb.size() == 40u); - for (size_t i = 0u; i < 40u; i++) - { - if (i < 5u) - { - assert(*gb.address(i) == 5u); - } - else if (i < 25u) - { - assert(*gb.address(i) == 10u); - } - else - { - assert(*gb.address(i) == 5u); - } - } - gb.erase(15u, 15u); - assert(gb.size() == 25u); - for (size_t i = 0u; i < 25u; i++) - { - if (i < 5u) - { - assert(*gb.address(i) == 5u); - } - else if (i < 15u) - { - assert(*gb.address(i) == 10u); - } - else - { - assert(*gb.address(i) == 5u); - } - } - assert((gb.address(24u) - gb.address(0u)) > 24u); - gb.compact(); - assert((gb.address(24u) - gb.address(0u)) == 24u); - gb.insert(30u, dat.ptr, dat.length); - assert(gb.size() == 25u); - } - - unittest - { - ubyte[10] dat = 22u; - GapBuffer gb = new GapBuffer(dat); - assert(gb.size() == 10u); - assert(*gb.address(5u) == 22u); - ubyte[5] newdat = 11u; - gb.insert(0u, newdat.ptr, newdat.length); - assert(gb.size() == 15u); - assert(*gb.address(3u) == 11u); - assert(*gb.address(10u) == 22u); - } -} diff --git a/src/jes/core/textbuffer.d b/src/jes/core/textbuffer.d new file mode 100644 index 0000000..b278669 --- /dev/null +++ b/src/jes/core/textbuffer.d @@ -0,0 +1,168 @@ +module jes.core.textbuffer; + +import core.stdc.string; +import core.stdc.stdlib; + +class TextBuffer +{ + /** Number of bytes to initially allocate for an empty text buffer. */ + enum size_t INITIAL_CAPACITY = 8u; + + /** Number of extra bytes to allocate when growing. */ + enum size_t GROWTH_CAPACITY = 2000u; + + /** Size of data. */ + private size_t m_size; + + /** Underlying capacity in memory. */ + private size_t m_capacity; + + /** Data. */ + private ubyte * m_data; + + /** + * Construct a TextBuffer object that is initially empty. + * + * @param capacity The number of bytes to initially allocate. + */ + this(size_t capacity = INITIAL_CAPACITY) + { + m_data = cast(ubyte *)malloc(capacity); + } + + /** + * Destructor for a TextBuffer object. + */ + ~this() + { + free(m_data); + } + + /** + * Get the address of a byte in the text buffer. + * + * @param offset Byte offset into the text buffer. + */ + ubyte * address(size_t offset) + { + return &m_data[offset]; + } + + /** + * Erase data from the text buffer. + * + * @param position The index of the data to remove. + * @param n The number of bytes to remove. + */ + void erase(size_t position, size_t n) + { + if ((position < m_size) && (position + n <= m_size)) + { + memmove(&m_data[position], &m_data[position + n], m_size - (position + n)); + m_size -= n; + } + } + + /** + * Insert data into the text buffer. + * + * @param position The index at which to insert the data. + * @param data The data to insert. + * @param n The number of bytes to insert. + */ + void insert(size_t position, const ubyte * data, size_t n) + { + if (position <= m_size) + { + ensure_free(n); + memmove(&m_data[position + n], &m_data[position], m_size - position); + memcpy(&m_data[position], data, n); + m_size += n; + } + } + + /** + * Get the size of the data in the text buffer. + */ + @property size_t size() const + { + return m_size; + } + + /** + * Ensure that there are at least n free. + * + * @param n The number of bytes that must be free. + */ + private void ensure_free(size_t n) + { + if ((m_capacity - m_size) < n) + { + /* Grow the text buffer. */ + m_capacity = m_size + n + GROWTH_CAPACITY; + ubyte * new_data = cast(ubyte *)malloc(m_capacity); + memcpy(new_data, m_data, m_size); + free(m_data); + m_data = new_data; + } + } + + version(unittest) invariant + { + assert(m_data !is null); + assert(m_size <= m_capacity); + } + + unittest + { + TextBuffer tb = new TextBuffer(); + assert(tb.size == 0u); + ubyte[20] dat = 5u; + tb.insert(0u, dat.ptr, dat.length); + ubyte * first_address = tb.address(5u); + assert(tb.size() == 20u); + for (size_t i = 0u; i < 20u; i++) + { + assert(*tb.address(i) == 5u); + } + dat[] = 10u; + tb.insert(5u, dat.ptr, dat.length); + assert(tb.size() == 40u); + for (size_t i = 0u; i < 40u; i++) + { + if (i < 5u) + { + assert(*tb.address(i) == 5u); + } + else if (i < 25u) + { + assert(*tb.address(i) == 10u); + } + else + { + assert(*tb.address(i) == 5u); + } + } + tb.erase(15u, 15u); + assert(tb.size() == 25u); + for (size_t i = 0u; i < 25u; i++) + { + if (i < 5u) + { + assert(*tb.address(i) == 5u); + } + else if (i < 15u) + { + assert(*tb.address(i) == 10u); + } + else + { + assert(*tb.address(i) == 5u); + } + } + assert((tb.address(24u) - tb.address(0u)) == 24u); + tb.insert(30u, dat.ptr, dat.length); + assert(tb.size == 25u); + assert(first_address == tb.address(5u)); + } +}