finish doxygen docs.
This also includes a lot of code cleanups along the way.
This commit is contained in:
parent
386869df44
commit
a8b09001f7
|
@ -0,0 +1,33 @@
|
|||
Checks: >-
|
||||
bugprone-*,
|
||||
cppcoreguidelines-*,
|
||||
google-*,
|
||||
misc-*,
|
||||
modernize-*,
|
||||
performance-*,
|
||||
readability-*,
|
||||
-bugprone-lambda-function-name,
|
||||
-bugprone-reserved-identifier,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-google-readability-braces-around-statements,
|
||||
-google-readability-function-size,
|
||||
-misc-no-recursion,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-trailing-return-type,
|
||||
-performance-unnecessary-value-param,
|
||||
-readability-magic-numbers,
|
||||
|
||||
CheckOptions:
|
||||
- key: readability-function-cognitive-complexity.Threshold
|
||||
value: 100
|
||||
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||
value: true
|
||||
# Set naming conventions for your style below (there are dozens of naming settings possible):
|
||||
# See https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
|
@ -1,10 +1,12 @@
|
|||
.idea
|
||||
.trunk
|
||||
.vc
|
||||
.vscode
|
||||
|
||||
*.a
|
||||
*.o
|
||||
*.bin
|
||||
*.pc
|
||||
build
|
||||
core
|
||||
core.*
|
||||
|
|
49
Arena.cc
49
Arena.cc
|
@ -26,7 +26,7 @@ namespace klib {
|
|||
|
||||
|
||||
Arena::Arena()
|
||||
: store(nullptr), size(0), fd(0), arenaType(ARENA_UNINIT)
|
||||
: store(nullptr), size(0), fd(0), arenaType(ArenaType::Uninit)
|
||||
{}
|
||||
|
||||
|
||||
|
@ -39,20 +39,20 @@ Arena::~Arena()
|
|||
void
|
||||
Arena::Initialize()
|
||||
{
|
||||
assert(this->arenaType != ARENA_UNINIT);
|
||||
assert(this->arenaType != ArenaType::Uninit);
|
||||
this->store = nullptr;
|
||||
this->size = 0;
|
||||
this->arenaType = ARENA_UNINIT;
|
||||
this->arenaType = ArenaType::Uninit;
|
||||
this->fd = 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Arena::SetStatic(uint8_t *mem, size_t allocSize)
|
||||
Arena::SetStatic(uint8_t *mem, size_t memSize)
|
||||
{
|
||||
this->store = mem;
|
||||
this->size = allocSize;
|
||||
this->arenaType = ARENA_STATIC;
|
||||
this->size = memSize;
|
||||
this->arenaType = ArenaType::Static;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ Arena::SetAlloc(size_t allocSize)
|
|||
this->Destroy();
|
||||
}
|
||||
|
||||
this->arenaType = ARENA_ALLOC;
|
||||
this->arenaType = ArenaType::Alloc;
|
||||
this->size = allocSize;
|
||||
this->store = new uint8_t[allocSize];
|
||||
if (this->store == nullptr) {
|
||||
|
@ -85,7 +85,7 @@ Arena::MemoryMap(int memFileDes, size_t memSize)
|
|||
this->Destroy();
|
||||
}
|
||||
|
||||
this->arenaType = ARENA_MMAP;
|
||||
this->arenaType = ArenaType::MemoryMapped;
|
||||
this->size = memSize;
|
||||
this->store = (uint8_t *) mmap(NULL, memSize, PROT_RW, MAP_SHARED,
|
||||
memFileDes, 0);
|
||||
|
@ -224,18 +224,18 @@ Arena::Clear()
|
|||
void
|
||||
Arena::Destroy()
|
||||
{
|
||||
if (this->arenaType == ARENA_UNINIT) {
|
||||
if (this->arenaType == ArenaType::Uninit) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this->arenaType) {
|
||||
case ARENA_STATIC:
|
||||
case ArenaType::Static:
|
||||
break;
|
||||
case ARENA_ALLOC:
|
||||
case ArenaType::Alloc:
|
||||
delete this->store;
|
||||
break;
|
||||
#if defined(__linux__)
|
||||
case ARENA_MMAP:
|
||||
case ArenaType::MemoryMapped:
|
||||
if (munmap(this->store, this->size) == -1) {
|
||||
abort();
|
||||
return;
|
||||
|
@ -257,7 +257,7 @@ Arena::Destroy()
|
|||
|
||||
}
|
||||
|
||||
this->arenaType = ARENA_UNINIT;
|
||||
this->arenaType = ArenaType::Uninit;
|
||||
this->size = 0;
|
||||
this->store = nullptr;
|
||||
return;
|
||||
|
@ -266,24 +266,24 @@ Arena::Destroy()
|
|||
std::ostream &
|
||||
operator<<(std::ostream &os, Arena &arena)
|
||||
{
|
||||
auto cursor = arena.Store();
|
||||
auto cursor = arena.NewCursor();
|
||||
char cursorString[33] = {0};
|
||||
snprintf(cursorString, 32, "%#016llx",
|
||||
(long long unsigned int)cursor);
|
||||
|
||||
os << "Arena<";
|
||||
switch (arena.Type()) {
|
||||
case ARENA_UNINIT:
|
||||
case ArenaType::Uninit:
|
||||
os << "uninitialized";
|
||||
break;
|
||||
case ARENA_STATIC:
|
||||
case ArenaType::Static:
|
||||
os << "static";
|
||||
break;
|
||||
case ARENA_ALLOC:
|
||||
case ArenaType::Alloc:
|
||||
os << "allocated";
|
||||
break;
|
||||
#if defined(__linux__)
|
||||
case ARENA_MMAP:
|
||||
case ArenaType::MemoryMapped:
|
||||
os << "mmap/file";
|
||||
break;
|
||||
#endif
|
||||
|
@ -328,5 +328,18 @@ Arena::Write(const char *path)
|
|||
return retc;
|
||||
}
|
||||
|
||||
uint8_t &
|
||||
Arena::operator[](size_t index)
|
||||
{
|
||||
if (index > this->size) {
|
||||
#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT)
|
||||
throw std::range_error("index out of range");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
return this->store[index];
|
||||
}
|
||||
|
||||
|
||||
} // namespace klib
|
||||
|
|
172
Arena.h
172
Arena.h
|
@ -4,24 +4,13 @@
|
|||
/// \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.
|
||||
///
|
||||
/// \section PLATFORM SUPPORT
|
||||
///
|
||||
/// Arena will build on the major platforms, but memory-mapped files are only
|
||||
/// supported on Unix-like systems. File I/O on Windows, for example, reads the
|
||||
/// file into an allocated arena. See Arena::Open for more details.
|
||||
|
||||
|
||||
#ifndef KIMODEM_ARENA_H
|
||||
|
@ -37,71 +26,156 @@
|
|||
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,
|
||||
/// \enum ArenaType
|
||||
///
|
||||
/// ArenaType describes the type of \class Arena.
|
||||
enum class ArenaType : uint8_t {
|
||||
/// Uninit is an unintialized arena.
|
||||
Uninit,
|
||||
/// Static is an arena backed by a static block of memory.
|
||||
Static,
|
||||
/// Alloc is an arena backed by allocated memory.
|
||||
Alloc,
|
||||
/// MemoryMapped is an arena backed by a memory-mapped file.
|
||||
MemoryMapped,
|
||||
};
|
||||
|
||||
|
||||
/// Arena is the class that implements a memory arena.
|
||||
///
|
||||
/// The Arena uses the concept of a cursor to point to memory in the arena. The
|
||||
/// #NewCursor and #End methods return pointers to the start and end of the
|
||||
/// arena memory.
|
||||
///
|
||||
/// The arena should be initalized with one of the Set methods (SetStatic,
|
||||
/// SetAlloc) or one of the file-based options (Create, Open, MemoryMap). At
|
||||
/// this point, no further memory management should be done until the end of the
|
||||
/// arena's life, at which point Destroy should be called.
|
||||
class Arena {
|
||||
public:
|
||||
/// An Arena is initialized with no backing memory.
|
||||
Arena();
|
||||
|
||||
~Arena();
|
||||
|
||||
/*
|
||||
* InitializeArena is intended for use only with systems that
|
||||
* do not initialize new variables to zero. It should be called
|
||||
* exactly once, at the start of the program. Any other time the
|
||||
* arena needs to be reset, it should be called with Clear or
|
||||
* Destroy.
|
||||
*/
|
||||
/// Initialize is intended for use only with systems that do not
|
||||
/// initialize new variables to zero. It should be called exactly once,
|
||||
/// at the start of the program. Any other time the arena needs to be
|
||||
/// reset, it should be called with #Clear or #Destroy.
|
||||
void Initialize();
|
||||
|
||||
int SetStatic(uint8_t *, size_t);
|
||||
/// SetStatic points the arena to a chunk of memory. That memory is
|
||||
/// intended to be statically allocated, e.g. via a global `static
|
||||
/// uint8_t memory[memSize];`. If the arena is already backed, then
|
||||
/// #Destroy will be called first.
|
||||
///
|
||||
/// \param mem A pointer to a section of memory, preferably statically
|
||||
/// allocated.
|
||||
/// \param memSize The size of the memory section.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int SetStatic(uint8_t *mem, size_t memSize);
|
||||
|
||||
/// SetAlloc allocates a chunk of memory for the arena; the arena takes
|
||||
/// ownership. If the arena is already backed, then #Destroy will be
|
||||
/// called first.
|
||||
///
|
||||
/// \param allocSize The size of memory to allocate.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int SetAlloc(size_t allocSize);
|
||||
|
||||
#if defined(__linux__)
|
||||
/// MemoryMap points the arena to a memory-mapped file. This is
|
||||
/// currently only supported on Linux. If the arena is already backed,
|
||||
/// then #Destroy will be called first.
|
||||
///
|
||||
/// \param memFileDes File descriptor to map into memory.
|
||||
/// \param memSize The size of memory to map.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int MemoryMap(int memFileDes, size_t memSize); // Arena will own fd.
|
||||
|
||||
/// Create creates a new file, truncating it if it already exists. On
|
||||
/// Unix-based platforms, the arena will be backed by a memory via
|
||||
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read
|
||||
/// the file into an allocated arena, calling #SetAlloc.
|
||||
///
|
||||
/// \param path The path to the file that should be created.
|
||||
/// \param fileSize The size of the file to create.
|
||||
/// \param mode The permissions to load.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Create(const char *path, size_t fileSize, mode_t mode);
|
||||
|
||||
/// Open reads a file into the arena; the file must already exist. On
|
||||
/// Unix-based platforms, the arena will be backed by a memory via
|
||||
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read
|
||||
/// the file into an allocated arena, calling #SetAlloc. On these
|
||||
/// platforms, in order to persist changes, #Write must be called to
|
||||
/// sync changes to disk.
|
||||
///
|
||||
/// \param path The path to the file to be loaded.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Open(const char *path);
|
||||
#elif defined(__WIN64__) || defined(__WIN32__)
|
||||
int Open(const char *path);
|
||||
#endif
|
||||
|
||||
/// NewCursor returns a pointer to the start of the memory in the arena.
|
||||
///
|
||||
/// \return A pointer to the start of the arena memory.
|
||||
uint8_t *NewCursor() const { return this->store; }
|
||||
|
||||
/// End returns a pointer to the end of the arena memory.
|
||||
///
|
||||
/// \return A pointer to the end of the arena memory.
|
||||
uint8_t *End() { return this->store + this->size; }
|
||||
|
||||
/// CursorInArena checks whether the cursor is still in the arena.
|
||||
///
|
||||
/// \param cursor A pointer that ostensibly points to the arena's
|
||||
/// memory.
|
||||
/// \return True if the cursor is still in the arena.
|
||||
bool CursorInArena(const uint8_t *cursor);
|
||||
|
||||
/// Returns the current size of the arena.
|
||||
///
|
||||
/// \return The size of the arena.
|
||||
size_t Size() const
|
||||
{ return this->size; }
|
||||
|
||||
uint8_t Type() const
|
||||
/// Type returns an ArenaType describing the arena.
|
||||
///
|
||||
/// \return An ArenaType describing the backing memory for the arena.
|
||||
ArenaType Type() const
|
||||
{ return this->arenaType; }
|
||||
|
||||
uintptr_t Store() { return (uintptr_t)this->store; }
|
||||
/// Ready returns whether the arena is initialized.
|
||||
bool Ready() const { return this->Type() != ArenaType::Uninit; };
|
||||
|
||||
bool Ready() const { return this->Type() != ARENA_UNINIT; };
|
||||
/// Clear zeroizes the memory in the arena.
|
||||
void Clear();
|
||||
void Destroy(); /* dispose of any memory used by arena */
|
||||
|
||||
/*
|
||||
* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE
|
||||
* BACKING FILE!
|
||||
*/
|
||||
/// Destroy removes any backing memory (e.g. from SetAlloc or
|
||||
/// MemoryMap). This does not call Clear; if the arena was backed by a
|
||||
/// file that should be persisted, it would wipe out the file.
|
||||
void Destroy();
|
||||
|
||||
/// Write dumps the arena to a file suitable for loading by Open.
|
||||
///
|
||||
/// \warning DANGER: if arena is memory-mapped, DO NOT WRITE TO THE
|
||||
/// BACKING FILE!
|
||||
///
|
||||
/// \param path
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Write(const char *path);
|
||||
|
||||
uint8_t &operator[](size_t index)
|
||||
{ return this->store[index]; }
|
||||
/// This operator allows the data in the arena 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);
|
||||
|
||||
private:
|
||||
uint8_t *store;
|
||||
|
@ -111,6 +185,14 @@ private:
|
|||
};
|
||||
|
||||
|
||||
/// Write an Arena out to the output stream.
|
||||
///
|
||||
/// The resulting output looks something like
|
||||
/// Arena<allocated>@0x7fff91dfad70,store<128B>@0x0055d6c5881ec0.
|
||||
///
|
||||
/// \param os
|
||||
/// \param arena
|
||||
/// \return
|
||||
std::ostream &operator<<(std::ostream& os, Arena &arena);
|
||||
|
||||
|
||||
|
|
25
Buffer.cc
25
Buffer.cc
|
@ -3,22 +3,7 @@
|
|||
/// \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>
|
||||
|
@ -31,7 +16,11 @@
|
|||
namespace klib {
|
||||
|
||||
|
||||
/// The defaultCapacity for a new Buffer is a reasonably arbitrary starting
|
||||
/// point.
|
||||
constexpr size_t defaultCapacity = 32;
|
||||
/// maxReasonableLine is the longest a reasonable line could be. It assumes
|
||||
/// something like a long, unprettified JSON strong or the like.
|
||||
constexpr size_t maxReasonableLine = 8192;
|
||||
|
||||
|
||||
|
@ -323,7 +312,11 @@ uint8_t &
|
|||
Buffer::operator[](size_t index)
|
||||
{
|
||||
if (index > this->length) {
|
||||
#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT)
|
||||
throw std::range_error("array index out of bounds");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
return this->contents[index];
|
||||
}
|
||||
|
|
24
Buffer.h
24
Buffer.h
|
@ -1,26 +1,9 @@
|
|||
///
|
||||
/// \file Buffer.hcc
|
||||
/// \file Buffer.h
|
||||
/// \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.
|
||||
|
@ -221,8 +204,11 @@ private:
|
|||
bool autoTrim;
|
||||
};
|
||||
|
||||
|
||||
/// The << operator is overloaded to write out the contents of the Buffer.
|
||||
std::ostream &operator<<(std::ostream &os, const Buffer &buf);
|
||||
|
||||
/// Two Buffers are not equal if their lengths differ or if their contents
|
||||
/// differ.
|
||||
inline bool operator!=(const Buffer &lhs, const Buffer &rhs) { return !(lhs == rhs); };
|
||||
|
||||
} // namespace klib
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
find_package(Doxygen)
|
||||
|
||||
if (${DOXYGEN_FOUND})
|
||||
set(DOXYGEN_GENERATE_MAN YES)
|
||||
set(DOXYGEN_GENERATE_LATEX YES)
|
||||
#set(DOXYGEN_EXTRACT_ALL YES)
|
||||
|
||||
doxygen_add_docs(klib_docs
|
||||
${HEADER_FILES} ${SOURCE_FILES})
|
||||
|
|
|
@ -1,43 +1,42 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
project(klib LANGUAGES CXX VERSION 0.0.1)
|
||||
project(klib LANGUAGES CXX
|
||||
VERSION 0.0.1
|
||||
DESCRIPTION "Kyle's C++ library")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
|
||||
else()
|
||||
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_compile_options("-stdlib=libc++")
|
||||
else()
|
||||
# nothing special for gcc at the moment
|
||||
endif()
|
||||
endif()
|
||||
if (MSVC)
|
||||
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
|
||||
else ()
|
||||
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
add_compile_options("-stdlib=libc++")
|
||||
else ()
|
||||
# nothing special for gcc at the moment
|
||||
endif ()
|
||||
endif ()
|
||||
add_compile_options("-DDESKTOP_BUILD")
|
||||
|
||||
set(HEADER_FILES
|
||||
Arena.h
|
||||
Buffer.h
|
||||
Dictionary.h
|
||||
Test.h
|
||||
TLV.h)
|
||||
klib.h
|
||||
Arena.h
|
||||
Buffer.h
|
||||
Dictionary.h
|
||||
Test.h
|
||||
TLV.h)
|
||||
|
||||
set(SOURCE_FILES
|
||||
Arena.cc
|
||||
Buffer.cc
|
||||
Dictionary.cc
|
||||
Test.cc
|
||||
TLV.cc)
|
||||
Arena.cc
|
||||
Buffer.cc
|
||||
Dictionary.cc
|
||||
Test.cc
|
||||
TLV.cc)
|
||||
|
||||
add_library(klib STATIC
|
||||
Arena.cc
|
||||
Buffer.cc
|
||||
Dictionary.cc
|
||||
TLV.cc
|
||||
)
|
||||
install(TARGETS klib LIBRARY DESTINATION ${PREFIX}/lib)
|
||||
install(FILES ${HEADER_FILES} DESTINATION include/klib)
|
||||
install(FILES klibConfig.cmake DESTINATION share/klib/cmake)
|
||||
Arena.cc
|
||||
Buffer.cc
|
||||
Dictionary.cc
|
||||
TLV.cc)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
@ -56,10 +55,22 @@ add_test(bufferTest buffer_test)
|
|||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
klibConfig.cmake
|
||||
VERSION ${PACKAGE_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
klibConfig.cmake
|
||||
VERSION ${PACKAGE_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
add_custom_target(cloc
|
||||
COMMAND cloc ${SOURCE_FILES} ${HEADER_FILES}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
configure_file(klib.pc.in klib.pc @ONLY)
|
||||
install(TARGETS klib LIBRARY DESTINATION ${PREFIX}/lib)
|
||||
install(FILES ${HEADER_FILES} DESTINATION include/{klib})
|
||||
install(FILES klibConfig.cmake DESTINATION share/klib/cmake)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/klib.pc DESTINATION lib/pkgconfig)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc/klib)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/man DESTINATION share/man)
|
||||
|
||||
include(CMakePack.txt)
|
||||
include(CMakeDocs.txt)
|
||||
include(CMakeDocs.txt)
|
||||
|
|
|
@ -7,7 +7,6 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
|||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
|
||||
|
||||
# Debian settings
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "K. Isom")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Shimmering Clarity C++ library")
|
||||
|
|
|
@ -6,13 +6,17 @@
|
|||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
bool
|
||||
Dictionary::Lookup(const char *key, uint8_t klen, TLV::Record &res)
|
||||
{
|
||||
res.Tag = this->kTag;
|
||||
uint8_t *cursor = TLV::FindTag(this->arena, NULL, res);
|
||||
uint8_t *cursor = TLV::FindTag(this->arena, nullptr, res);
|
||||
|
||||
while (cursor != NULL) {
|
||||
while (cursor != nullptr) {
|
||||
if ((klen == res.Len) &&
|
||||
(memcmp(res.Val, key, klen) == 0)) {
|
||||
TLV::ReadFromMemory(res, cursor);
|
||||
|
@ -33,11 +37,11 @@ int
|
|||
Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
|
||||
{
|
||||
TLV::Record rec;
|
||||
uint8_t *cursor = NULL;
|
||||
uint8_t *cursor = nullptr;
|
||||
|
||||
SetRecord(rec, this->kTag, klen, key);
|
||||
cursor = this->seek(key, klen);
|
||||
if (cursor != NULL) {
|
||||
if (cursor != nullptr) {
|
||||
TLV::DeleteRecord(this->arena, cursor);
|
||||
TLV::DeleteRecord(this->arena, cursor);
|
||||
}
|
||||
|
@ -46,13 +50,13 @@ Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
|
|||
return -1;
|
||||
}
|
||||
|
||||
cursor = TLV::WriteToMemory(this->arena, NULL, rec);
|
||||
if (cursor == NULL) {
|
||||
cursor = TLV::WriteToMemory(this->arena, nullptr, rec);
|
||||
if (cursor == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SetRecord(rec, this->vTag, vlen, val);
|
||||
if (TLV::WriteToMemory(this->arena, NULL, rec) == NULL) {
|
||||
if (TLV::WriteToMemory(this->arena, nullptr, rec) == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -67,9 +71,9 @@ Dictionary::seek(const char *key, uint8_t klen)
|
|||
TLV::Record rec;
|
||||
|
||||
rec.Tag = this->kTag;
|
||||
uint8_t *cursor = TLV::LocateTag(this->arena, NULL, rec);
|
||||
uint8_t *cursor = TLV::LocateTag(this->arena, nullptr, rec);
|
||||
|
||||
while (cursor != NULL) {
|
||||
while (cursor != nullptr) {
|
||||
if ((klen == rec.Len) && (this->kTag == rec.Tag)) {
|
||||
if (memcmp(rec.Val, key, klen) == 0) {
|
||||
return cursor;
|
||||
|
@ -79,14 +83,14 @@ Dictionary::seek(const char *key, uint8_t klen)
|
|||
cursor = TLV::LocateTag(this->arena, cursor, rec);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Dictionary::Has(const char *key, uint8_t klen)
|
||||
Dictionary::Contains(const char *key, uint8_t klen)
|
||||
{
|
||||
return this->seek(key, klen) != NULL;
|
||||
return this->seek(key, klen) != nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,10 +99,10 @@ Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen)
|
|||
{
|
||||
size_t required = 0;
|
||||
uintptr_t remaining = 0;
|
||||
uint8_t *cursor = NULL;
|
||||
uint8_t *cursor = nullptr;
|
||||
|
||||
cursor = TLV::FindEmpty(this->arena, NULL);
|
||||
if (cursor == NULL) {
|
||||
cursor = TLV::FindEmpty(this->arena, nullptr);
|
||||
if (cursor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -111,41 +115,39 @@ Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen)
|
|||
}
|
||||
|
||||
|
||||
#if defined(DESKTOP_BUILD)
|
||||
void
|
||||
Dictionary::DumpKVPairs()
|
||||
std::ostream &
|
||||
operator<<(std::ostream &os, const Dictionary &dictionary)
|
||||
{
|
||||
uint8_t *cursor = (this->arena).NewCursor();
|
||||
#if defined(DESKTOP_BUILD)
|
||||
uint8_t *cursor = (dictionary.arena).NewCursor();
|
||||
TLV::Record rec;
|
||||
|
||||
TLV::ReadFromMemory(rec, cursor);
|
||||
std::cout << "Dictionary KV pairs" << std::endl;
|
||||
if (rec.Tag == TAG_EMPTY) {
|
||||
std::cout << "\t(NONE)" << std::endl;
|
||||
return;
|
||||
os << "Dictionary KV pairs" << std::endl;
|
||||
if (rec.Tag == TLV::TAG_EMPTY) {
|
||||
os << "\t(NONE)" << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
while ((cursor != NULL) && (rec.Tag != TAG_EMPTY)) {
|
||||
std::cout << "\t" << rec.Val << "->";
|
||||
while ((cursor != nullptr) && (rec.Tag != TLV::TAG_EMPTY)) {
|
||||
os << "\t" << rec.Val << "->";
|
||||
cursor = TLV::SkipRecord(rec, cursor);
|
||||
TLV::ReadFromMemory(rec, cursor);
|
||||
std::cout << rec.Val << std::endl;
|
||||
os << rec.Val << std::endl;
|
||||
cursor = TLV::SkipRecord(rec, cursor);
|
||||
TLV::ReadFromMemory(rec, cursor);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
void
|
||||
Dictionary::DumpKVPairs()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
int
|
||||
Dictionary::DumpToFile(const char *path)
|
||||
{
|
||||
this->arena.Write(path);
|
||||
return this->arena.Write(path);
|
||||
}
|
||||
|
||||
|
||||
} // namespace klib
|
108
Dictionary.h
108
Dictionary.h
|
@ -1,3 +1,10 @@
|
|||
///
|
||||
/// \file klib.h
|
||||
/// \author kyle
|
||||
/// \date 2023-10-06
|
||||
///
|
||||
|
||||
|
||||
#ifndef KLIB_DICTIONARY_H
|
||||
#define KLIB_DICTIONARY_H
|
||||
|
||||
|
@ -6,39 +13,110 @@
|
|||
#include "TLV.h"
|
||||
|
||||
|
||||
#define DICTIONARY_TAG_KEY 1
|
||||
#define DICTIONARY_TAG_VAL 2
|
||||
static constexpr uint8_t DICTIONARY_TAG_KEY = 1;
|
||||
static constexpr uint8_t DICTIONARY_TAG_VAL = 2;
|
||||
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
/*
|
||||
* A Dictionary is a collection of key-value pairs, similar to how
|
||||
* a dictionary is a mapping of names to definitions.
|
||||
*/
|
||||
/// Dictionary implements a key-value store on top of Arena and TLV::Record.
|
||||
///
|
||||
/// phonebook of SSIDs and WPA keys on a microcontroller. This phonebook had to
|
||||
/// be stored in persistent NVRAM storage, preëmpting the use of std::map or
|
||||
/// similar. The hardware in use was also not conducive to more expensive
|
||||
/// options. It was originally named Phonebook until it was adapted to a more
|
||||
/// general-purpose data structure.
|
||||
///
|
||||
/// Keys and vales are stored as sequential pairs of TLV records; they are
|
||||
/// expected to contain string values but this isn't necessarily the case. The
|
||||
/// tag values default to a tag of DICTIONARY_TAG_KEY, and values to a tag of
|
||||
/// DICTIONARY_TAG_VAL.
|
||||
class Dictionary {
|
||||
public:
|
||||
/// A Dictionary can be initialized with just a backing Arena.
|
||||
///
|
||||
/// \param arena The backing arena for the Dictionary.
|
||||
Dictionary(Arena &arena) :
|
||||
arena(arena),
|
||||
kTag(DICTIONARY_TAG_KEY),
|
||||
vTag(DICTIONARY_TAG_VAL) {} ;
|
||||
vTag(DICTIONARY_TAG_VAL)
|
||||
{};
|
||||
|
||||
/// A Dictionary can also be configured with custom key and value types.
|
||||
///
|
||||
/// \param arena The backing arena for the Dictionary.
|
||||
/// \param kt The value to use for key tags.
|
||||
/// \param vt The value to use for val tags.
|
||||
Dictionary(Arena &arena, uint8_t kt, uint8_t vt) :
|
||||
arena(arena),
|
||||
kTag(kt),
|
||||
vTag(vt) {};
|
||||
vTag(vt)
|
||||
{};
|
||||
|
||||
bool Lookup(const char *key, uint8_t klen, TLV::Record &res);
|
||||
int Set(const char *key, uint8_t klen, const char *val,
|
||||
uint8_t vlen);
|
||||
bool Has(const char *key, uint8_t klen);
|
||||
void DumpKVPairs();
|
||||
void DumpToFile(const char *path);
|
||||
/// Lookup checks to see if the Dictionary has a value under key.
|
||||
///
|
||||
/// \param key The key to search for.
|
||||
/// \param klen The length of the key.
|
||||
/// \param res The TLV::Record to store the value in;
|
||||
/// \return True if the key was found, false otherwise.
|
||||
bool Lookup(const char *key, uint8_t klen, TLV::Record &res);
|
||||
|
||||
/// Set adds a pairing for key → value in the Dictionary.
|
||||
///
|
||||
/// If the key is already present in the dictionary, both the
|
||||
/// key and value are deleted, and a new pair is insert.
|
||||
///
|
||||
/// \warning If the key is present, but there isn't enough space to
|
||||
/// store the new Val, the Dictionary will not contain either key
|
||||
/// or value.
|
||||
///
|
||||
///
|
||||
/// \param key The key to associate.
|
||||
/// \param klen The length of the key.
|
||||
/// \param val The value to associate.
|
||||
/// \param vlen The length of the value.
|
||||
/// \return Returns 0 on success and -1 on failure.
|
||||
int Set(const char *key, uint8_t klen, const char *val,
|
||||
uint8_t vlen);
|
||||
|
||||
/// Contains checks the dictionary to see if it contains a given key.
|
||||
///
|
||||
/// \param key The key to look up.
|
||||
/// \param klen The length of the key.
|
||||
/// \return True if the key is in the Dictionary, otherwise false.
|
||||
bool Contains(const char *key, uint8_t klen);
|
||||
|
||||
|
||||
/// DumpToFile is a wrapper aorund a call to Arena::Write on the
|
||||
/// underlying Arena.
|
||||
///
|
||||
/// \param path The path to the dumped file.
|
||||
/// \return 0 on success, -1 on failure.
|
||||
int DumpToFile(const char *path);
|
||||
|
||||
/// operator<< writes the key pairs to the output stream.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
/// \param dictionary The dictionary to write out.
|
||||
/// \return The output stream is returned.
|
||||
friend std::ostream &operator<<(std::ostream &os,
|
||||
const Dictionary &dictionary);
|
||||
private:
|
||||
uint8_t *seek(const char *key, uint8_t klen);
|
||||
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
||||
uint8_t *seek(const char *key, uint8_t klen);
|
||||
|
||||
Arena &arena;
|
||||
uint8_t kTag;
|
||||
uint8_t vTag;
|
||||
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
||||
|
||||
Arena &arena;
|
||||
uint8_t kTag;
|
||||
uint8_t vTag;
|
||||
};
|
||||
|
||||
|
||||
} // namespace klib
|
||||
|
||||
#endif
|
||||
|
|
28
TLV.cc
28
TLV.cc
|
@ -14,7 +14,7 @@ namespace TLV {
|
|||
static bool
|
||||
spaceAvailable(Arena &arena, uint8_t *cursor, uint8_t len)
|
||||
{
|
||||
if (cursor == NULL) {
|
||||
if (cursor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -33,21 +33,25 @@ clearUnused(Record &rec)
|
|||
uint8_t *
|
||||
WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec)
|
||||
{
|
||||
// If cursor is NULL, the user needs us to select an empty
|
||||
// If cursor is nullptr, the user needs us to select an empty
|
||||
// slot for the record. If we can't find one, that's an
|
||||
// error.
|
||||
//
|
||||
// If, however, the user gives us a cursor, we'll trust it
|
||||
// (though spaceAvailable will sanity check that cursor).
|
||||
if (cursor == NULL) {
|
||||
if (cursor == nullptr) {
|
||||
cursor = FindEmpty(arena, cursor);
|
||||
if (cursor == NULL) {
|
||||
return NULL;
|
||||
if (cursor == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arena.CursorInArena(cursor)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!spaceAvailable(arena, cursor, rec.Len)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(cursor, &rec, REC_SIZE(rec));
|
||||
|
@ -98,21 +102,21 @@ LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
|
|||
{
|
||||
uint8_t tag, len;
|
||||
|
||||
if (cursor == NULL) {
|
||||
if (cursor == nullptr) {
|
||||
cursor = arena.NewCursor();
|
||||
}
|
||||
|
||||
while ((tag = cursor[0]) != rec.Tag) {
|
||||
len = cursor[1];
|
||||
if (!spaceAvailable(arena, cursor, len)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
cursor += len;
|
||||
cursor += 2;
|
||||
}
|
||||
|
||||
if (tag != rec.Tag) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (tag != TAG_EMPTY) {
|
||||
|
@ -142,7 +146,11 @@ SkipRecord(Record &rec, uint8_t *cursor)
|
|||
void
|
||||
DeleteRecord(Arena &arena, uint8_t *cursor)
|
||||
{
|
||||
if (cursor == NULL) {
|
||||
if (cursor == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arena.CursorInArena(cursor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
120
TLV.h
120
TLV.h
|
@ -1,35 +1,76 @@
|
|||
///
|
||||
/// \file TLV.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-06
|
||||
/// \brief TLV.h implements basic tag-length-value records.
|
||||
///
|
||||
/// TLV implements tag-length-value (TLV) records. Each record can have
|
||||
/// a maximum length of 253 bytes; each TLV record occupies a fixed 255
|
||||
/// bytes in memory. TLV records don't allocate memory.
|
||||
///
|
||||
/// This system uses an Arena as a backing store.
|
||||
///
|
||||
|
||||
#ifndef KIMODEM_TLV_H
|
||||
#define KIMODEM_TLV_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "Arena.h"
|
||||
|
||||
using namespace klib;
|
||||
|
||||
|
||||
#ifndef TLV_MAX_LEN
|
||||
#define TLV_MAX_LEN 253
|
||||
#endif
|
||||
|
||||
|
||||
#define TAG_EMPTY 0
|
||||
|
||||
|
||||
namespace klib {
|
||||
namespace TLV {
|
||||
|
||||
#ifndef TLV_MAX_LEN
|
||||
static constexpr size_t TLV_MAX_LEN = 253;
|
||||
#endif
|
||||
|
||||
static constexpr uint8_t TAG_EMPTY = 0;
|
||||
|
||||
|
||||
/// Record describes a tag-length-value record.
|
||||
///
|
||||
/// TLV records occupy a fixed size in memory, which can be controlled with the
|
||||
/// TLV_MAX_LEN define. If this isn't defined, it defaults to a size of 253.
|
||||
/// When writen to an Arena, it occupies Len + 2 bytes. The strings
|
||||
/// are not null-terminated in the arena.
|
||||
struct Record {
|
||||
uint8_t Tag;
|
||||
uint8_t Len;
|
||||
char Val[TLV_MAX_LEN];
|
||||
/// A Tag is used to identify the type of this record.
|
||||
uint8_t Tag;
|
||||
/// Len describes the number of bytes stored in #Val.
|
||||
uint8_t Len;
|
||||
/// Val contains the data in the record.
|
||||
uint8_t Val[TLV_MAX_LEN];
|
||||
};
|
||||
|
||||
uint8_t *WriteToMemory(Arena &, uint8_t *, Record &);
|
||||
void ReadFromMemory(Record &, uint8_t *);
|
||||
void SetRecord(Record &, uint8_t, uint8_t, const char *);
|
||||
void DeleteRecord(Arena &, uint8_t *);
|
||||
/// WriteToMemory writes the TLV record into the arena at the location pointed
|
||||
/// to in the arena.
|
||||
///
|
||||
/// \param arena The backing memory store.
|
||||
/// \param cursor Pointer into the arena's memory.
|
||||
/// \param rec A TLV record to be serialized.
|
||||
/// \return A pointer the memory after the record.
|
||||
uint8_t *WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
/// ReadFromMemory reads a record from the memory pointed to by the cursor.
|
||||
///
|
||||
/// \param rec The TLV record to be filled in.
|
||||
/// \param cursor A pointer into an arena's memory store.
|
||||
void ReadFromMemory(Record &rec, uint8_t *cursor);
|
||||
|
||||
/// SetRecord sets a record.
|
||||
///
|
||||
/// \param rec The record to be set.
|
||||
/// \param tag The record's tag.
|
||||
/// \param length The record's length.
|
||||
/// \param data The data to fill the record with.
|
||||
void SetRecord(Record &rec, uint8_t tag, uint8_t length, const char *data);
|
||||
|
||||
/// DeleteRecord removes the record from the arena. All records ahead of this
|
||||
/// record are shifted backwards so that there are no gaps.
|
||||
void DeleteRecord(Arena &arena, uint8_t *cursor);
|
||||
|
||||
/*
|
||||
* returns a pointer to memory where the record was found,
|
||||
|
@ -37,11 +78,48 @@ void DeleteRecord(Arena &, uint8_t *);
|
|||
* FindTag will call LocateTag and then SkipRecord if the
|
||||
* tag was found.
|
||||
*/
|
||||
uint8_t *FindTag(Arena &, uint8_t *, Record &);
|
||||
uint8_t *LocateTag(Arena &, uint8_t *, Record &);
|
||||
/// FindTag finds the next occurrence of the record's tag.
|
||||
///
|
||||
/// The record must have a tag set, which tells FindTag which tag to look for.
|
||||
/// If found, it fills the record. \see LocateTag.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \param rec The record to be filled.
|
||||
/// \return If the tag is found, a cursor pointing to the next record is
|
||||
/// returned; otherwise nullptr is returned.
|
||||
uint8_t *FindTag(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
uint8_t *FindEmpty(Arena &, uint8_t *);
|
||||
uint8_t *SkipRecord(Record &, uint8_t *);
|
||||
/// LocateTag operates similarly to FindTag, but the cursor points to the
|
||||
/// beginning of the found record.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \param rec The record to be filled.
|
||||
/// \return If the tag is found, a cursor pointing to the record is
|
||||
/// returned; otherwise nullptr is returned.
|
||||
uint8_t *LocateTag(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
/// FindEmpty finds a pointer the next available empty space.
|
||||
///
|
||||
/// \return A cursor to the start of empty space in the arena, or nullptr
|
||||
/// if there is no more empty space available.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \return If the arena has space available, a cursor pointing the start
|
||||
/// of empty space; otherwise, nullptr is returned.
|
||||
uint8_t *FindEmpty(Arena &arena, uint8_t *cursor);
|
||||
|
||||
/// SkipRecord skips the cursor to the next record.
|
||||
///
|
||||
/// \param rec The record that should be skipped.
|
||||
/// \param cursor A pointer to the record in the arena.
|
||||
/// \return The pointer to the next record in the arena.
|
||||
uint8_t *SkipRecord(Record &rec, uint8_t *cursor);
|
||||
|
||||
|
||||
} // namespace TLV
|
||||
|
|
2
Test.cc
2
Test.cc
|
@ -15,7 +15,7 @@ namespace klib {
|
|||
void
|
||||
TestAssert(bool condition, std::string message = "Assertion failed.")
|
||||
{
|
||||
#if defined(NDEBUG)
|
||||
#if defined(NDEBUG) || defined(KLIB_NO_ASSERT)
|
||||
if (!condition) {
|
||||
throw AssertionFailed(message);
|
||||
}
|
||||
|
|
34
Test.h
34
Test.h
|
@ -1,27 +1,43 @@
|
|||
//
|
||||
// Created by kyle on 2023-10-09.
|
||||
//
|
||||
|
||||
#include <string>
|
||||
|
||||
///
|
||||
/// \file Test.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-09
|
||||
/// \brief Test.h implements basic testing tools.
|
||||
///
|
||||
#ifndef KLIB_TEST_H
|
||||
#define KLIB_TEST_H
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
void
|
||||
TestAssert(bool condition, std::string message);
|
||||
/// TestAssert is a variant on the assert macro.
|
||||
///
|
||||
/// If NDEBUG is set, TestAssert will throw an exception if condition is false.
|
||||
/// Otherwise, it calls assert after printing the message.
|
||||
///
|
||||
/// In addition to NDEBUG, KLIB_NO_ASSERT will suppress assertions.
|
||||
///
|
||||
/// \throws AssertionFailed
|
||||
///
|
||||
/// \param condition The condition to assert.
|
||||
/// \param message The message that should be displayed if condition is false.
|
||||
inline void TestAssert(bool condition, std::string message);
|
||||
|
||||
|
||||
/// AssertionFailed indicates that some invariant didn't hold.
|
||||
class AssertionFailed : public std::exception {
|
||||
public:
|
||||
/// AssertionFailed is constructed with a message describing what
|
||||
/// failed.
|
||||
explicit AssertionFailed(std::string message);
|
||||
|
||||
/// what returns a message describing the exception.
|
||||
char *what();
|
||||
|
||||
public:
|
||||
private:
|
||||
std::string msg;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Arena.h"
|
||||
#include "Dictionary.h"
|
||||
#include "testFixtures.h"
|
||||
using namespace klib;
|
||||
|
||||
|
||||
constexpr char TEST_KVSTR1[] = "foo";
|
||||
|
@ -56,18 +57,18 @@ main(int argc, const char *argv[])
|
|||
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN3, TEST_KVSTR3);
|
||||
|
||||
Dictionary dict(arena);
|
||||
assert(!dict.Has(TEST_KVSTR2, TEST_KVSTRLEN2));
|
||||
assert(!dict.Contains(TEST_KVSTR2, TEST_KVSTRLEN2));
|
||||
|
||||
assert(testSetKV(dict, TEST_KVSTR1, TEST_KVSTRLEN1, TEST_KVSTR3,
|
||||
TEST_KVSTRLEN3));
|
||||
dict.DumpKVPairs();
|
||||
std::cout << dict;
|
||||
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR3,
|
||||
TEST_KVSTRLEN3));
|
||||
dict.DumpKVPairs();
|
||||
assert(dict.Has(TEST_KVSTR2, TEST_KVSTRLEN2));
|
||||
std::cout << dict;
|
||||
assert(dict.Contains(TEST_KVSTR2, TEST_KVSTRLEN2));
|
||||
assert(testSetKV(dict, TEST_KVSTR4, TEST_KVSTRLEN4, TEST_KVSTR5,
|
||||
TEST_KVSTRLEN5));
|
||||
dict.DumpKVPairs();
|
||||
std::cout << dict;
|
||||
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
|
||||
|
||||
assert(cmpRecord(value, expect));
|
||||
|
@ -75,7 +76,7 @@ main(int argc, const char *argv[])
|
|||
std::cout << "test overwriting key" << std::endl;
|
||||
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR6,
|
||||
TEST_KVSTRLEN6));
|
||||
dict.DumpKVPairs();
|
||||
std::cout << dict;
|
||||
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN6, TEST_KVSTR6);
|
||||
std::cout << "\tlookup" << std::endl;
|
||||
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
|
||||
|
@ -85,7 +86,7 @@ main(int argc, const char *argv[])
|
|||
std::cout << "\tadd new key to dictionary" << std::endl;
|
||||
assert(testSetKV(dict, TEST_KVSTR3, TEST_KVSTRLEN3, TEST_KVSTR5,
|
||||
TEST_KVSTRLEN5));
|
||||
dict.DumpKVPairs();
|
||||
std::cout << dict;
|
||||
|
||||
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN5, TEST_KVSTR5);
|
||||
assert(dict.Lookup(TEST_KVSTR4, TEST_KVSTRLEN4, value));
|
||||
|
@ -100,5 +101,5 @@ main(int argc, const char *argv[])
|
|||
#endif
|
||||
|
||||
arena.Clear();
|
||||
dict.DumpKVPairs();
|
||||
std::cout << dict;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
///
|
||||
/// \file klib.h
|
||||
/// \author kyle
|
||||
/// \created 2023-10-10
|
||||
/// \brief klib is my collection of C++ data structures and code.
|
||||
///
|
||||
/// \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.
|
||||
///
|
||||
/// \mainpage klib documentation
|
||||
/// Hello, world.
|
||||
///
|
||||
/// \section Introduction
|
||||
///
|
||||
/// This is a collection of data structures and subroutines that I find
|
||||
/// useful in building things.
|
||||
|
||||
#ifndef KLIB_KLIB_H
|
||||
#define KLIB_KLIB_H
|
||||
|
||||
|
||||
/// klib is the top-level namespace containing all the code in this library.
|
||||
namespace klib {}
|
||||
|
||||
|
||||
#endif // KLIB_KLIB_H
|
|
@ -0,0 +1,10 @@
|
|||
prefix="@CMAKE_INSTALL_PREFIX@"
|
||||
exec_prefix="${prefix}"
|
||||
libdir="${prefix}/lib"
|
||||
includedir="${prefix}/include"
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: @CMAKE_PROJECT_DESCRIPTION@
|
||||
Version: @PROJECT_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
|
||||
#include <string.h>
|
||||
#include "TLV.h"
|
||||
|
||||
|
||||
#define ARENA_SIZE 128
|
||||
|
@ -20,10 +21,14 @@
|
|||
#define TEST_STR4 "How is a raven like a writing desk?"
|
||||
#define TEST_STRLEN4 35
|
||||
|
||||
|
||||
namespace klib {
|
||||
|
||||
|
||||
static bool
|
||||
cmpRecord(TLV::Record &a, TLV::Record &b)
|
||||
{
|
||||
if (a.Tag != b .Tag) {
|
||||
if (a.Tag != b.Tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -39,4 +44,7 @@ cmpRecord(TLV::Record &a, TLV::Record &b)
|
|||
}
|
||||
|
||||
|
||||
#endif
|
||||
} // namespace klib
|
||||
|
||||
|
||||
#endif // KLIB_TESTFIXTURES_H
|
||||
|
|
Loading…
Reference in New Issue