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
|
.idea
|
||||||
|
.trunk
|
||||||
.vc
|
.vc
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
*.a
|
*.a
|
||||||
*.o
|
*.o
|
||||||
*.bin
|
*.bin
|
||||||
|
*.pc
|
||||||
build
|
build
|
||||||
core
|
core
|
||||||
core.*
|
core.*
|
||||||
|
|
49
Arena.cc
49
Arena.cc
|
@ -26,7 +26,7 @@ namespace klib {
|
||||||
|
|
||||||
|
|
||||||
Arena::Arena()
|
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
|
void
|
||||||
Arena::Initialize()
|
Arena::Initialize()
|
||||||
{
|
{
|
||||||
assert(this->arenaType != ARENA_UNINIT);
|
assert(this->arenaType != ArenaType::Uninit);
|
||||||
this->store = nullptr;
|
this->store = nullptr;
|
||||||
this->size = 0;
|
this->size = 0;
|
||||||
this->arenaType = ARENA_UNINIT;
|
this->arenaType = ArenaType::Uninit;
|
||||||
this->fd = 0;
|
this->fd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
Arena::SetStatic(uint8_t *mem, size_t allocSize)
|
Arena::SetStatic(uint8_t *mem, size_t memSize)
|
||||||
{
|
{
|
||||||
this->store = mem;
|
this->store = mem;
|
||||||
this->size = allocSize;
|
this->size = memSize;
|
||||||
this->arenaType = ARENA_STATIC;
|
this->arenaType = ArenaType::Static;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ Arena::SetAlloc(size_t allocSize)
|
||||||
this->Destroy();
|
this->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->arenaType = ARENA_ALLOC;
|
this->arenaType = ArenaType::Alloc;
|
||||||
this->size = allocSize;
|
this->size = allocSize;
|
||||||
this->store = new uint8_t[allocSize];
|
this->store = new uint8_t[allocSize];
|
||||||
if (this->store == nullptr) {
|
if (this->store == nullptr) {
|
||||||
|
@ -85,7 +85,7 @@ Arena::MemoryMap(int memFileDes, size_t memSize)
|
||||||
this->Destroy();
|
this->Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->arenaType = ARENA_MMAP;
|
this->arenaType = ArenaType::MemoryMapped;
|
||||||
this->size = memSize;
|
this->size = memSize;
|
||||||
this->store = (uint8_t *) mmap(NULL, memSize, PROT_RW, MAP_SHARED,
|
this->store = (uint8_t *) mmap(NULL, memSize, PROT_RW, MAP_SHARED,
|
||||||
memFileDes, 0);
|
memFileDes, 0);
|
||||||
|
@ -224,18 +224,18 @@ Arena::Clear()
|
||||||
void
|
void
|
||||||
Arena::Destroy()
|
Arena::Destroy()
|
||||||
{
|
{
|
||||||
if (this->arenaType == ARENA_UNINIT) {
|
if (this->arenaType == ArenaType::Uninit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this->arenaType) {
|
switch (this->arenaType) {
|
||||||
case ARENA_STATIC:
|
case ArenaType::Static:
|
||||||
break;
|
break;
|
||||||
case ARENA_ALLOC:
|
case ArenaType::Alloc:
|
||||||
delete this->store;
|
delete this->store;
|
||||||
break;
|
break;
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
case ARENA_MMAP:
|
case ArenaType::MemoryMapped:
|
||||||
if (munmap(this->store, this->size) == -1) {
|
if (munmap(this->store, this->size) == -1) {
|
||||||
abort();
|
abort();
|
||||||
return;
|
return;
|
||||||
|
@ -257,7 +257,7 @@ Arena::Destroy()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->arenaType = ARENA_UNINIT;
|
this->arenaType = ArenaType::Uninit;
|
||||||
this->size = 0;
|
this->size = 0;
|
||||||
this->store = nullptr;
|
this->store = nullptr;
|
||||||
return;
|
return;
|
||||||
|
@ -266,24 +266,24 @@ Arena::Destroy()
|
||||||
std::ostream &
|
std::ostream &
|
||||||
operator<<(std::ostream &os, Arena &arena)
|
operator<<(std::ostream &os, Arena &arena)
|
||||||
{
|
{
|
||||||
auto cursor = arena.Store();
|
auto cursor = arena.NewCursor();
|
||||||
char cursorString[33] = {0};
|
char cursorString[33] = {0};
|
||||||
snprintf(cursorString, 32, "%#016llx",
|
snprintf(cursorString, 32, "%#016llx",
|
||||||
(long long unsigned int)cursor);
|
(long long unsigned int)cursor);
|
||||||
|
|
||||||
os << "Arena<";
|
os << "Arena<";
|
||||||
switch (arena.Type()) {
|
switch (arena.Type()) {
|
||||||
case ARENA_UNINIT:
|
case ArenaType::Uninit:
|
||||||
os << "uninitialized";
|
os << "uninitialized";
|
||||||
break;
|
break;
|
||||||
case ARENA_STATIC:
|
case ArenaType::Static:
|
||||||
os << "static";
|
os << "static";
|
||||||
break;
|
break;
|
||||||
case ARENA_ALLOC:
|
case ArenaType::Alloc:
|
||||||
os << "allocated";
|
os << "allocated";
|
||||||
break;
|
break;
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
case ARENA_MMAP:
|
case ArenaType::MemoryMapped:
|
||||||
os << "mmap/file";
|
os << "mmap/file";
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -328,5 +328,18 @@ Arena::Write(const char *path)
|
||||||
return retc;
|
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
|
} // namespace klib
|
||||||
|
|
172
Arena.h
172
Arena.h
|
@ -4,24 +4,13 @@
|
||||||
/// \date 2023-10-06
|
/// \date 2023-10-06
|
||||||
/// \brief Memory management using an arena.
|
/// \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.
|
/// 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
|
#ifndef KIMODEM_ARENA_H
|
||||||
|
@ -37,71 +26,156 @@
|
||||||
namespace klib {
|
namespace klib {
|
||||||
|
|
||||||
|
|
||||||
/// \enum ArenaType describes the type of #Arena.
|
/// \enum ArenaType
|
||||||
enum ArenaType : uint8_t {
|
///
|
||||||
/// ARENA_UNINIT is an unintialized arena.
|
/// ArenaType describes the type of \class Arena.
|
||||||
ARENA_UNINIT,
|
enum class ArenaType : uint8_t {
|
||||||
/// ARENA_STATIC is an arena backed by a static block of memory.
|
/// Uninit is an unintialized arena.
|
||||||
ARENA_STATIC,
|
Uninit,
|
||||||
/// ARENA_ALLOC is an arena backed by allocated memory.
|
/// Static is an arena backed by a static block of memory.
|
||||||
ARENA_ALLOC,
|
Static,
|
||||||
/// ARENA_MMAP is an arena backed by a memory-mapped file.
|
/// Alloc is an arena backed by allocated memory.
|
||||||
ARENA_MMAP,
|
Alloc,
|
||||||
|
/// MemoryMapped is an arena backed by a memory-mapped file.
|
||||||
|
MemoryMapped,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Arena is the class that implements a memory arena.
|
/// 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 {
|
class Arena {
|
||||||
public:
|
public:
|
||||||
|
/// An Arena is initialized with no backing memory.
|
||||||
Arena();
|
Arena();
|
||||||
|
|
||||||
~Arena();
|
~Arena();
|
||||||
|
|
||||||
/*
|
/// Initialize is intended for use only with systems that do not
|
||||||
* InitializeArena is intended for use only with systems that
|
/// initialize new variables to zero. It should be called exactly once,
|
||||||
* do not initialize new variables to zero. It should be called
|
/// at the start of the program. Any other time the arena needs to be
|
||||||
* exactly once, at the start of the program. Any other time the
|
/// reset, it should be called with #Clear or #Destroy.
|
||||||
* arena needs to be reset, it should be called with Clear or
|
|
||||||
* Destroy.
|
|
||||||
*/
|
|
||||||
void Initialize();
|
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);
|
int SetAlloc(size_t allocSize);
|
||||||
|
|
||||||
#if defined(__linux__)
|
#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.
|
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);
|
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);
|
int Open(const char *path);
|
||||||
#elif defined(__WIN64__) || defined(__WIN32__)
|
#elif defined(__WIN64__) || defined(__WIN32__)
|
||||||
int Open(const char *path);
|
int Open(const char *path);
|
||||||
#endif
|
#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; }
|
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; }
|
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);
|
bool CursorInArena(const uint8_t *cursor);
|
||||||
|
|
||||||
|
/// Returns the current size of the arena.
|
||||||
|
///
|
||||||
|
/// \return The size of the arena.
|
||||||
size_t Size() const
|
size_t Size() const
|
||||||
{ return this->size; }
|
{ 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; }
|
{ 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 Clear();
|
||||||
void Destroy(); /* dispose of any memory used by arena */
|
|
||||||
|
|
||||||
/*
|
/// Destroy removes any backing memory (e.g. from SetAlloc or
|
||||||
* DANGER: if arena is file backed (mmap or open), DO NOT WRITE TO THE
|
/// MemoryMap). This does not call Clear; if the arena was backed by a
|
||||||
* BACKING FILE!
|
/// 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);
|
int Write(const char *path);
|
||||||
|
|
||||||
uint8_t &operator[](size_t index)
|
/// This operator allows the data in the arena to be accessed
|
||||||
{ return this->store[index]; }
|
/// 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:
|
private:
|
||||||
uint8_t *store;
|
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);
|
std::ostream &operator<<(std::ostream& os, Arena &arena);
|
||||||
|
|
||||||
|
|
||||||
|
|
25
Buffer.cc
25
Buffer.cc
|
@ -3,22 +3,7 @@
|
||||||
/// \author K. Isom <kyle@imap.cc>
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
/// \date 2023-10-09
|
/// \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 <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -31,7 +16,11 @@
|
||||||
namespace klib {
|
namespace klib {
|
||||||
|
|
||||||
|
|
||||||
|
/// The defaultCapacity for a new Buffer is a reasonably arbitrary starting
|
||||||
|
/// point.
|
||||||
constexpr size_t defaultCapacity = 32;
|
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;
|
constexpr size_t maxReasonableLine = 8192;
|
||||||
|
|
||||||
|
|
||||||
|
@ -323,7 +312,11 @@ uint8_t &
|
||||||
Buffer::operator[](size_t index)
|
Buffer::operator[](size_t index)
|
||||||
{
|
{
|
||||||
if (index > this->length) {
|
if (index > this->length) {
|
||||||
|
#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT)
|
||||||
throw std::range_error("array index out of bounds");
|
throw std::range_error("array index out of bounds");
|
||||||
|
#else
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return this->contents[index];
|
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>
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
/// \date 2023-10-09
|
/// \date 2023-10-09
|
||||||
/// \brief Buffer implements basic line buffers.
|
/// \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
|
/// 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
|
/// editing. It allocates memory in powers of two, and will grow or shrink
|
||||||
/// as needed.
|
/// as needed.
|
||||||
|
@ -221,8 +204,11 @@ private:
|
||||||
bool autoTrim;
|
bool autoTrim;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The << operator is overloaded to write out the contents of the Buffer.
|
||||||
std::ostream &operator<<(std::ostream &os, const Buffer &buf);
|
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); };
|
inline bool operator!=(const Buffer &lhs, const Buffer &rhs) { return !(lhs == rhs); };
|
||||||
|
|
||||||
} // namespace klib
|
} // namespace klib
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
|
|
||||||
if (${DOXYGEN_FOUND})
|
if (${DOXYGEN_FOUND})
|
||||||
|
set(DOXYGEN_GENERATE_MAN YES)
|
||||||
|
set(DOXYGEN_GENERATE_LATEX YES)
|
||||||
|
#set(DOXYGEN_EXTRACT_ALL YES)
|
||||||
|
|
||||||
doxygen_add_docs(klib_docs
|
doxygen_add_docs(klib_docs
|
||||||
${HEADER_FILES} ${SOURCE_FILES})
|
${HEADER_FILES} ${SOURCE_FILES})
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
cmake_minimum_required(VERSION 3.25)
|
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)
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
if(MSVC)
|
if (MSVC)
|
||||||
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
|
add_compile_options("/W4" "$<$<CONFIG:RELEASE>:/O2>")
|
||||||
else()
|
else ()
|
||||||
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
|
add_compile_options("-Wall" "-Wextra" "-Werror" "$<$<CONFIG:RELEASE>:-O3>")
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
add_compile_options("-stdlib=libc++")
|
add_compile_options("-stdlib=libc++")
|
||||||
else()
|
else ()
|
||||||
# nothing special for gcc at the moment
|
# nothing special for gcc at the moment
|
||||||
endif()
|
endif ()
|
||||||
endif()
|
endif ()
|
||||||
add_compile_options("-DDESKTOP_BUILD")
|
add_compile_options("-DDESKTOP_BUILD")
|
||||||
|
|
||||||
set(HEADER_FILES
|
set(HEADER_FILES
|
||||||
|
klib.h
|
||||||
Arena.h
|
Arena.h
|
||||||
Buffer.h
|
Buffer.h
|
||||||
Dictionary.h
|
Dictionary.h
|
||||||
|
@ -33,11 +36,7 @@ add_library(klib STATIC
|
||||||
Arena.cc
|
Arena.cc
|
||||||
Buffer.cc
|
Buffer.cc
|
||||||
Dictionary.cc
|
Dictionary.cc
|
||||||
TLV.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)
|
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
@ -61,5 +60,17 @@ write_basic_package_version_file(
|
||||||
COMPATIBILITY AnyNewerVersion
|
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(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_MINOR ${PROJECT_VERSION_MINOR})
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||||
|
|
||||||
|
|
||||||
# Debian settings
|
# Debian settings
|
||||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "K. Isom")
|
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "K. Isom")
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Shimmering Clarity C++ library")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Shimmering Clarity C++ library")
|
||||||
|
|
|
@ -6,13 +6,17 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace klib {
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Dictionary::Lookup(const char *key, uint8_t klen, TLV::Record &res)
|
Dictionary::Lookup(const char *key, uint8_t klen, TLV::Record &res)
|
||||||
{
|
{
|
||||||
res.Tag = this->kTag;
|
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) &&
|
if ((klen == res.Len) &&
|
||||||
(memcmp(res.Val, key, klen) == 0)) {
|
(memcmp(res.Val, key, klen) == 0)) {
|
||||||
TLV::ReadFromMemory(res, cursor);
|
TLV::ReadFromMemory(res, cursor);
|
||||||
|
@ -33,11 +37,11 @@ int
|
||||||
Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
|
Dictionary::Set(const char *key, uint8_t klen, const char *val, uint8_t vlen)
|
||||||
{
|
{
|
||||||
TLV::Record rec;
|
TLV::Record rec;
|
||||||
uint8_t *cursor = NULL;
|
uint8_t *cursor = nullptr;
|
||||||
|
|
||||||
SetRecord(rec, this->kTag, klen, key);
|
SetRecord(rec, this->kTag, klen, key);
|
||||||
cursor = this->seek(key, klen);
|
cursor = this->seek(key, klen);
|
||||||
if (cursor != NULL) {
|
if (cursor != nullptr) {
|
||||||
TLV::DeleteRecord(this->arena, cursor);
|
TLV::DeleteRecord(this->arena, cursor);
|
||||||
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor = TLV::WriteToMemory(this->arena, NULL, rec);
|
cursor = TLV::WriteToMemory(this->arena, nullptr, rec);
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRecord(rec, this->vTag, vlen, val);
|
SetRecord(rec, this->vTag, vlen, val);
|
||||||
if (TLV::WriteToMemory(this->arena, NULL, rec) == NULL) {
|
if (TLV::WriteToMemory(this->arena, nullptr, rec) == nullptr) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +71,9 @@ Dictionary::seek(const char *key, uint8_t klen)
|
||||||
TLV::Record rec;
|
TLV::Record rec;
|
||||||
|
|
||||||
rec.Tag = this->kTag;
|
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 ((klen == rec.Len) && (this->kTag == rec.Tag)) {
|
||||||
if (memcmp(rec.Val, key, klen) == 0) {
|
if (memcmp(rec.Val, key, klen) == 0) {
|
||||||
return cursor;
|
return cursor;
|
||||||
|
@ -79,14 +83,14 @@ Dictionary::seek(const char *key, uint8_t klen)
|
||||||
cursor = TLV::LocateTag(this->arena, cursor, rec);
|
cursor = TLV::LocateTag(this->arena, cursor, rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
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;
|
size_t required = 0;
|
||||||
uintptr_t remaining = 0;
|
uintptr_t remaining = 0;
|
||||||
uint8_t *cursor = NULL;
|
uint8_t *cursor = nullptr;
|
||||||
|
|
||||||
cursor = TLV::FindEmpty(this->arena, NULL);
|
cursor = TLV::FindEmpty(this->arena, nullptr);
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,41 +115,39 @@ Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(DESKTOP_BUILD)
|
std::ostream &
|
||||||
void
|
operator<<(std::ostream &os, const Dictionary &dictionary)
|
||||||
Dictionary::DumpKVPairs()
|
|
||||||
{
|
{
|
||||||
uint8_t *cursor = (this->arena).NewCursor();
|
#if defined(DESKTOP_BUILD)
|
||||||
|
uint8_t *cursor = (dictionary.arena).NewCursor();
|
||||||
TLV::Record rec;
|
TLV::Record rec;
|
||||||
|
|
||||||
TLV::ReadFromMemory(rec, cursor);
|
TLV::ReadFromMemory(rec, cursor);
|
||||||
std::cout << "Dictionary KV pairs" << std::endl;
|
os << "Dictionary KV pairs" << std::endl;
|
||||||
if (rec.Tag == TAG_EMPTY) {
|
if (rec.Tag == TLV::TAG_EMPTY) {
|
||||||
std::cout << "\t(NONE)" << std::endl;
|
os << "\t(NONE)" << std::endl;
|
||||||
return;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((cursor != NULL) && (rec.Tag != TAG_EMPTY)) {
|
while ((cursor != nullptr) && (rec.Tag != TLV::TAG_EMPTY)) {
|
||||||
std::cout << "\t" << rec.Val << "->";
|
os << "\t" << rec.Val << "->";
|
||||||
cursor = TLV::SkipRecord(rec, cursor);
|
cursor = TLV::SkipRecord(rec, cursor);
|
||||||
TLV::ReadFromMemory(rec, cursor);
|
TLV::ReadFromMemory(rec, cursor);
|
||||||
std::cout << rec.Val << std::endl;
|
os << rec.Val << std::endl;
|
||||||
cursor = TLV::SkipRecord(rec, cursor);
|
cursor = TLV::SkipRecord(rec, cursor);
|
||||||
TLV::ReadFromMemory(rec, cursor);
|
TLV::ReadFromMemory(rec, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void
|
|
||||||
Dictionary::DumpKVPairs()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
|
||||||
|
int
|
||||||
Dictionary::DumpToFile(const char *path)
|
Dictionary::DumpToFile(const char *path)
|
||||||
{
|
{
|
||||||
this->arena.Write(path);
|
return this->arena.Write(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace klib
|
92
Dictionary.h
92
Dictionary.h
|
@ -1,3 +1,10 @@
|
||||||
|
///
|
||||||
|
/// \file klib.h
|
||||||
|
/// \author kyle
|
||||||
|
/// \date 2023-10-06
|
||||||
|
///
|
||||||
|
|
||||||
|
|
||||||
#ifndef KLIB_DICTIONARY_H
|
#ifndef KLIB_DICTIONARY_H
|
||||||
#define KLIB_DICTIONARY_H
|
#define KLIB_DICTIONARY_H
|
||||||
|
|
||||||
|
@ -6,33 +13,102 @@
|
||||||
#include "TLV.h"
|
#include "TLV.h"
|
||||||
|
|
||||||
|
|
||||||
#define DICTIONARY_TAG_KEY 1
|
static constexpr uint8_t DICTIONARY_TAG_KEY = 1;
|
||||||
#define DICTIONARY_TAG_VAL 2
|
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 collection of key-value pairs, similar to how
|
||||||
* a dictionary is a mapping of names to definitions.
|
* 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 {
|
class Dictionary {
|
||||||
public:
|
public:
|
||||||
|
/// A Dictionary can be initialized with just a backing Arena.
|
||||||
|
///
|
||||||
|
/// \param arena The backing arena for the Dictionary.
|
||||||
Dictionary(Arena &arena) :
|
Dictionary(Arena &arena) :
|
||||||
arena(arena),
|
arena(arena),
|
||||||
kTag(DICTIONARY_TAG_KEY),
|
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) :
|
Dictionary(Arena &arena, uint8_t kt, uint8_t vt) :
|
||||||
arena(arena),
|
arena(arena),
|
||||||
kTag(kt),
|
kTag(kt),
|
||||||
vTag(vt) {};
|
vTag(vt)
|
||||||
|
{};
|
||||||
|
|
||||||
|
/// 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);
|
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,
|
int Set(const char *key, uint8_t klen, const char *val,
|
||||||
uint8_t vlen);
|
uint8_t vlen);
|
||||||
bool Has(const char *key, uint8_t klen);
|
|
||||||
void DumpKVPairs();
|
/// Contains checks the dictionary to see if it contains a given key.
|
||||||
void DumpToFile(const char *path);
|
///
|
||||||
|
/// \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:
|
private:
|
||||||
uint8_t *seek(const char *key, uint8_t klen);
|
uint8_t *seek(const char *key, uint8_t klen);
|
||||||
|
|
||||||
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
||||||
|
|
||||||
Arena &arena;
|
Arena &arena;
|
||||||
|
@ -41,4 +117,6 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace klib
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
28
TLV.cc
28
TLV.cc
|
@ -14,7 +14,7 @@ namespace TLV {
|
||||||
static bool
|
static bool
|
||||||
spaceAvailable(Arena &arena, uint8_t *cursor, uint8_t len)
|
spaceAvailable(Arena &arena, uint8_t *cursor, uint8_t len)
|
||||||
{
|
{
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,21 +33,25 @@ clearUnused(Record &rec)
|
||||||
uint8_t *
|
uint8_t *
|
||||||
WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec)
|
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
|
// slot for the record. If we can't find one, that's an
|
||||||
// error.
|
// error.
|
||||||
//
|
//
|
||||||
// If, however, the user gives us a cursor, we'll trust it
|
// If, however, the user gives us a cursor, we'll trust it
|
||||||
// (though spaceAvailable will sanity check that cursor).
|
// (though spaceAvailable will sanity check that cursor).
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
cursor = FindEmpty(arena, cursor);
|
cursor = FindEmpty(arena, cursor);
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!arena.CursorInArena(cursor)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (!spaceAvailable(arena, cursor, rec.Len)) {
|
if (!spaceAvailable(arena, cursor, rec.Len)) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(cursor, &rec, REC_SIZE(rec));
|
memcpy(cursor, &rec, REC_SIZE(rec));
|
||||||
|
@ -98,21 +102,21 @@ LocateTag(Arena &arena, uint8_t *cursor, Record &rec)
|
||||||
{
|
{
|
||||||
uint8_t tag, len;
|
uint8_t tag, len;
|
||||||
|
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
cursor = arena.NewCursor();
|
cursor = arena.NewCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((tag = cursor[0]) != rec.Tag) {
|
while ((tag = cursor[0]) != rec.Tag) {
|
||||||
len = cursor[1];
|
len = cursor[1];
|
||||||
if (!spaceAvailable(arena, cursor, len)) {
|
if (!spaceAvailable(arena, cursor, len)) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
cursor += len;
|
cursor += len;
|
||||||
cursor += 2;
|
cursor += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag != rec.Tag) {
|
if (tag != rec.Tag) {
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag != TAG_EMPTY) {
|
if (tag != TAG_EMPTY) {
|
||||||
|
@ -142,7 +146,11 @@ SkipRecord(Record &rec, uint8_t *cursor)
|
||||||
void
|
void
|
||||||
DeleteRecord(Arena &arena, uint8_t *cursor)
|
DeleteRecord(Arena &arena, uint8_t *cursor)
|
||||||
{
|
{
|
||||||
if (cursor == NULL) {
|
if (cursor == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arena.CursorInArena(cursor)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
116
TLV.h
116
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
|
#ifndef KIMODEM_TLV_H
|
||||||
#define KIMODEM_TLV_H
|
#define KIMODEM_TLV_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "Arena.h"
|
#include "Arena.h"
|
||||||
|
|
||||||
using namespace klib;
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef TLV_MAX_LEN
|
|
||||||
#define TLV_MAX_LEN 253
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define TAG_EMPTY 0
|
|
||||||
|
|
||||||
|
|
||||||
namespace klib {
|
namespace klib {
|
||||||
namespace TLV {
|
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 {
|
struct Record {
|
||||||
|
/// A Tag is used to identify the type of this record.
|
||||||
uint8_t Tag;
|
uint8_t Tag;
|
||||||
|
/// Len describes the number of bytes stored in #Val.
|
||||||
uint8_t Len;
|
uint8_t Len;
|
||||||
char Val[TLV_MAX_LEN];
|
/// Val contains the data in the record.
|
||||||
|
uint8_t Val[TLV_MAX_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t *WriteToMemory(Arena &, uint8_t *, Record &);
|
/// WriteToMemory writes the TLV record into the arena at the location pointed
|
||||||
void ReadFromMemory(Record &, uint8_t *);
|
/// to in the arena.
|
||||||
void SetRecord(Record &, uint8_t, uint8_t, const char *);
|
///
|
||||||
void DeleteRecord(Arena &, uint8_t *);
|
/// \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,
|
* 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
|
* FindTag will call LocateTag and then SkipRecord if the
|
||||||
* tag was found.
|
* tag was found.
|
||||||
*/
|
*/
|
||||||
uint8_t *FindTag(Arena &, uint8_t *, Record &);
|
/// FindTag finds the next occurrence of the record's tag.
|
||||||
uint8_t *LocateTag(Arena &, uint8_t *, Record &);
|
///
|
||||||
|
/// 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 *);
|
/// LocateTag operates similarly to FindTag, but the cursor points to the
|
||||||
uint8_t *SkipRecord(Record &, uint8_t *);
|
/// 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
|
} // namespace TLV
|
||||||
|
|
2
Test.cc
2
Test.cc
|
@ -15,7 +15,7 @@ namespace klib {
|
||||||
void
|
void
|
||||||
TestAssert(bool condition, std::string message = "Assertion failed.")
|
TestAssert(bool condition, std::string message = "Assertion failed.")
|
||||||
{
|
{
|
||||||
#if defined(NDEBUG)
|
#if defined(NDEBUG) || defined(KLIB_NO_ASSERT)
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
throw AssertionFailed(message);
|
throw AssertionFailed(message);
|
||||||
}
|
}
|
||||||
|
|
34
Test.h
34
Test.h
|
@ -1,27 +1,43 @@
|
||||||
//
|
///
|
||||||
// Created by kyle on 2023-10-09.
|
/// \file Test.h
|
||||||
//
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-09
|
||||||
#include <string>
|
/// \brief Test.h implements basic testing tools.
|
||||||
|
///
|
||||||
#ifndef KLIB_TEST_H
|
#ifndef KLIB_TEST_H
|
||||||
#define KLIB_TEST_H
|
#define KLIB_TEST_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
namespace klib {
|
namespace klib {
|
||||||
|
|
||||||
|
|
||||||
void
|
/// TestAssert is a variant on the assert macro.
|
||||||
TestAssert(bool condition, std::string message);
|
///
|
||||||
|
/// 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 {
|
class AssertionFailed : public std::exception {
|
||||||
public:
|
public:
|
||||||
|
/// AssertionFailed is constructed with a message describing what
|
||||||
|
/// failed.
|
||||||
explicit AssertionFailed(std::string message);
|
explicit AssertionFailed(std::string message);
|
||||||
|
|
||||||
|
/// what returns a message describing the exception.
|
||||||
char *what();
|
char *what();
|
||||||
|
|
||||||
public:
|
private:
|
||||||
std::string msg;
|
std::string msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Arena.h"
|
#include "Arena.h"
|
||||||
#include "Dictionary.h"
|
#include "Dictionary.h"
|
||||||
#include "testFixtures.h"
|
#include "testFixtures.h"
|
||||||
|
using namespace klib;
|
||||||
|
|
||||||
|
|
||||||
constexpr char TEST_KVSTR1[] = "foo";
|
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);
|
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN3, TEST_KVSTR3);
|
||||||
|
|
||||||
Dictionary dict(arena);
|
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,
|
assert(testSetKV(dict, TEST_KVSTR1, TEST_KVSTRLEN1, TEST_KVSTR3,
|
||||||
TEST_KVSTRLEN3));
|
TEST_KVSTRLEN3));
|
||||||
dict.DumpKVPairs();
|
std::cout << dict;
|
||||||
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR3,
|
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR3,
|
||||||
TEST_KVSTRLEN3));
|
TEST_KVSTRLEN3));
|
||||||
dict.DumpKVPairs();
|
std::cout << dict;
|
||||||
assert(dict.Has(TEST_KVSTR2, TEST_KVSTRLEN2));
|
assert(dict.Contains(TEST_KVSTR2, TEST_KVSTRLEN2));
|
||||||
assert(testSetKV(dict, TEST_KVSTR4, TEST_KVSTRLEN4, TEST_KVSTR5,
|
assert(testSetKV(dict, TEST_KVSTR4, TEST_KVSTRLEN4, TEST_KVSTR5,
|
||||||
TEST_KVSTRLEN5));
|
TEST_KVSTRLEN5));
|
||||||
dict.DumpKVPairs();
|
std::cout << dict;
|
||||||
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
|
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
|
||||||
|
|
||||||
assert(cmpRecord(value, expect));
|
assert(cmpRecord(value, expect));
|
||||||
|
@ -75,7 +76,7 @@ main(int argc, const char *argv[])
|
||||||
std::cout << "test overwriting key" << std::endl;
|
std::cout << "test overwriting key" << std::endl;
|
||||||
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR6,
|
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR6,
|
||||||
TEST_KVSTRLEN6));
|
TEST_KVSTRLEN6));
|
||||||
dict.DumpKVPairs();
|
std::cout << dict;
|
||||||
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN6, TEST_KVSTR6);
|
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN6, TEST_KVSTR6);
|
||||||
std::cout << "\tlookup" << std::endl;
|
std::cout << "\tlookup" << std::endl;
|
||||||
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
|
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;
|
std::cout << "\tadd new key to dictionary" << std::endl;
|
||||||
assert(testSetKV(dict, TEST_KVSTR3, TEST_KVSTRLEN3, TEST_KVSTR5,
|
assert(testSetKV(dict, TEST_KVSTR3, TEST_KVSTRLEN3, TEST_KVSTR5,
|
||||||
TEST_KVSTRLEN5));
|
TEST_KVSTRLEN5));
|
||||||
dict.DumpKVPairs();
|
std::cout << dict;
|
||||||
|
|
||||||
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN5, TEST_KVSTR5);
|
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN5, TEST_KVSTR5);
|
||||||
assert(dict.Lookup(TEST_KVSTR4, TEST_KVSTRLEN4, value));
|
assert(dict.Lookup(TEST_KVSTR4, TEST_KVSTRLEN4, value));
|
||||||
|
@ -100,5 +101,5 @@ main(int argc, const char *argv[])
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
arena.Clear();
|
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 <string.h>
|
||||||
|
#include "TLV.h"
|
||||||
|
|
||||||
|
|
||||||
#define ARENA_SIZE 128
|
#define ARENA_SIZE 128
|
||||||
|
@ -20,10 +21,14 @@
|
||||||
#define TEST_STR4 "How is a raven like a writing desk?"
|
#define TEST_STR4 "How is a raven like a writing desk?"
|
||||||
#define TEST_STRLEN4 35
|
#define TEST_STRLEN4 35
|
||||||
|
|
||||||
|
|
||||||
|
namespace klib {
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
cmpRecord(TLV::Record &a, TLV::Record &b)
|
cmpRecord(TLV::Record &a, TLV::Record &b)
|
||||||
{
|
{
|
||||||
if (a.Tag != b .Tag) {
|
if (a.Tag != b.Tag) {
|
||||||
return false;
|
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