Implementation of an input/output device tied to in-memory data. More...
#include <string.h>
#include "skinkit.h"
#include "file.h"
#include "hqmemcpy.h"
#include "memfs.h"
#include "mem.h"
#include "swdevice.h"
#include "zlibutil.h"
Macros | |
#define | INITIAL_BUFFER_SIZE 4096 |
The default allocation (in bytes) of RAM space for new files. | |
#define | COMPRESSION_THRESHOLD 1024 |
The size (in bytes) below which we don't bother with compression. | |
#define | BUFFER_GROWTH_FACTOR 2 |
The factor by which file data buffers are expanded when the current buffer is exhausted. Must be greater than 1. | |
#define | BUFFER_GROWTH_RETRIES 4 |
The number of retries attempted when expanding data buffers. The requested expansion will be reduced by the reciprocal of this number for each attempt. | |
#define | INITIAL_LIST_SIZE 4 |
The default number of slots allocated for child nodes when a new directory node is added. | |
#define | LIST_GROWTH_FACTOR 2 |
The factor by which directory lists are grown when the current list size is not large enough to accommodate a new entry. | |
Functions | |
static MFSNODE * | findRelativeNode (MFSNODE *pNode, char *pszName, int32 flags, MFSNODE **ppParent, uint32 *pIndex, MFSFILE *pExistingFile, DEVICE_result *err) |
Find or create a file node within the tree. More... | |
static HqBool | prepareForAccess (MFSFILEDESC *pDesc) |
Ensure that the file data is ready for access according to the requirements of the given file descriptor. More... | |
static HqBool | linkNode (MFSDIR *pDir, MFSNODE *pChild, uint32 *pIndex) |
Link a node into the tree. More... | |
static uint8 * | growFileBuffer (MFSFILE *pFile) |
Re-allocate a larger data buffer for the given file. More... | |
static void | shedFileBuffer (MFSFILE *pFile) |
Set pFile->pData to NULL, and de-allocate the buffer if it was runtime allocated. More... | |
static void | destroyNode (MFSNODE *pNode, HqBool fDestroyBuffer) |
Completely destroy a node and all of its descendants inclusively. More... | |
static MFSNODE * | makeNode (char *pszName, HqBool fIsDirectory, MFSFILE *pExistingFile) |
Construct and return a single new node, without linking it into a directory tree. More... | |
static HqBool | inflateData (MFSFILE *pFile) |
Decompress the file's data buffer using zlib. More... | |
static HqBool | deflateData (MFSFILE *pFile) |
Compress the file's data buffer using zlib. More... | |
static void | countOpenFiles (MFSNODE *pNode, uint32 *pnReaders, uint32 *pnWriters) |
Determine the degree of open file access beneath a particular node. More... | |
MFSNODE * | MFSNewRoot (char *pszRootName) |
Construct and return a completely new root directory node. More... | |
void | MFSReleaseRoot (MFSNODE *pMFSRoot) |
Destroy the given root node, and the entire virtual filesystem descended from it. More... | |
MFSNODE * | MFSCopyTree (MFSNODE *pMFSRoot, HqBool fCopyFileData) |
Construct and return a completely new filesystem tree by making a recursive copy of the given tree. More... | |
MFSNODE * | MFSFindRelative (MFSNODE *pRoot, char *pszFilename, MFSNODE **ppParent, uint32 *pIndex, DEVICE_result *err) |
Locate a file node, relative to the given root, according to its pathname. More... | |
int32 | MFSDelete (MFSNODE *pRoot, char *pszFilename, DEVICE_result *err) |
Delete a file or directory, and completely release all of its associated memory resources. More... | |
int32 | MFSRename (MFSNODE *pRoot, char *pszFromName, char *pszToName, DEVICE_result *err) |
Rename a file, preserving all of its contents and other properties. More... | |
HqBool | MFSOpen (MFSNODE *pRoot, char *pszFilename, int32 openFlags, MFSFILEDESC **ppDesc, DEVICE_result *err) |
Open or create a file for reading and/or writing. More... | |
MFSFILE * | MFSGetFile (MFSFILEDESC *pDesc) |
Return the underlying file from its open descriptor. More... | |
HqBool | MFSClose (MFSFILEDESC *pDesc) |
Close a file that was previously opened with MFSOpen(). More... | |
int32 | MFSRead (MFSFILEDESC *pDesc, uint8 *buffer, int32 cbLen, DEVICE_result *err) |
Read a specified number of bytes from the current location of an open file. More... | |
int32 | MFSWrite (MFSFILEDESC *pDesc, uint8 *buffer, int32 cbLen, DEVICE_result *err) |
Write a specified number of bytes to an open file at the current location, expanding the file as necessary. More... | |
int32 | MFSWriteString (MFSFILEDESC *pDesc, char *pszString, DEVICE_result *err) |
Write a specified NUL-terminated character string to an open file at the current location, expanding the file as necessary. The terminating NUL character itself is not written. More... | |
HqBool | MFSSeek (MFSFILEDESC *pDesc, Hq32x2 *pDestination, int32 flags, DEVICE_result *err) |
Set (or query) the current the position in an open file. More... | |
HqBool | MFSSetCompression (MFSFILEDESC *pDesc, HqBool fCompress) |
Controls whether the file's data buffer will be compressed (or re-compressed) when the file is closed. More... | |
HqBool | MFSAvail (MFSFILEDESC *pDesc, Hq32x2 *pAvail, int32 reasonFlag) |
Determine the number of bytes that are currently available to be read from an open file. More... | |
MFSITERSTATE * | MFSIterBegin (MFSNODE *pRoot, void *pPrivate) |
Begin an iteration over all or part of the file system. More... | |
HqBool | MFSIterNext (MFSITERSTATE *pState) |
Proceed to the first (or next) file in an iteration sequence. More... | |
void | MFSIterEnd (MFSITERSTATE *pState) |
Close an iteration sequence. More... | |
uint32 | MFSIterNameLength (MFSITERSTATE *pState, HqBool fLeadingSlash) |
Obtain the pathname length of the file at the current iteration step. More... | |
HqBool | MFSIterName (MFSITERSTATE *pState, char *pszFilenameBuf, uint32 bufLen, HqBool fLeadingSlash) |
Obtain the pathname of the file at the current iteration step. More... | |
MFSNODE * | MFSIterNode (MFSITERSTATE *pState) |
Obtain a direct pointer to the MFS_File node of the current iteration step. More... | |
void | MFSMemUsage (MFSNODE *pNode, uint32 *pROMSize, uint32 *pRAMSize) |
Calculate the amount of memory currently in use by the virtual filesystem descended from the given node. More... | |
Implementation of an input/output device tied to in-memory data.
This is used as the backend to the RAM device %ramdev%
described in ramdev.c.
Determine the degree of open file access beneath a particular node.
This function can be used to determine whether it is currently safe to delete a directory, for example.
pNode | The node to descend from. |
pnReaders | Receives the number of open file descriptors for read access. Should be initialized to zero by the caller. |
pnWriters | Receives the number of open file descriptors for write access. Should be initialized to zero by the caller. |
Compress the file's data buffer using zlib.
If the given file already has a compressed data stream, and it has not been modified, then this function just throws away the uncompressed data, since it is not necessary to re-compress an unmodified file.
pFile | The file to compress. |
Completely destroy a node and all of its descendants inclusively.
Because nodes have no internal pointers to their parent, this function does not unlink pNode from its directory. The caller is assumed to have done this separately, by NULLing out the appropriate slot in the directory's child node array, before calling this function to destroy the unlinked sub-tree.
All dynamically-allocated memory associated with pNode and its descendants will be freed, with the exception of file data buffers if fDestroyBuffer is FALSE
.
Upon return from this function, pNode will be a stale pointer, and should not be used again.
pNode | The root of the sub-tree to destroy. This node will itself also be destroyed. |
fDestroyBuffer | Controls whether the internal data buffers of file nodes are de-allocated. Normally, you would pass TRUE . The only sensible exception is where pNode is a leaf file, whose buffer has just been assigned elsewhere. In this case, operation is non-recursive. There is no value in recursively destroying a sub-tree without de-allocating the file buffers: they will simply leak. |
|
static |
Find or create a file node within the tree.
This function can locate existing file nodes, as well as create new ones. It also returns enough information about the node's position within its parent directory, so that the caller could decide to unlink the node from the tree.
The function works recursively. Recursion is achieved by replacing the first argument with an inner tree node, and correspondingly advancing past the leftmost path element of pszName.
When creating new files, this function will automatically create any new directories required to house the file according to its specified pathname.
pNode | Root of the tree to be searched. |
pszName | Pathname of the file to be located, which should be expressed relative to pNode. The caller should ensure that this string is in writable memory (it must not be a C literal string). In case of doubt, always make a copy of the string, but the caller must then also ensure that the copy is freed again later. The reason for this is that the implementation makes temporary changes within the string to assist its search algorithm. These changes are not persistent, but it is possible for the changes to trigger memory faults if the strings are in ROM (as literal strings may be). |
flags | Configured as per the DEVICETYPE::open_file() function prototype in the Core RIP Device Interface, indicating the required access to the file. Crucially, this argument determines whether or not to create a new file. |
ppParent | Upon success (including in the file-creation case), this receives a pointer to the parent node of the file. This will always be a node of type MFS_Directory. |
pIndex | Upon success (including in the file-creation case), this receives the numeric index of the file within the parent directory's child array. |
pExistingFile | Ignored, except in the file-creation case. When a file is being created, this can be used to specify an existing file, whose contents should be transferred to the new file. This feature is used, for example, when re-naming a file. |
err | A device error code, or DeviceNoError on success. |
NULL
if the file was not found (and was not required to be created), or if the creation attempt failed. Re-allocate a larger data buffer for the given file.
pFile | The file whose buffer is to be grown. The current buffer will be expanded by a factor BUFFER_GROWTH_FACTOR, memory-permitting. Otherwise, attempts will be made for less growth. If the re-allocation succeeds, the old buffer will automatically be freed (unless it is a static array). The old file contents are copied into the new buffer, and the cbCapacity and fDynamicBuffer fields of pFile are updated for the new buffer. |
NULL
if the allocation failed due to memory exhaustion. Decompress the file's data buffer using zlib.
If the file's fCompressed field is TRUE
, use this function to decompress its data stream before trying to read/write the file.
This function is idempotent. If you call it redundantly, it will behave as a no-op and return TRUE
.
pFile | The file to decompress. |
Link a node into the tree.
Forms a link between a parent directory and a new node, which may be either a directory or a file, but which must not already be linked into the tree.
This function will automatically grow the directory's child node array as necessary.
pDir | The parent directory, into which the new node is inserted. |
pChild | The new node. |
pIndex | Upon successful return, receives the index of pChild within the child node array of pDir. On successful return, pDir->entries[ *pIndex ] == pChild . |
Construct and return a single new node, without linking it into a directory tree.
pszName | The leaf name of the node. The implementation will create a freshly-allocated copy of this string. |
fIsDirectory | Determines whether the new node should be of type MFS_Directory or MFS_File. |
pExistingFile | When making a file node, this pointer can optionally specify a pre-existing file, whose fields (including the data buffer pointers) will be copied into the new file. If you pass NULL , then a fresh file will be made, with a freshly-allocated, empty data buffer of default initial size. This parameter is ignored completely if fIsDirectory == TRUE . |
|
static |
Ensure that the file data is ready for access according to the requirements of the given file descriptor.
If write access is required, this function calls inflateData() to decompress the whole file.
If read-only access is required, this function prepares a zlib stream, so that the data can be read sequentially without decompressing the whole file. This is an optimization aimed at saving memory in some typical situations. The caller does not declare that all reads will be sequential, but it is useful for us to start by assuming that they will be. We can then avoid a full decompression until the caller performs the first arbitrary seek operation. If this happens, it will be detected by MFSSeek(), which will discard the zlib stream, and call inflateData() to decompress the whole file.
pDesc | A valid new file descriptor, which has not yet been used for any I/O operations (unless the caller wishes to reset access to the beginning of the file). |
|
static |
Set pFile->pData
to NULL, and de-allocate the buffer if it was runtime allocated.
pFile | The file whose buffer is being discarded. |