223 lines
6.6 KiB
C++
223 lines
6.6 KiB
C++
///
|
|
/// \file Arena.h
|
|
/// \author K. Isom
|
|
/// \date 2023-10-06
|
|
/// \brief Memory management using an arena.
|
|
///
|
|
/// 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
|
|
#define KIMODEM_ARENA_H
|
|
|
|
|
|
#include <iostream>
|
|
#include <sys/stat.h>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
#include "Exceptions.h"
|
|
|
|
|
|
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
|
|
|
#include <Windows.h>
|
|
#include <fileapi.h>
|
|
|
|
#endif
|
|
|
|
|
|
namespace klib {
|
|
|
|
|
|
/// \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 initialized 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();
|
|
|
|
/// 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);
|
|
|
|
|
|
/// 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.
|
|
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
|
|
int MemoryMap(int memFileDes, size_t memSize);
|
|
#else
|
|
|
|
int MemoryMap(int memFileDes, size_t memSize)
|
|
{ (void)memFileDes; (void)memSize; throw NotImplemented("WIN32"); }
|
|
|
|
#endif
|
|
/// 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.
|
|
/// \return Returns 0 on success and -1 on error.
|
|
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
|
|
int Create(const char *path, size_t fileSize);
|
|
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
|
int Create(const char *path, size_t fileSize);
|
|
|
|
#endif
|
|
|
|
/// 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);
|
|
|
|
/// 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; }
|
|
|
|
/// Type returns an ArenaType describing the arena.
|
|
///
|
|
/// \return An ArenaType describing the backing memory for the arena.
|
|
ArenaType Type() const
|
|
{ return this->arenaType; }
|
|
|
|
/// Ready returns whether the arena is initialized.
|
|
bool Ready() const
|
|
{ return this->Type() != ArenaType::Uninit; };
|
|
|
|
/// Clear zeroizes the memory in the arena.
|
|
void Clear();
|
|
|
|
/// 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);
|
|
|
|
/// 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;
|
|
size_t size;
|
|
int fd;
|
|
ArenaType arenaType;
|
|
};
|
|
|
|
|
|
/// Write an Arena out to the output stream.
|
|
///
|
|
/// The resulting output looks something like
|
|
/// ```
|
|
/// Arena<allocated>@0x7fff91dfad70,store<128B>@0x0055d6c5881ec0.
|
|
/// ^ ^ ^ ^
|
|
/// | +- base memory | +- store memory
|
|
/// +- arena type +- arena size
|
|
/// ```
|
|
///
|
|
/// \param os
|
|
/// \param arena
|
|
/// \return
|
|
std::ostream &operator<<(std::ostream &os, Arena &arena);
|
|
|
|
|
|
} // namespace klib
|
|
|
|
|
|
#endif
|