📄 storage.c
字号:
UINT32 psize, pnsize, pnnsize;
UINT32 pnid;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return FALSE;
}
/* Scan block list until we find an empty block */
p = IdToBlock (stobj, 0);
if ((p == NULL) || (p->next == NULL))
return FALSE; /* Nothing further to be done */
pn = p->next;
if (pn->id == 0) {
/* We have two adjacent blocks that are free, they must be joined. */
/* This should never happen, but we take care of it just in case. */
p->size += pn->size;
PUTHEADER (stobj, p);
p->next = pn->next;
DEALLOC (&pn);
return TRUE;
}
psize = p->size;
pnsize = pn->size;
pnid = pn->id;
/* If the block following p->next is free, it has to be joined: */
pnn = pn->next;
pnnsize = 0;
if ((pnn != NULL) && (pnn->id == 0))
pnnsize = pnn->size;
p->size += pnsize + pnnsize;
/* For now, we mark the area of the two (or three) blocks as one big
* empty block. The write operations to the store are carried out
* in an order that will guarantee that the contents are always
* consistent. */
PUTHEADER(stobj, p);
/* Move data part of allocated block */
CopyData (stobj, p->pos + sizeof (BlockHeader),
pn->pos + sizeof (BlockHeader),
pnsize - sizeof (BlockHeader));
/* Write header of the new empty block */
pn->size = psize + pnnsize;
pn->pos = p->pos + pnsize;
pn->id = 0;
PUTHEADER (stobj, pn);
/* Write header of the new allocated block */
p->size = pnsize;
p->id = pnid;
PUTHEADER (stobj, p);
/* Remove the list node, in case we joined two free blocks */
if (pnnsize > 0) {
pn->next = pnn->next;
DEALLOC (&pnn);
}
return TRUE;
}
/*
* Allocate a new block of a size that will hold "len" bytes of data.
* "id" must be different from NULL. If *id != 0, then that number
* is used as the Id for the new block, unless it is alreaday in use,
* in which case FALSE is returned. If *id == 0, then a new unique
* id is allocated, larger than any other currently in use.
* Returns FALSE in case of error, e.g., if there is not enough
* memory left to add a block of the indicated size.
*/
BOOL
Storage_AllocateBlock (StorageObject *stobj, UINT32 len, UINT32 *id)
{
ListNode *p, *tmp, *pnew;
UINT32 newid;
UINT32 sz;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return FALSE;
}
/* Compute the size we will search for: add space for block header
* and then round upward to nearest multiple of 8. */
sz = len + sizeof (BlockHeader);
sz = (sz + 7) & ~7;
/* Search the block list for a block of size at least sz.
* The search begins at the beginning of the block list. */
for (p = stobj->blocklist; p; p = p->next) {
if ((p->id == 0) && (p->size >= sz)) {
break;
}
}
/* If there was no block of sufficient size, return FALSE. */
if (p == NULL) {
return FALSE;
}
/* Get new ID */
if (id == NULL)
return FALSE;
if (*id == 0) {
/* Create a new unique ID */
newid = AllocNewId (stobj);
*id = newid;
}
else {
/* Use the ID suggested by the caller, unless it is already in use. */
newid = *id;
for (tmp = stobj->blocklist; tmp; tmp = tmp->next)
if (tmp->id == newid)
return FALSE;
}
if (p->size >= sz + MINBLOCKSIZE) {
/* If block is large enough, it can be split:
* write second header (size, id = 0)
* write first header (size, id = ID) */
pnew = NEWARRAY (ListNode, 1);
if (pnew == NULL)
return FALSE;
pnew->pos = p->pos + sz;
pnew->size = p->size - sz;
pnew->id = 0;
PUTHEADER (stobj, pnew);
p->size = sz;
p->id = newid;
PUTHEADER (stobj, p);
pnew->next = p->next;
p->next = pnew;
}
else /* No split, use the entire block. */ {
/* write header (size, id = ID) */
p->id = newid;
PUTHEADER (stobj, p);
}
stobj->bytesUsed += p->size;
return TRUE;
}
/*
* Fetch the contents of a block, or part of a block.
* The retrieved data is copied to the location indicated by "recData".
* Returns FALSE in case of error, e.g., if the specified block
* does not exist.
*/
BOOL
Storage_Get (StorageObject *stobj, UINT32 id,
UINT32 start, UINT32 len, VOID *recData)
{
ListNode *p;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return FALSE;
}
p = IdToBlock (stobj, id);
if (p == NULL)
return FALSE;
if (start + len + sizeof (BlockHeader) > p->size)
return FALSE;
stobj->readStorage (p->pos + sizeof (BlockHeader) + start,
len, recData);
return TRUE;
}
/*
* Write the contents of an existing block, or part of a block.
* The data written to the block is read from the location
* indicated by "recData".
* Returns FALSE in case of error, e.g., if the specified block
* does not exist, or if the indicated start position and data length
* adds up to a position beyond the current end of the block.
*/
BOOL
Storage_Put (StorageObject *stobj, UINT32 id,
UINT32 start, UINT32 len, VOID *recData)
{
ListNode *p;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return FALSE;
}
p = IdToBlock (stobj, id);
if (p == NULL)
return FALSE;
if (start + len + sizeof (BlockHeader) > p->size)
return FALSE;
stobj->writeStorage (p->pos + sizeof (BlockHeader) + start,
len, recData);
return TRUE;
}
/*
* Delete the block with given block ID.
* Returns FALSE in case of error, e.g., if the indicated block
* does not exist.
*/
BOOL
Storage_DeleteBlock (StorageObject *stobj, UINT32 id)
{
ListNode *p, *pred, *succ;
if ((stobj == NULL) || (stobj->blocklist == NULL) || (id == 0)) {
return FALSE;
}
for (pred = NULL, p = stobj->blocklist; p; pred = p, p = p->next) {
if (p->id == id)
break;
}
if (p == NULL)
return FALSE;
succ = p->next;
stobj->bytesUsed -= p->size;
/* Set to 'free' */
p->id = 0;
PUTHEADER (stobj, p);
/* Merge with previous record? */
if (pred && (pred->id == 0)) {
pred->size += p->size;
PUTHEADER (stobj, pred);
pred->next = p->next;
DEALLOC (&p);
p = pred;
}
/* Merge with next record? */
if (succ && (succ->id == 0)) {
p->size += succ->size;
PUTHEADER (stobj, p);
p->next = succ->next;
DEALLOC (&succ);
}
return TRUE;
}
/*
* Return the size of the indicated block.
* Returns 0 in case of error.
*/
UINT32
Storage_GetBlockSize (StorageObject *stobj, UINT32 id)
{
ListNode *p;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return 0;
}
p = IdToBlock (stobj, id);
if (p == NULL)
return 0;
return p->size - sizeof (BlockHeader);
}
/*
* Get the IDs of all blocks in the storage area.
* Sets "*idarr" to point to an array of UINT32 values,
* and "*len" to the number of elements in the array.
* Returns FALSE in case of error.
* NOTE: it is the caller's responsibility to deallocate the array.
*/
BOOL
Storage_GetAllBlockIds (StorageObject *stobj,
UINT32 **idarr, UINT16 *len)
{
UINT16 i = 0;
ListNode *p;
if ((stobj == NULL) || (stobj->blocklist == NULL) ||
(idarr == NULL) || (len == NULL))
return FALSE;
*idarr = NULL;
*len = 0;
/* Count the number of allocated blocks */
for (p = stobj->blocklist; p; p = p->next) {
if (p->id != 0)
i++;
}
if (i == 0) {
return TRUE;
}
*idarr = NEWARRAY (UINT32, i);
if (*idarr == NULL)
return FALSE;
*len = i;
i = 0;
for (p = stobj->blocklist; p; p = p->next) {
if (p->id != 0)
(*idarr)[i++] = p->id;
}
return TRUE;
}
/*
* Return the size of the largest block that could be allocated
* if the storage area were completely compacted.
* Returns 0 in case of error.
*/
UINT32
Storage_NumBytesFree (StorageObject *stobj)
{
INT32 nf;
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return 0;
}
nf = stobj->size - stobj->bytesUsed - 4 - sizeof (BlockHeader);
if (nf < 0)
return 0;
return (UINT32)(nf & ~0x7);
}
/*
* Return the number of bytes currently used in the allocated blocks.
*/
UINT32
Storage_BytesUsed (StorageObject *stobj)
{
if ((stobj == NULL) || (stobj->blocklist == NULL)) {
return 0;
}
return stobj->bytesUsed + 4;
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -