Harlequin RIP SDK

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 MFSNODEfindRelativeNode (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 uint8growFileBuffer (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 MFSNODEmakeNode (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...
 
MFSNODEMFSNewRoot (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...
 
MFSNODEMFSCopyTree (MFSNODE *pMFSRoot, HqBool fCopyFileData)
 Construct and return a completely new filesystem tree by making a recursive copy of the given tree. More...
 
MFSNODEMFSFindRelative (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...
 
MFSFILEMFSGetFile (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...
 
MFSITERSTATEMFSIterBegin (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...
 
MFSNODEMFSIterNode (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...
 

Detailed Description

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.

Function Documentation

◆ countOpenFiles()

static void countOpenFiles ( MFSNODE pNode,
uint32 pnReaders,
uint32 pnWriters 
)
static

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.

Parameters
pNodeThe node to descend from.
pnReadersReceives the number of open file descriptors for read access. Should be initialized to zero by the caller.
pnWritersReceives the number of open file descriptors for write access. Should be initialized to zero by the caller.

◆ deflateData()

static HqBool deflateData ( MFSFILE pFile)
static

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.

Parameters
pFileThe file to compress.
Returns
TRUE on success, FALSE on failure.

◆ destroyNode()

static void destroyNode ( MFSNODE pNode,
HqBool  fDestroyBuffer 
)
static

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.

Parameters
pNodeThe root of the sub-tree to destroy. This node will itself also be destroyed.
fDestroyBufferControls 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.

◆ findRelativeNode()

static MFSNODE * findRelativeNode ( MFSNODE pNode,
char *  pszName,
int32  flags,
MFSNODE **  ppParent,
uint32 pIndex,
MFSFILE pExistingFile,
DEVICE_result err 
)
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.

Parameters
pNodeRoot of the tree to be searched.
pszNamePathname 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).
flagsConfigured 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.
ppParentUpon 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.
pIndexUpon success (including in the file-creation case), this receives the numeric index of the file within the parent directory's child array.
pExistingFileIgnored, 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.
errA device error code, or DeviceNoError on success.
Returns
A pointer to the located file node. Returns NULL if the file was not found (and was not required to be created), or if the creation attempt failed.

◆ growFileBuffer()

static uint8 * growFileBuffer ( MFSFILE pFile)
static

Re-allocate a larger data buffer for the given file.

Parameters
pFileThe 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.
Returns
A pointer to the new buffer, or NULL if the allocation failed due to memory exhaustion.

◆ inflateData()

static HqBool inflateData ( MFSFILE pFile)
static

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.

Parameters
pFileThe file to decompress.
Returns
TRUE on success, FALSE on failure.

◆ linkNode()

static HqBool linkNode ( MFSDIR pDir,
MFSNODE pChild,
uint32 pIndex 
)
static

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.

Parameters
pDirThe parent directory, into which the new node is inserted.
pChildThe new node.
pIndexUpon successful return, receives the index of pChild within the child node array of pDir. On successful return, pDir->entries[ *pIndex ] == pChild.
Returns
TRUE on success, FALSE on failure. Failure would be due to memory exhaustion when attempting to grow the child array.

◆ makeNode()

static MFSNODE * makeNode ( char *  pszName,
HqBool  fIsDirectory,
MFSFILE pExistingFile 
)
static

Construct and return a single new node, without linking it into a directory tree.

Parameters
pszNameThe leaf name of the node. The implementation will create a freshly-allocated copy of this string.
fIsDirectoryDetermines whether the new node should be of type MFS_Directory or MFS_File.
pExistingFileWhen 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.

◆ prepareForAccess()

static HqBool prepareForAccess ( MFSFILEDESC pDesc)
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.

Parameters
pDescA 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).

◆ shedFileBuffer()

static void shedFileBuffer ( MFSFILE pFile)
static

Set pFile->pData to NULL, and de-allocate the buffer if it was runtime allocated.

Parameters
pFileThe file whose buffer is being discarded.