From 386869df4486193676ddce4884413757d1dffc77 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Mon, 9 Oct 2023 22:41:07 -0700 Subject: [PATCH] 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. --- Arena.cc | 5 +- Arena.h | 35 +++++++- Buffer.cc | 122 +++++++++++++++++++------- Buffer.h | 233 +++++++++++++++++++++++++++++++++++++++++++------- CMakeDocs.txt | 4 +- Test.cc | 4 +- Test.h | 2 +- bufferTest.cc | 29 +++---- 8 files changed, 344 insertions(+), 90 deletions(-) diff --git a/Arena.cc b/Arena.cc index 5f18269..4910b43 100644 --- a/Arena.cc +++ b/Arena.cc @@ -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()) { @@ -328,4 +329,4 @@ Arena::Write(const char *path) } -} // namespace klib \ No newline at end of file +} // namespace klib diff --git a/Arena.h b/Arena.h index c682ac9..eeabbd3 100644 --- a/Arena.h +++ b/Arena.h @@ -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 +/// +/// 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(); diff --git a/Buffer.cc b/Buffer.cc index df40e00..64fafe0 100644 --- a/Buffer.cc +++ b/Buffer.cc @@ -1,6 +1,24 @@ -// -// Created by kyle on 2023-10-09. -// +/// +/// \file Buffer.cc +/// \author K. Isom +/// \date 2023-10-09 +/// +/// \section COPYRIGHT +/// Copyright 2023 K. Isom +/// +/// 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 #include @@ -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; } @@ -223,13 +255,13 @@ void Buffer::HexDump(std::ostream &os) { #ifndef NDEBUG - size_t index = 0; + size_t index = 0; os << std::hex; os << std::setfill('0'); for (index = 0; index < this->length; index++) { - bool eol = (index % 16) == 0; + bool eol = (index % 16) == 0; if (eol && (index > 0)) { os << std::endl; } @@ -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); - - return this->Trim() != 0; + 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(buf.Contents()); + return os; +} + + } // namespace klib diff --git a/Buffer.h b/Buffer.h index 397226a..37ceb25 100644 --- a/Buffer.h +++ b/Buffer.h @@ -1,6 +1,30 @@ -// -// Created by kyle on 2023-10-09. -// +/// +/// \file Buffer.hcc +/// \author K. Isom +/// \date 2023-10-09 +/// \brief Buffer implements basic line buffers. +/// +/// \section COPYRIGHT +/// Copyright 2023 K. Isom +/// +/// 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); - bool Append(const char *s); - bool Append(uint8_t *data, size_t datalen); - bool Append(uint8_t c); + /// A Buffer can be initialized with a starting C-style string. + explicit Buffer(const char *s); - 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); + /// A Buffer can be initialized with a starting string. + explicit Buffer(const std::string s); - bool Remove(size_t index, size_t count); - bool Remove(size_t index); // remove single char + ~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); + + /// 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); + + /// 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 */ - void Resize(size_t newCapacity); - size_t Trim(); - void Clear(); - void Reclaim(); - void HexDump(std::ostream &os); + /// 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); - uint8_t &operator[](size_t index); private: - size_t mustGrow(size_t delta) const; - bool shiftRight(size_t offset, size_t delta); - bool shiftLeft(size_t offset, size_t delta); + size_t mustGrow(size_t delta) const; - uint8_t *contents; - size_t length; - size_t capacity; + 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 diff --git a/CMakeDocs.txt b/CMakeDocs.txt index a20bd27..d8f0912 100644 --- a/CMakeDocs.txt +++ b/CMakeDocs.txt @@ -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 () \ No newline at end of file diff --git a/Test.cc b/Test.cc index ba442ba..46a9d5f 100644 --- a/Test.cc +++ b/Test.cc @@ -33,10 +33,10 @@ AssertionFailed::AssertionFailed(std::string message) : msg(message) } char * -AssertionFailed::what() const noexcept +AssertionFailed::what() { return const_cast(this->msg.c_str()); } -} // namespace klib \ No newline at end of file +} // namespace klib diff --git a/Test.h b/Test.h index cec9ee5..7ca9799 100644 --- a/Test.h +++ b/Test.h @@ -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; diff --git a/bufferTest.cc b/bufferTest.cc index 1efc418..b1f153d 100644 --- a/bufferTest.cc +++ b/bufferTest.cc @@ -1,6 +1,8 @@ +#include #include #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; }