/// /// \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 #include #include #include 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 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(); /// 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(); /// 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; } /// 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@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