#pragma once #ifndef IWFSMFILE_H #define IWFSMFILE_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 with support of reader/writer address space locking and free space block management using bitmaps. * @author Anton Adamansky (adamansky@softmotions.com) * * @note Before using API of this module you should call * `iw_init(void)` iowow module initialization routine. * * Features: * * - Address blocks allocation and deallocation using bitmaps. * - Read/write file address space locking. * - Tunable file expansion policies. * - Read/write methods locking option 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_FSM` `C` structure. * The `iwfs_fsmfile_open(IWFS_FSM *f, const IWFS_FSM_OPTS *opts)` opens file * and initializes a given `IWFS_FSM` structure. * * File format: * @verbatim [FSM_CTL_MAGICK u32][block pow u8] [bmoffset u64][bmlength u64] [crzsum u64][crznum u32][crszvar u64][reserved u256] [custom header size u32][custom header data...] [fsm data...] @endverbatim * * where: * * - FSM_CTL_MAGICK: Free-space file magic number (32 bit) * - block pow: Block size as power of `2` Eg: `6` means `64` bit block size. (8 bit) * - bmoffset: Free space bitmap area offset in bytes (64 bit) * - bmlength: Free space bitmap area length. (64 bit) * - crzsum: Number of allocated blocks. (64 bit) * - crznum: Number of all allocated continuous areas. (32 bit) * - crszvar Allocated areas length standard variance (deviation^2 * N) (64 bit) * - reserved: Reserved space. * - custom header size: Length of custom header area. See `IWFS_FSM::writehdr` and `IWFS_FSM::readhdr` */ #include "iwexfile.h" #include #include IW_EXTERN_C_START /** Free space allocation flags * @see IWFS_FSM::allocate */ typedef uint8_t iwfs_fsm_aflags; /** Use default allocation settings */ #define IWFSM_ALLOC_DEFAULTS ((iwfs_fsm_aflags) 0x00U) /** Do not @em overallocate a requested free space in order to reduce fragmentation */ #define IWFSM_ALLOC_NO_OVERALLOCATE ((iwfs_fsm_aflags) 0x01U) /** Do not extend the file and its bitmap free space mapping in the case if * file size expansion is required. * In this case the `IWFS_ERROR_NO_FREE_SPACE` error will be raised.*/ #define IWFSM_ALLOC_NO_EXTEND ((iwfs_fsm_aflags) 0x02U) /** Force offset of an allocated space to be page aligned. */ #define IWFSM_ALLOC_PAGE_ALIGNED ((iwfs_fsm_aflags) 0x04U) /** Do not collect internal allocation stats for this allocation. */ #define IWFSM_ALLOC_NO_STATS ((iwfs_fsm_aflags) 0x08U) /** Force all of the allocated address space backed by real file address space. */ #define IWFSM_SOLID_ALLOCATED_SPACE ((iwfs_fsm_aflags) 0x10U) /** Do msync of bitmap allocation index. */ #define IWFSM_SYNC_BMAP ((iwfs_fsm_aflags) 0x20U) #define IWFSM_MAGICK 0x19cc7cc #define IWFSM_CUSTOM_HDR_DATA_OFFSET \ (4 /*magic*/ + 1 /*block pow*/ + 8 /*fsm bitmap block offset */ + 8 /*fsm bitmap block length*/ + \ 8 /*all allocated block length sum */ + 4 /*number of all allocated areas */ + \ 8 /* allocated areas length standard variance (deviation^2 * N) */ + 32 /*reserved*/ + \ 4 /*custom hdr size*/) /** File cleanup flags used in `IWFS_FSM::clear` */ typedef uint8_t iwfs_fsm_clrfalgs; /** Perform file size trimming after cleanup */ #define IWFSM_CLEAR_TRIM ((iwfs_fsm_clrfalgs) 0x01U) /** `IWFS_FSM` file open modes used in `IWFS_FSM_OPTS` */ typedef uint8_t iwfs_fsm_openflags; /** Do not use threading locks */ #define IWFSM_NOLOCKS ((iwfs_fsm_openflags) 0x01U) /** Strict block checking for alloc/dealloc operations. 10-15% performance overhead. */ #define IWFSM_STRICT ((iwfs_fsm_openflags) 0x02U) /** Do not trim fsm file on close */ #define IWFSM_NO_TRIM_ON_CLOSE ((iwfs_fsm_openflags) 0x04U) /** * @brief Error codes specific to `IWFS_FSM`. */ typedef enum { _IWFS_FSM_ERROR_START = (IW_ERROR_START + 4000UL), IWFS_ERROR_NO_FREE_SPACE, /**< No free space. */ IWFS_ERROR_INVALID_BLOCK_SIZE, /**< Invalid block size specified */ IWFS_ERROR_RANGE_NOT_ALIGNED, /**< Specified range/offset is not aligned with page/block */ IWFS_ERROR_FSM_SEGMENTATION, /**< Free-space map segmentation error */ IWFS_ERROR_INVALID_FILEMETA, /**< Invalid file-metadata */ IWFS_ERROR_PLATFORM_PAGE, /**< Platform page size incopatibility, data migration required. */ IWFS_ERROR_RESIZE_FAIL, /**< Failed to resize file */ _IWFS_FSM_ERROR_END } iwfs_fsm_ecode; /** * @brief `IWFS_FSM` file options. * @see iwfs_fsmfile_open(IWFS_FSM *f, const IWFS_FSM_OPTS *opts) */ typedef struct IWFS_FSM_OPTS { IWFS_EXT_OPTS exfile; size_t bmlen; /**< Initial size of free-space bitmap */ uint32_t hdrlen; /**< Length of custom file header.*/ iwfs_fsm_openflags oflags; /**< Operation mode flags */ iwfs_ext_mmap_opts_t mmap_opts; /**< Defaul mmap options used in `add_mmap` */ uint8_t bpow; /**< Block size power for 2 */ bool mmap_all; /**< Mmap all file data */ } IWFS_FSM_OPTS; /** * @brief `IWFS_FSM` file state container. * @see IWFS_FSM::state */ typedef struct IWFS_FSM_STATE { IWFS_EXT_STATE exfile; /**< File pool state */ size_t block_size; /**< Size of data block in bytes. */ iwfs_fsm_openflags oflags; /**< Operation mode flags. */ uint32_t hdrlen; /**< Length of custom file header length in bytes */ uint64_t blocks_num; /**< Number of available data blocks. */ uint64_t free_segments_num; /**< Number of free (deallocated) continuous data segments. */ double_t avg_alloc_size; /**< Average allocation number of blocks */ double_t alloc_dispersion; /**< Average allocation blocks dispersion */ } IWFS_FSM_STATE; typedef struct IWFS_FSMDBG_STATE { IWFS_FSM_STATE state; uint64_t bmoff; uint64_t bmlen; uint64_t lfbklen; uint64_t lfbkoff; } IWFS_FSMDBG_STATE; /** * @brief Auto-expandable file with support of reader/writer address space * locking * and free space blocks management using bitmaps. */ typedef struct IWFS_FSM { struct IWFS_FSM_IMPL *impl; /** * @brief Allocate a continuous address space within a file * with length greater or equal to the desired @a len bytes. * * `Offset` and `length` allocated area will be block size aligned. * * @param f `IWFS_FSM` file. * @param len Desired length of an allocated area in bytes. * @param [in,out] oaddr Placeholder for the address of an allocated area. * Value of @a oaddr passed to this function used as * `hint` in order * to allocate area located closely to the specified @a * oaddr value. * @param [out] olen Actual length of an allocated area in bytes. * @param opts Allocation options bitmask flag @ref iwfs_fsm_aflags * @return `0` on success or error code. */ iwrc(*allocate)(struct IWFS_FSM *f, off_t len, off_t *oaddr, off_t *olen, iwfs_fsm_aflags opts); /** * @brief Reallocate and adjust a size of an allocated block. * * If the given @a nlen value lesser than actual length of segment @a olen in * that case * segment will be truncated. * * @param f `IWFS_FSM` file. * @param nlen Desired length of segment in bytes. * @param oaddr [in,out] Address of an allocated segment. Placeholder for new * address of reallocated segment. * @param olen [in,out] Length of an allocated segment. Placeholder for length * of reallocated segment. * @param opts Allocation options bitmask flag @ref iwfs_fsm_aflags * @return `0` on success or error code. */ iwrc(*reallocate)(struct IWFS_FSM *f, off_t nlen, off_t *oaddr, off_t *olen, iwfs_fsm_aflags opts); /** * @brief Free a previously allocated area. * @param addr Address space offset in bytes it must be block size * aligned. * @param len Length of area to release. * @return `0` on success or error code. */ iwrc(*deallocate)(struct IWFS_FSM *f, off_t addr, off_t len); /** * @brief Check allocation status of region specified by @a addr and @a len * @return `0` on success or error code. */ iwrc(*check_allocation_status)(struct IWFS_FSM *f, off_t addr, off_t len, bool allocated); /** * @brief Write a data to the custom file header. * * A custom file header size specified in IWFS_FSM_OPTS::hdrlen options on * file creation. * * @param off Offset position relative to custom header start offset. * @param buf Data buffer to write * @param siz Number of bytes of @a buf to write into header. * @return `0` on success or error code. */ iwrc(*writehdr)(struct IWFS_FSM *f, off_t off, const void *buf, off_t siz); /** * @brief Read a data from the custom file header. * * A custom file header size specified in IWFS_FSM_OPTS::hdrlen options on * file creation. * * @param off Offset position relative to custom header start offset. * @param [out] buf Data buffer to read into * @param Number of bytes to read */ iwrc(*readhdr)(struct IWFS_FSM *f, off_t off, void *buf, off_t siz); /** * @brief Cleanup all allocated data blocks and reset the file to the initial * empty state. * * @param clrflags * @return `0` on success or error code. */ iwrc(*clear)(struct IWFS_FSM *f, iwfs_fsm_clrfalgs clrflags); /* See iwexfile.h */ /** @see IWFS_EXT::ensure_size */ iwrc(*ensure_size)(struct IWFS_FSM *f, off_t size); /** @see IWFS_EXT::add_mmap */ iwrc(*add_mmap)(struct IWFS_FSM *f, off_t off, size_t maxlen, iwfs_ext_mmap_opts_t opts); /** @see IWFS_EXT::remap_all */ iwrc(*remap_all)(struct IWFS_FSM *f); /** * @brief Get a pointer to the registered mmap area starting at `off`. * * WARNING: Internal read lock will be acquired and * must be released by subsequent `release_mmap()` call * after all activity with mmaped region has finished. * * @see IWFS_FSM::add_mmap * @see IWFS_EXT::acquire_mmap */ iwrc(*acquire_mmap)(struct IWFS_FSM *f, off_t off, uint8_t **mm, size_t *sp); /** * @brief Retrieve mmaped region by its offset @a off */ iwrc(*probe_mmap)(struct IWFS_FSM *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_FSM *f); /** @see IWFS_EXT::remove_mmap */ iwrc(*remove_mmap)(struct IWFS_FSM *f, off_t off); /** @see IWFS_EXT::sync_mmap */ iwrc(*sync_mmap)(struct IWFS_FSM *f, off_t off, iwfs_sync_flags flags); /* See iwfile.h */ /** @see IWFS_FILE::write */ iwrc(*write)(struct IWFS_FSM *f, off_t off, const void *buf, size_t siz, size_t *sp); /** @see IWFS_FILE::read */ iwrc(*read)(struct IWFS_FSM *f, off_t off, void *buf, size_t siz, size_t *sp); /** @see IWFS_FILE::close */ iwrc(*close)(struct IWFS_FSM *f); /** @see IWFS_FILE::sync */ iwrc(*sync)(struct IWFS_FSM *f, iwfs_sync_flags flags); /** @see IWFS_FILE::state */ iwrc(*state)(struct IWFS_FSM *f, IWFS_FSM_STATE *state); /** get access to the underlying iwextfile instance */ iwrc(*extfile)(struct IWFS_FSM *f, IWFS_EXT **ext); } IWFS_FSM; /** * @brief Open `IWFS_FSM` file. * * Example: * * Open a buffer pool file for multithreaded env with fibonacci file resize * policy with block size of 64 bytes and custom file header of 255 bytes * length. * * @code {.c} * IWFS_FSM_OPTS opts = { * .exfile = { * .file = { * .path = "myfile.dat", * .omode = IWFS_OWRITE | IWFS_OCREATE, * .lock_mode = IWP_WLOCK * }, * .rspolicy = iw_exfile_szpolicy_fibo * }, * .bpow = 6, // 2^6 bytes block size * .hdrlen = 255, // Size of custom file header * .oflags = IWFSM_STRICT // Use verbose free-space bitmap checking for * // allocations (10-15% overhead) * }; * * IWFS_FSM f; * size_t sp; * off_t space_len, space_addr = 0; * * iwrc rc = iwfs_fsmfile_open(&f, &opts); * * //Allocate 2 blocks of file space * rc = f.allocate(&f, 128, &space_addr, &space_len, 0); * if (!rc) { * int data = 33; * // Write some data to the allocated block with writer lock acquired on * // `[space_addr, sizeof(data))` * rc = f.lwrite(&f, space_addr, &data, sizeof(data), &sp); * ... * } * ... * @endcode * * @param f File handle * @param opts File open options * @relatesalso IWFS_FSM */ IW_EXPORT WUR iwrc iwfs_fsmfile_open(IWFS_FSM *f, const IWFS_FSM_OPTS *opts); /** * @brief Init `iwfsmfile` submodule. */ IW_EXPORT WUR iwrc iwfs_fsmfile_init(void); IW_EXTERN_C_END #endif