Documentation, usability updates, and removing debug code.

+ First pass at documenting the Buffer class.
+ Starting Arena documentation.
+ Various usability updates, such as overloading operators.
+ Remove debug traces.
This commit is contained in:
Kyle Isom 2023-10-09 22:41:07 -07:00
parent 0f1eff514d
commit 386869df44
8 changed files with 344 additions and 90 deletions

View File

@ -268,7 +268,8 @@ operator<<(std::ostream &os, Arena &arena)
{
auto cursor = arena.Store();
char cursorString[33] = {0};
snprintf(cursorString, 32, "%#016llx", cursor);
snprintf(cursorString, 32, "%#016llx",
(long long unsigned int)cursor);
os << "Arena<";
switch (arena.Type()) {

35
Arena.h
View File

@ -1,9 +1,29 @@
/// @file Arena.h
/// @author K. Isom
/// @brief Memory management using an arena.
/// @section DESCRIPTION
///
/// \file Arena.h
/// \author K. Isom
/// \date 2023-10-06
/// \brief Memory management using an arena.
///
/// \section COPYRIGHT
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that the
/// above copyright notice and this permission notice appear in all copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
/// SOFTWARE.
///
/// \section DESCRIPTION
/// Arena defines a memory management backend for pre-allocating memory.
#ifndef KIMODEM_ARENA_H
#define KIMODEM_ARENA_H
@ -17,13 +37,20 @@
namespace klib {
/// \enum ArenaType describes the type of #Arena.
enum ArenaType : uint8_t {
/// ARENA_UNINIT is an unintialized arena.
ARENA_UNINIT,
/// ARENA_STATIC is an arena backed by a static block of memory.
ARENA_STATIC,
/// ARENA_ALLOC is an arena backed by allocated memory.
ARENA_ALLOC,
/// ARENA_MMAP is an arena backed by a memory-mapped file.
ARENA_MMAP,
};
/// Arena is the class that implements a memory arena.
class Arena {
public:
Arena();

116
Buffer.cc
View File

@ -1,6 +1,24 @@
//
// Created by kyle on 2023-10-09.
//
///
/// \file Buffer.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
///
/// \section COPYRIGHT
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that the
/// above copyright notice and this permission notice appear in all copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
/// SOFTWARE.
///
#include <cassert>
#include <cstring>
@ -24,8 +42,6 @@ nearestPower(size_t x)
return 0;
}
std::cout << "x -> ";
x--;
x |= x >> 1;
@ -35,28 +51,26 @@ nearestPower(size_t x)
x |= x >> 16;
x |= x >> 32;
std::cout << x + 1 << std::endl;
return x + 1;
}
Buffer::Buffer()
: contents(nullptr), length(0), capacity(0)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
{
this->Resize(defaultCapacity);
}
Buffer::Buffer(size_t initialCapacity)
: contents(nullptr), length(0), capacity(0)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
{
this->Resize(initialCapacity);
}
Buffer::Buffer(const char *data)
: contents(nullptr), length(0), capacity(0)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
{
size_t datalen = strnlen(data, maxReasonableLine);
@ -64,6 +78,13 @@ Buffer::Buffer(const char *data)
}
Buffer::Buffer(const std::string s)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
{
this->Append(s);
}
bool
Buffer::Append(const char *s)
{
@ -72,8 +93,16 @@ Buffer::Append(const char *s)
return this->Append((uint8_t *) s, slen);
}
bool
Buffer::Append(uint8_t *data, size_t datalen)
Buffer::Append(const std::string s)
{
return this->Append((const uint8_t *) s.c_str(), s.size());
}
bool
Buffer::Append(const uint8_t *data, const size_t datalen)
{
auto resized = false;
auto newCap = this->mustGrow(datalen);
@ -89,15 +118,16 @@ Buffer::Append(uint8_t *data, size_t datalen)
return resized;
}
bool
Buffer::Append(uint8_t c)
Buffer::Append(const uint8_t c)
{
return this->Append(&c, 1);
}
bool
Buffer::Insert(size_t index, const char *s)
Buffer::Insert(const size_t index, const char *s)
{
size_t slen = strnlen(s, maxReasonableLine);
@ -106,7 +136,14 @@ Buffer::Insert(size_t index, const char *s)
bool
Buffer::Insert(size_t index, uint8_t *data, size_t datalen)
Buffer::Insert(const size_t index, const std::string s)
{
return this->Insert(index, (const uint8_t *) s.c_str(), s.size());
}
bool
Buffer::Insert(const size_t index, const uint8_t *data, const size_t datalen)
{
auto resized = this->shiftRight(index, datalen);
@ -115,15 +152,16 @@ Buffer::Insert(size_t index, uint8_t *data, size_t datalen)
return resized;
}
bool
Buffer::Insert(size_t index, uint8_t c)
Buffer::Insert(const size_t index, const uint8_t c)
{
return this->Insert(index, &c, 1);
}
bool
Buffer::Remove(size_t index, size_t count)
Buffer::Remove(const size_t index, const size_t count)
{
auto resized = this->shiftLeft(index, count);
@ -180,6 +218,7 @@ Buffer::Clear()
if (this->length == 0) {
return;
}
memset(this->contents, 0, this->length);
this->length = 0;
}
@ -187,23 +226,16 @@ Buffer::Clear()
void
Buffer::Reclaim()
{
std::cout << "clear" << std::endl;
this->Clear();
std::cout << "nullptr check" << std::endl;
if (this->contents == nullptr) {
std::cout << "assert checks" << std::endl;
assert(this->length == 0);
assert(this->capacity == 0);
return;
}
std::cout << "delete " << this->Capacity() << "B" << std::endl;
this->HexDump(std::cout);
delete this->contents;
std::cout << "reset contents" << std::endl;
this->contents = nullptr;
std::cout << "reset capacity" << std::endl;
this->capacity = 0;
}
@ -240,7 +272,7 @@ Buffer::HexDump(std::ostream &os)
os << std::setw(2);
}
os << (unsigned short)this->contents[index];
os << (unsigned short) this->contents[index];
if ((index % 15) != 0 || (index == 0)) {
os << " ";
@ -268,7 +300,8 @@ Buffer::shiftRight(size_t offset, size_t delta)
if (this->length == 0) return 0;
memmove(this->contents + (offset + delta), this->contents + offset, this->length);
memmove(this->contents + (offset + delta), this->contents + offset,
this->length);
return resized;
}
@ -276,21 +309,44 @@ Buffer::shiftRight(size_t offset, size_t delta)
bool
Buffer::shiftLeft(size_t offset, size_t delta)
{
// for (size_t i = offset; i < this->length; i++) {
// this->contents[i] = this->contents[i+delta];
// }
memmove(this->contents + offset, this->contents + (offset + delta), this->length);
memmove(this->contents + offset, this->contents + (offset + delta),
this->length);
if (this->AutoTrimIsEnabled()) {
return this->Trim() != 0;
}
return false;
}
uint8_t &
Buffer::operator[](size_t index)
{
if (index > this->length) {
throw std::range_error("array index out of bounds");
}
return this->contents[index];
}
bool
operator==(const Buffer &lhs, const Buffer &rhs)
{
if (lhs.length != rhs.length) {
return false;
}
return memcmp(lhs.contents, rhs.contents, rhs.length) == 0;
}
std::ostream &
operator<<(std::ostream &os, const Buffer &buf)
{
// std::string s((const char *)buf.Contents(), buf.Length());
os << const_cast<uint8_t *>(buf.Contents());
return os;
}
} // namespace klib

205
Buffer.h
View File

@ -1,6 +1,30 @@
//
// Created by kyle on 2023-10-09.
//
///
/// \file Buffer.hcc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
/// \brief Buffer implements basic line buffers.
///
/// \section COPYRIGHT
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that the
/// above copyright notice and this permission notice appear in all copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
/// SOFTWARE.
///
/// \section DESCRIPTION
/// Buffer implements a basic uint8_t line buffer that is intended for use in text
/// editing. It allocates memory in powers of two, and will grow or shrink
/// as needed.
///
#ifndef KGE_BUFFER_H
#define KGE_BUFFER_H
@ -11,50 +35,197 @@
namespace klib {
/// Buffer is a basic line buffer.
///
/// The buffer manages its own internal memory, growing and shrinking
/// as needed. Its capacity is separate from its length; the optimal
/// capacity is determined as the nearest power of two that is greater
/// than the length of the buffer. For example, if the buffer has a
/// length of 5 bytes, 8 bytes will be allocated. If the buffer is 9
/// bytes, 16 bytes will be allocated.
///
/// The #Append and #Insert methods will call #Resize as necessary to grow
/// the buffer. Similarly the #Remove methods will call #Trim to reclaim some
/// memory if possible, but only if #AutoTrimIsEnabled (it is by default).
class Buffer {
public:
/// A Buffer can be constructed empty, with no memory allocated (yet).
Buffer();
explicit Buffer(size_t);
explicit Buffer(const char *);
~Buffer() { this->Reclaim(); }
uint8_t *Contents() { return this->contents; }
size_t Length() const { return this->length; };
size_t Capacity() const { return this->capacity; }
/// A Buffer can be constructed with an explicit capacity.
///
/// \param initialCapacity The initial allocation size for the buffer.
explicit Buffer(size_t initialCapacity);
/// A Buffer can be initialized with a starting C-style string.
explicit Buffer(const char *s);
/// A Buffer can be initialized with a starting string.
explicit Buffer(const std::string s);
~Buffer()
{ this->Reclaim(); }
/// Contents returns the Buffer's contents.
uint8_t *Contents() const
{ return this->contents; }
/// Length returns the length of the data currently stored in the
/// buffer.
size_t Length() const
{ return this->length; };
/// Capacity returns the amount of memory allocated to the Buffer.
size_t Capacity() const
{ return this->capacity; }
/// Append copies in a C-style string to the end of the buffer.
///
/// \param s The string to append.
/// \return True if the Buffer was resized.
bool Append(const char *s);
bool Append(uint8_t *data, size_t datalen);
bool Append(uint8_t c);
bool Insert(size_t index, const char *s);
bool Insert(size_t index, uint8_t *data, size_t datalen);
bool Insert(size_t index, uint8_t c);
/// Append copies in a string to the end of the buffer.
///
/// \param s The string to append.
/// \return True if the Buffer was resized.
bool Append(const std::string s);
bool Remove(size_t index, size_t count);
/// Append copies in a byte buffer to the end of the buffer.
///
/// \param data The byte buffer to insert.
/// \param datalen The length of the byte buffer.
/// \return True if the Buffer was resized.
bool Append(const uint8_t *data, const size_t datalen);
/// Append copies a single character to the end of the buffer.
///
/// \param c The character to append.
/// \return True if the Buffer was resized.
bool Append(const uint8_t c);
/// Insert copies a C-style string into the buffer at index.
///
/// \param index The index to insert the string at.
/// \param s The string to insert.
/// \return True if the Buffer was resized.
bool Insert(const size_t index, const char *s);
/// Insert copies a string into the buffer at index.
///
/// \param index The index the string should be inserted at.
/// \param s The string to insert.
/// \return True if the Buffer was resized.
bool Insert(const size_t index, const std::string s);
/// Insert copies a uint8_t buffer into the buffer at index.
///
/// \param index The index to insert the buffer at.
/// \param data The buffer to insert.
/// \param datalen The size of the data buffer.
/// \return True if the Buffer was resized.
bool
Insert(const size_t index, const uint8_t *data, const size_t datalen);
/// Insert copies a character into the buffer at index.
///
/// \param index The index to insert the character at.
/// \param c The character to insert.
/// \return True if the Buffer was resized.
bool Insert(const size_t index, const uint8_t c);
/// Remove removes `count` bytes from the buffer at `index`.
///
/// \param index The starting index to remove bytes from.
/// \param count The number of bytes to remove.
/// \return True if the Buffer was resized.
bool Remove(const size_t index, const size_t count);
/// Remove removes a single byte from the buffer.
///
/// \param index The index pointing to the byte to be removed.
/// \return True if the Buffer was resized.
bool Remove(size_t index); // remove single char
/* memory management */
/// Resize changes the capacity of the buffer to `newCapacity`.
///
/// If newCapacity is less than the length of the Buffer, it
/// will remove enough bytes from the end to make this happen.
///
/// \param newCapacity The new capacity for the Buffer.
void Resize(size_t newCapacity);
/// Trim will resize the Buffer to an appropriate size based on
/// its length.
///
/// \return The new capacity of the Buffer.
size_t Trim();
/// DisableAutoTrim prevents the #Buffer from automatically
/// trimming memory after a call to #Remove.
void DisableAutoTrim()
{ this->autoTrim = false; }
/// EnableAutoTrim enables automatically trimming memory after
/// calls to #Remove.
void EnableAutoTrim()
{ this->autoTrim = true; }
/// AutoTrimIsEnabled returns true if autotrim is enabled.
///
/// \return #Remove will call Trim.
bool AutoTrimIsEnabled()
{ return this->autoTrim; }
/// Clear removes the data stored in the buffer. It will not
/// call #Trim; the capacity of the buffer will not be altered.
void Clear();
/// Reclaim the memory in the buffer: the buffer will call #Clear,
/// followed by deleting any allocated memory.
void Reclaim();
/// HexDump dumps the data in the buffer to the output stream;
/// it is intended as a debugging tool.
///
/// \param os The output stream to write to.
void HexDump(std::ostream &os);
/// This operator allows the data in the buffer to be accessed
/// as if it were an array. If the index is out of bounds, it
/// will throw a range_error.
///
/// \throws std::range_error.
///
/// \param index The index to retrieve.
/// \return
uint8_t &operator[](size_t index);
/// Two buffers are equal if their lengths are the same and
/// their contents are the same. Equality is irrespective of
/// their capacities.
friend bool operator==(const Buffer &lhs, const Buffer &rhs);
private:
size_t mustGrow(size_t delta) const;
bool shiftRight(size_t offset, size_t delta);
bool shiftLeft(size_t offset, size_t delta);
uint8_t *contents;
size_t length;
size_t capacity;
bool autoTrim;
};
std::ostream &operator<<(std::ostream &os, const Buffer &buf);
inline bool operator!=(const Buffer &lhs, const Buffer &rhs) { return !(lhs == rhs); };
} // namespace klib
#endif //KGE_BUFFER_H
#endif // KGE_BUFFER_H

View File

@ -4,7 +4,7 @@ find_package(Doxygen)
if (${DOXYGEN_FOUND})
doxygen_add_docs(klib_docs
${HEADER_FILES} ${SOURCE_FILES}
USE_STAMP_FILE)
${HEADER_FILES} ${SOURCE_FILES})
# USE_STAMP_FILE)
endif ()

