You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.6 KiB
C

// SPDX-License-Identifier: LGPL-3.0-or-later
/**
* \file mem_file.h
*
* Manage raw memory byte buffers as an I/O stream file-like resource.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*
* Memory file resources are regular raw-bytes buffer that may be read,
* written and seeked like regular files, implementing the
* `StmOps` interface. Memory buffer may be managed into a variety of ways
* giving the API some flexibility, the policy is determined by a set of flags,
* specified upon initialization (and possibly changed on the fly).
* When writing to a memory file the buffer will tipically be
* `realloc()`ated as needed, the `granularity` field provides a hint
* to reduce excessive reallocations (buffer shall be `realloc()`ated to
* multiples of such value). Buffer reallocation may be opted out altoghether
* by toggling the `MEM_FILE_NOGROWBIT` flag, which turns any attempt
* of writing more bytes than currently available into a short-write.
* Memory buffers may also be initialized in read-only or write-only mode,
* enabling the API to reliably manage `const` qualified buffers or
* write-only memory zones.
*/
#ifndef DF_MEM_FILE_H_
#define DF_MEM_FILE_H_
#include "stm.h"
/// Default `MemFile` reallocation granularity.
#define MEM_FILE_GRAN (16u * 1024)
/// Allow write operations.
#define MEM_FILE_WRBIT BIT(0)
/// Allow read operations.
#define MEM_FILE_RDBIT BIT(1)
/**
* \brief `MemFile` whose `OWN` flag is set own the memory buffer,
* `free()`ing it upon close and `realloc()`ating it as necessary.
*/
#define MEM_FILE_OWNBIT BIT(2)
/**
* \brief `MemFile` with `NOGROW` flag won't reallocate
* their buffer, consequently short-writes are not treated as errors.
*/
#define MEM_FILE_NOGROWBIT BIT(3)
/**
* \brief A file-like memory buffer.
*
* Provides functionality to operate on a memory chunk in a stream-like fashion,
*/
typedef struct {
char *buf; ///< memory buffer `MemFile` operates on
size_t pos; ///< current position inside `buf`
size_t nbytes; ///< `buf` length, in bytes
size_t cap; ///< `buf` actual capacity, in bytes
size_t gran; ///< reallocation granularity in bytes, must be a power of 2
unsigned flags; ///< operating mode flags
} MemFile;
/// Stream operations on `MemFile`, including `Close()`.
extern const StmOps *const Stm_MemFileOps;
/// Non `Close()`-ing stream operations on `MemFile`.
extern const StmOps *const Stm_NcMemFileOps;
/**
* \brief Perform basic initialization of a `MemFile`, with an empty
* initial buffer.
*
* The resulting memory file resource shall be initialized according to
* function arguments and its buffer shall be `NULL`.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] gran Reallocation granularity in bytes, must be a power of 2
* \param [in] flags Operating mode flags for the newly initialized memory file
*/
void Stm_InitMemFile(MemFile *stm, size_t gran, unsigned flags);
/**
* \brief Initialize `MemFile` from an existing buffer.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] buf Read-only buffer to be used, must hold at least `nbytes` bytes
* \param [in] nbytes `buf` size in bytes
* \param [in] gran Memory file reallocation granularity, in bytes, must be a power of 2
* \param [in] flags Memory file resource operating mode flags
*/
void Stm_MemFileFromBuf(MemFile *stm, void *buf, size_t nbytes, size_t gran, unsigned flags);
/**
* \brief Initialize a `MemFile` for read over an existing memory buffer.
*
* Resulting memory file resource is implicitly initialized as read-only,
* its buffer won't grow, won't be reallocated, nor it shall be `free()`-d upon
* close.
*
* \param [out] stm Pointer to a memory file, must not be `NULL`
* \param [in] buf Read-only buffer to be used, must hold at least `nbytes` bytes
* \param [in] nbytes `buf` size in bytes
*
* \note No check is made to ensure the provided buffer is NUL-terminated,
* the resulting `MemFile` is initialized with the provided buffer
* as-is. If such characteristic is important, it is the caller's
* responsibility to ensure that.
*/
void Stm_RoMemFileFromBuf(MemFile *, const void *, size_t);
/**
* \brief Take ownership of the buffer managed by a `MemFile`.
*
* \return A pointer to the managed buffer, whose ownership is transferred
* to the caller, thus the caller shall be responsible for its
* deallocation (if necessary).
*
* \note It is not always necessary to call `free()` on the returned buffer,
* since memory files may operate even on static or stack-allocated
* buffers (see `Stm_MemFileFromBuf()` for example).
*/
FORCE_INLINE char *Stm_TakeMemFileBuf(MemFile *stm)
{
char *buf = stm->buf;
stm->flags &= ~MEM_FILE_OWNBIT;
stm->buf = NULL;
stm->nbytes = stm->cap = 0;
return buf;
}
/**
* \brief Read `nbytes` bytes from `stm` into `buf`.
*
* \return Number of bytes actually read from `stm`, -1 on error.
*/
Sint64 Stm_MemFileRead(MemFile *stm, void *buf, size_t nbytes);
/**
* \brief Write `nbytes` bytes from `buf` into `stm`.
*
* \return Number of bytes actually written to `stm`, -1 on error.
*/
Sint64 Stm_MemFileWrite(MemFile *stm, const void *buf, size_t nbytes);
/**
* \brief Set `stm` file pointer, offsetting it of `pos` bytes from `whence`
*
* \return New file cursor position, -1 on error.
*/
Sint64 Stm_MemFileSeek(MemFile *stm, Sint64 pos, SeekMode whence);
/// Close `stm`, `free()`-ing buffer if necessary.
void Stm_MemFileClose(MemFile *stm);
#endif