#pragma once #ifndef IWEXFILE_H #define IWEXFILE_H /************************************************************************************************** * IOWOW library * * MIT License * * Copyright (c) 2012-2020 Softmotions Ltd * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *************************************************************************************************/ /** @file * @brief Auto-expandable file. * @author Anton Adamansky (adamansky@softmotions.com) * * @note Before using API of this module you should call * * `iw_init(void)` iowow module initialization routine. * * Features: * - Tuneable file expansion policies. * Custom file resize policies supported by specifying * `IWFS_EXT_OPTS::rspolicy` option value. * The following policies are implemented: * - Exact. File resizing fits exactly to the size required by `write` * operation. This is the default behaviour. * - Fibonacci policy. Next file size computed accourding to * fibonacci sequence of previous file sizes: `file_size(n+1) = MAX(file_size(n) + file_size(n-1), nsize)` * - Multiplication resize policy. Next file size: `file_size(n+1) = N * file_size(n)` where * `N` is a rational number `IW_RNUM` greater than `1` * - Read/write locking over a file's address space in multithreaded * environment. * - File shrinking/truncation support. * - A number mmaped regions can be registered in the file's address space. * These regions used in read/write operation and automatically maintained * during file resize operations. * * File operations implemented as function pointers contained in `IWFS_EXT` `C` * structure. * The `iwrc iwfs_exfile_open(IWFS_EXT *f, const IWFS_EXT_OPTS *opts);` opens * file and initializes a given `IWFS_EXT` structure. */ #include "iwfile.h" IW_EXTERN_C_START struct IWFS_EXT_OPTS; struct IWFS_EXT; /** * @enum iwfs_ext_ecode * @brief Error codes specific to this module. */ typedef enum { _IWFS_EXT_ERROR_START = (IW_ERROR_START + 3000UL), IWFS_ERROR_MMAP_OVERLAP, /**< Region is mmaped already, mmaping overlaps */ IWFS_ERROR_NOT_MMAPED, /**< Region is not mmaped */ IWFS_ERROR_RESIZE_POLICY_FAIL, /**< Invalid result of resize policy function.*/ IWFS_ERROR_MAXOFF, /**< Maximum file offset reached. */ _IWFS_EXT_ERROR_END } iwfs_ext_ecode; typedef uint8_t iwfs_ext_mmap_opts_t; /** Use shared mmaping synchronized with file data */ #define IWFS_MMAP_SHARED ((iwfs_ext_mmap_opts_t) 0x00U) /** Use private mmap */ #define IWFS_MMAP_PRIVATE ((iwfs_ext_mmap_opts_t) 0x01U) /** * @brief File resize policy function type. * * This function called in the following cases: * - When a file needs to be resized. Returned new file size cannot * be lesser than requested @a nsize and must be `page aligned`. * - When a file is closed. In this case the first argument @a nsize * will be set to `-1` and function should return `0`. * This call can be used in order to release resources allocated for @a ctx * private data used in function. * * @param nsize Desired file size. * @param csize Current file size. * @param f File reference. * @param ctx Function context data pointer. A function is allowed to initialize * this pointer by own private data stucture. * * @return Computed new file size. */ typedef off_t (*IW_EXT_RSPOLICY)(off_t nsize, off_t csize, struct IWFS_EXT *f, void **ctx); /** * @brief Fibonacci resize file policy. * * New `file_size(n+1) = MAX(file_size(n) + file_size(n-1), nsize)` */ IW_EXPORT off_t iw_exfile_szpolicy_fibo(off_t nsize, off_t csize, struct IWFS_EXT *f, void **ctx); /** * @brief Rational number `IW_RNUM` file size multiplication policy. * * New `file_size = MAX(file_size * (N/D), nsize)` */ IW_EXPORT off_t iw_exfile_szpolicy_mul(off_t nsize, off_t csize, struct IWFS_EXT *f, void **ctx); /** * @brief `IWFS_EXT` file options. * @see iwrc iwfs_exfile_open(IWFS_EXT *f, const IWFS_EXT_OPTS *opts) */ typedef struct IWFS_EXT_OPTS { IWFS_FILE_OPTS file; /**< Underlying file options */ off_t initial_size; /**< Initial file size */ bool use_locks; /**< If `true` file operations will be guarded by rw lock. Default: `false` */ IW_EXT_RSPOLICY rspolicy; /**< File resize policy function ptr. Default: `exact size policy` */ void *rspolicy_ctx; /**< Custom opaque data for policy functions. Default: `0` */ uint64_t maxoff; /**< Maximum allowed file offset. Unlimited if zero. If maximum offset is reached `IWFS_ERROR_MAXOFF` will be reported. */ } IWFS_EXT_OPTS; /** * @struct IWFS_EXT_STATE * @brief `IWFS_EXT` file state info. * @see IWFS_EXT::state */ typedef struct IWFS_EXT_STATE { IWFS_FILE_STATE file; /**< Simple file state */ off_t fsize; /**< Current file size */ } IWFS_EXT_STATE; /** * @brief Auto-expandable file. */ typedef struct IWFS_EXT { struct IWFS_EXT_IMPL *impl; /** * @brief Ensures that a file's physical address space contains a given offset * @a off * * Various algorithms can be used for new space allocation, * see the features section for further explanation. * * @param f `IWFS_EXT` * @param off File offset what have to be within physically allocated file * address space. * @return `0` on success or error code. * * @see off_t iw_exfile_szpolicy_fibo(off_t nsize, off_t csize, struct * IWFS_EXT *f, void **ctx) * @see off_t iw_exfile_szpolicy_mul(off_t nsize, off_t csize, struct IWFS_EXT * *f, void **ctx) */ iwrc(*ensure_size)(struct IWFS_EXT *f, off_t off); /** * @brief Set the end of this file to the specified offset @a off exactly. */ iwrc(*truncate)(struct IWFS_EXT *f, off_t off); iwrc(*truncate_unsafe)(struct IWFS_EXT *f, off_t off); /** * @brief Register an address space specified by @a off and @a len as memory * mmaped region * within this file. * * It is not required for this region be physically represented in the file's * address space. * As soon as this region will be used for reading/writing it will be mmaped * and direct mmaped memory access will be used for IO in this area. * * For example: * @code {.c} * f.add_mmap(&f, 10, 20); * f.read(&f, 5, buf, 10, sp); // read [5-15) bytes * // [5-10) bytes will be read using system `pread` * // [10-15) bytes will be retrieved by direct `memcpy` from mmapped * region * @endcode * * Pointer to this region can be retrieved by `IWFS_EXT::acquire_mmap` * * @param f `IWFS_EXT` * @param off Offset of mmaped region * @param len Length of mmaped region * @return `0` on success or error code. */ iwrc(*add_mmap)(struct IWFS_EXT *f, off_t off, size_t len, iwfs_ext_mmap_opts_t opts); iwrc(*add_mmap_unsafe)(struct IWFS_EXT *f, off_t off, size_t len, iwfs_ext_mmap_opts_t opts); /** * @brief Retrieve mmaped region by its offset @a off and keep file as read locked. * * If region was not mmaped previously with IWFS_EXT::add_mmap * the `IWFS_ERROR_NOT_MMAPED` error code will be returned. * * WARNING: Internal read lock will be acquired and * must be released by subsequent `release_mmap()` call * after all activity with mmaped region has finished. * * @param f `IWFS_EXT` * @param off Region start offset * @param [out] mm Pointer assigned to start of mmaped region of `NULL` if * error occurred. * @param [out] sp Length of region * @return `0` on success or error code. */ iwrc(*acquire_mmap)(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp); /** * @brief Retrieve mmaped region by its offset @a off */ iwrc(*probe_mmap)(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp); iwrc(*probe_mmap_unsafe)(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp); /** * @brief Release the lock acquired by successfull call of `acquire_mmap()` */ iwrc(*release_mmap)(struct IWFS_EXT *f); /** * @brief Unmap mmaped region identified by @a off * * The `IWFS_ERROR_NOT_MMAPED` will returned * if region was not previously mapped with IWFS_EXT::add_mmap * * @param f `IWFS_EXT` * @param off Region start offset * @return `0` on success or error code. */ iwrc(*remove_mmap)(struct IWFS_EXT *f, off_t off); iwrc(*remove_mmap_unsafe)(struct IWFS_EXT *f, off_t off); /** * @brief Synchronize a file with a mmaped region identified by @a off offset. * * The `IWFS_ERROR_NOT_MMAPED` will returned * if region was not previously mapped with IWFS_EXT::add_mmap * * @param f `IWFS_EXT` * @param off Region start offset * @param flags Sync flags. * @return `0` on success or error code. */ iwrc(*sync_mmap)(struct IWFS_EXT *f, off_t off, iwfs_sync_flags flags); iwrc(*sync_mmap_unsafe)(struct IWFS_EXT *f, off_t off, iwfs_sync_flags flags); /** * @brief Remap all mmaped regions. * * @param f `IWFS_EXT` */ iwrc(*remap_all)(struct IWFS_EXT *f); /* See iwfile.h */ /** @see IWFS_FILE::write */ iwrc(*write)(struct IWFS_EXT *f, off_t off, const void *buf, size_t siz, size_t *sp); /** @see IWFS_FILE::read */ iwrc(*read)(struct IWFS_EXT *f, off_t off, void *buf, size_t siz, size_t *sp); /** @see IWFS_FILE::close */ iwrc(*close)(struct IWFS_EXT *f); /** @see IWFS_FILE::sync */ iwrc(*sync)(struct IWFS_EXT *f, iwfs_sync_flags flags); /** @see IWFS_FILE::state */ iwrc(*state)(struct IWFS_EXT *f, IWFS_EXT_STATE *state); /** @see IWFS_FILE::copy */ iwrc(*copy)(struct IWFS_EXT *f, off_t off, size_t siz, off_t noff); } IWFS_EXT; /** * @brief Open exfile. * * Example: * * Open a file for multithreaded env with fibonacci file resize policy and * initial size to 4K * * @code {.c} * IWFS_EXT_OPTS opts = { * .file = { * .path = "myfile.dat", * .omode = IWFS_OWRITE | IWFS_OCREATE, * .lock_mode = IWP_WLOCK * }, * .initial_size = 4096, * .use_locks = true, * .rspolicy = iw_exfile_szpolicy_fibo * }; * IWFS_EXT f; * iwrc rc = iwfs_exfile_open(&f, &opts); * * rc = f.write(&f, ...); * ... * @endcode * * * @param f Exfile handle. Simple memory placeholder. * @param opts File open options. Initialized file options. * @return Error code of `0` on success. * @relatesalso IWFS_EXT */ IW_EXPORT WUR iwrc iwfs_exfile_open(IWFS_EXT *f, const IWFS_EXT_OPTS *opts); /** * @brief Init `iwexfile` submodule. */ IW_EXPORT WUR iwrc iwfs_exfile_init(void); IW_EXTERN_C_END #endif