View File

@ -33,7 +33,7 @@ AssertionFailed::AssertionFailed(std::string message) : msg(message)
}
char *
AssertionFailed::what() const noexcept
AssertionFailed::what()
{
return const_cast<char *>(this->msg.c_str());
}

2
Test.h
View File

@ -19,7 +19,7 @@ class AssertionFailed : public std::exception {
public:
explicit AssertionFailed(std::string message);
char *what() const noexcept override;
char *what();
public:
std::string msg;

View File

@ -1,6 +1,8 @@
#include <cassert>
#include <iostream>
#include "Buffer.h"
using namespace klib;
int
@ -9,35 +11,26 @@ main(int argc, char *argv[])
(void) argc;
(void) argv;
klib::Buffer buffer("hlo, world");
Buffer buffer("hlo, world");
std::cout << buffer.Contents() << std::endl;
std::cout << buffer << std::endl;
buffer.Insert(1, (uint8_t *) "el", 2);
buffer.Append('!');
std::cout << buffer.Contents() << std::endl;
std::cout << buffer << std::endl;
std::cout << "remove end" << std::endl;
buffer.Remove(buffer.Length() - 1);
std::cout << "remove start" << std::endl;
buffer.Remove(0, 5);
std::cout << "insert char" << std::endl;
buffer.Insert(0, 'g');
std::cout << "insert chunk" << std::endl;
buffer.Insert(1, (uint8_t *) "oodbye", 6);
std::cout << "cruel" << std::endl;
std::cout << buffer << std::endl;
buffer.Insert(9, (uint8_t *)"cruel ", 6);
std::cout << buffer.Contents() << std::endl;
std::cout << buffer << std::endl;
buffer.HexDump(std::cout);
std::cout << "reclaim" << std::endl;
buffer.Reclaim();
std::cout << "append" << std::endl;
buffer.Append("and now for something completely different...");
std::cout << buffer.Contents() << std::endl;
@ -47,6 +40,12 @@ main(int argc, char *argv[])
buffer.Trim();
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << std::endl;
Buffer buffer2("and now for something completely different...");
assert(buffer == buffer2);
buffer2.Remove(buffer2.Length()-3, 3);
std::cout << buffer << std::endl;
assert(buffer != buffer2);
return 0;
}