📄 wipdb.c
字号:
*errorCode = DB_err_exists;
return DB_null;
}
if (key == NULL || (rec->flags & DB_mask_type) == DB_flag_set)
key = (BYTE*) &"";
newRec = db_createNewRecord(rec, key, flags, NULL, errorCode);
if (*errorCode != DB_err_success)
return DB_null;
currentRecord = newRec; /* Assume this record will be used in \*/
currentRef = recordNbr; /* the next operation. Cache record. */
return recordNbr;
}
/*
* Make an copy of a record and is fields. The element order is
* preserved. Fields of type int, str and mem are copied. For
* pointer fields, only the pointer is copied, not the content
* pointed out.
*
* Even if the original is write through or backup, the copy
* may or may not be. Instead 'flags' can be set to DB_writeThrough
* or DB_backup to indicate the new persistent state. This
* parameter is only used when the destination 'to' is DB_root.
* Otherwise the persistent state is inherited from the parent,
* i.e. from the 'to' record. Use DB_noPersistentSave when no
* persistent save is wanted.
*
* 'key' is the name of the copy. If 'key' is NULL, the original
* name will be used.
*
* If an error occurs, all work is undone and a correct
* structrure is maintained.
*
* It is legal to insert the copy into the source tree.
*
* A reference to the new record is returned.
*/
DB_ref
db_copyRecord (DB_ref from, DB_ref to, BYTE* key, UINT16 flags, UINT8 *errorCode)
{
ElementPtr fromElement;
ElementPtr topFromElement;
ElementPtr toElement;
ElementPtr topToElement;
RecordPtr toRec;
RecordPtr topToRec;
ElementPtr newElement;
DataUnion data;
UINT16 copyData;
short direction;
fromElement = (ElementPtr) db_findRecord(from);
toRec = db_findRecord(to); /* Search is continued further down (X) */
if (fromElement == NULL || toRec == NULL) {
*errorCode = DB_err_record;
return DB_null;
}
topFromElement = fromElement;
topToRec = toRec;
direction = GOING_DOWN;
if (key == NULL)
if (fromElement->flags & DB_mask_key)
key = ((FieldPtr) fromElement)->key;
else {
*errorCode = DB_err_noKeyGiven;
return DB_null;
}
if ((toRec->flags & DB_mask_type) == DB_flag_rec && db_findField(key)) { /* (X) */
*errorCode = DB_err_exists;
return DB_null;
}
copyData = ! (flags & DB_mask_move);
/* Create first element */
/* BOOKMARK:FLAGS */
flags = flags & DB_mask_persistent | fromElement->flags & ~ DB_mask_persistent;
newElement = (ElementPtr) db_createNewRecord(toRec, key, flags , NULL, errorCode);
if (*errorCode != DB_err_success)
return NULL;
topToElement = newElement;
toElement = newElement;
for (;;) {
/* Move to next element */
if (direction != GOING_UP && (fromElement->flags & DB_mask_dataUse) == DB_flag_dataChild &&
fromElement->data.e != fromElement)
{
direction = GOING_DOWN;
toRec = (RecordPtr) toElement;
fromElement = fromElement->data.e;
} else {
if (! (fromElement->flags & DB_mask_lastElement)) {
direction = GOING_RIGHT;
} else {
toElement = toElement->next;
Undo_going_down:
direction = GOING_UP;
toRec = (RecordPtr) toRec->next;
}
fromElement = fromElement->next;
}
/* Prevent loops when dest in source. */
if (fromElement == topToElement) {
if (fromElement->flags & DB_mask_lastElement)
/* Should never gone down. Now the the from-record contains
* only a copy of the new tree. Originaly it was empty and
* should be treated as if it still is. */
goto Undo_going_down;
else
/* Skip this element, which is the root of the copy.
* 'direction' must be GOING_DOWN to attach next element
* correctly. */
fromElement = fromElement->next;
}
/* Check if all is done */
if (fromElement == topFromElement)
break;
/* Create and add new element */
if (direction != GOING_UP) {
if ((fromElement->flags & DB_mask_dataUse) == DB_flag_dataChild)
/* ----------------------------------------------------- rec, set */
newElement = (ElementPtr) db_createNewRecord(toRec, ((FieldPtr) fromElement)->key,
fromElement->flags, direction == GOING_DOWN ? NULL : toElement, errorCode);
else { /* ------------------------------------------------ int, str, mem, ptr */
switch (fromElement->flags & DB_mask_dataUse) {
case DB_flag_dataExternal: /* str, mem */
if (copyData) {
data.s = db_duplicateStrMem(fromElement->data.s,
(UINT16) (fromElement->flags & DB_mask_type), errorCode);
break;
}
/* fall through if DB_flag_move is set */
case DB_flag_dataInternal: /* int, ptr */
data = fromElement->data;
break;
}
newElement = db_createNewElement(toRec, ((FieldPtr) fromElement)->key,
fromElement->flags, data, direction == GOING_DOWN ? NULL : toElement,
NULL, errorCode);
}
if (*errorCode != DB_err_success) {
db_deleteItem_ptr(topToRec, topToElement);
return NULL;
}
toElement = newElement;
}
}
return ((RecordPtr) toElement)->ref;
}
/*
* Move a record to a new destination. The element order is
* preserved.
*
* Even if the original is write through or backup, the copy
* may or may not be. Instead 'flags' can be set to DB_writeThrough
* or DB_backup to indicate the new persistent state. This
* parameter is only used when the destination 'to' is DB_root.
* Otherwise the persistent state is inherited from the parent,
* i.e. from the 'to' record. Use DB_noPersistentSave when no
* persistent save is wanted.
*
* If an error occurs, all work is undone and a correct
* structrure is maintained.
*
* A record moved into itself, it is deleted and the reference
* returned is not valid.
*
* A moved record gets a new reference and it is returned.
*/
DB_ref
db_moveRecord (DB_ref from, DB_ref fromParent, DB_ref to, UINT16 flags, UINT8 *errorCode)
{
BYTE key[DB_keyLen];
RecordPtr fromRec;
RecordPtr fromParentRec;
DB_ref ref;
fromRec = db_findRecord (from);
fromParentRec = db_findRecord (fromParent);
if (fromRec == NULL || fromParentRec == NULL)
return DB_null;
memcpy (key, fromRec->key, DB_keyLen);
*fromRec->key = '\0';
ref = db_copyRecord (from, to, key, (UINT16) (flags | DB_flag_move), errorCode);
if (*errorCode != DB_err_success) {
*fromRec->key = *key;
return DB_null;
}
db_saveDataMode = TRUE;
db_deleteItem_ptr (fromParentRec, (ElementPtr) fromRec);
db_saveDataMode = FALSE;
return ref;
}
/*
* Remove the specified item. If it is a string or memory block,
* associated data is freed. For records and sets, the entire
* subtree is deleted.
*/
UINT8
db_deleteItem (DB_ref record, const BYTE* key)
{
if (! db_findRecord(record))
return DB_err_record;
if (! db_findField(key))
return DB_err_field;
db_deleteElement(db_saveDataMode);
return DB_err_success;
}
/*
* Same functionality as deleteItem. 'element' is a pointer to the
* element to be deleted and 'rec' its parent.
*/
UINT8
db_deleteItem_ptr (RecordPtr rec, ElementPtr element)
{
if (rec == NULL || element == NULL)
return DB_err_nullValue;
prevElement = NULL;
currentElement = rec->data.e;
while (currentElement != (ElementPtr) rec) {
if (currentElement == element) {
currentRecord = rec;
currentRef = DB_null;
db_deleteElement(db_saveDataMode);
return DB_err_success;
}
prevElement = currentElement;
currentElement = currentElement->next;
}
return DB_err_field;
}
/*
* Remove all fields connected to 'record'. The entire subtree
* is deleted. Works on both records and sets.
*/
UINT8
db_clearRecord (DB_ref record)
{
if (! db_findRecord(record))
return DB_err_record;
if (currentRecord->flags & DB_mask_writeThrough)
DB_SET_CHILD_ID(((RecordBlockPtr) currentRecord)->blockId, 0)
db_deleteContent(currentRecord, db_saveDataMode);
return DB_err_success;
}
/*
* Move 'element' to the beginning of the list it is in.
*
* If the system crashes during this function, the
* 'element' item may be lost, even when using persitent
* memory.
*/
void
db_moveToFront (RecordPtr parent, ElementPtr element)
{
ElementPtr currentElem;
ElementPtr prevElem;
UINT32 prevId;
/* Find previous element in list */
currentElem = parent->data.e;
prevElem = NULL;
while (currentElem != element) {
if (currentElem == (ElementPtr)parent)
return;
prevElem = currentElem;
currentElem = currentElem->next;
}
if (prevElem == NULL)
return;
/* Move element to the beginning of the list */
if (element->flags & DB_mask_writeThrough) {
prevId = db_getBlockId(prevElem);
if (element->next == (ElementPtr) parent)
DB_SET_CHILD_ID(db_getBlockId((ElementPtr) parent), prevId)
else
DB_SET_NEXT_ID(element->next, prevId)
DB_SET_NEXT_ID(element, 0)
DB_SET_NEXT_ID(parent->data.e, db_getBlockId(element))
}
if (currentElem->flags & DB_mask_lastElement) {
currentElem->flags &= ~ DB_flag_lastElement;
prevElem->flags |= DB_flag_lastElement;
}
prevElem->next = currentElem->next;
currentElem->next = parent->data.e;
parent->data.e = currentElem;
}
/* ======== administration ======== */
/*
* Initiate the database and its structures.
*
* On some systems (Ericsson), when out of memory, the system
* does not restart until the control has been returned to the
* event loop. In this case, it is not legal to call any database
* functions between dbInit and returning to the event loop.
*
* TODO: add return value
*/
UINT16
db_dbInit (void)
{
char buffer[10+DB_keyLen];
UINT32 id;
int i;
db_serviceMode = FALSE;
db_saveDataMode = FALSE;
refHashTable = (RecordPtr*) OSConnectorAlloc(sizeof(RecordPtr) * DB_refHashSize);
db_root = (RecordPtr) OSConnectorAlloc(sizeof(RecordBlock));
#ifndef HAS_SETJMP
if (!refHashTable || !db_root)
return 0; /* A new event loop iteraration will cause a restart. */
#endif
db_root->next = NULL;
db_root->data.r = db_root; /* BOOKMARK:FLAGS */
db_root->flags = DB_flag_rec | DB_flag_key | DB_flag_writeThrough | DB_flag_dataChild;
db_root->ref = DB_root;
db_root->nextRec = NULL;
*(db_root->key + 0 ) = 'r';
*(db_root->key + 1 ) = 'o';
*(db_root->key + 2 ) = 'o';
*(db_root->key + 3 ) = 't';
*(db_root->key + 4 ) = '\0';
((RecordBlockPtr)db_root)->blockId = 1024;
for (i=0; i<DB_refHashSize; i++)
refHashTable[i] = NULL;
refHashTable[DB_root & DB_refHashMask] = db_root;
currentRef = DB_null;
if (!Storage_Init(&store, DATABASE_STORAGESIZE, (ReadFunction*) MEMa_readDatabase,
(WriteFunction*) MEMa_writeDatabase))
return 0;
if (! db_loadDB_wt()) {
/* First time or start-up failure */
*(UINT16*) (buffer + 8) = db_root->flags;
*(UINT32*) (buffer + 0) = 0; /* Next id */
*(UINT32*) (buffer + 4) = 0; /* Child id */
*(BYTE*) (buffer + 10) = 'r';
*(BYTE*) (buffer + 11) = 'o';
*(BYTE*) (buffer + 12) = 'o';
*(BYTE*) (buffer + 13) = 't';
*(BYTE*) (buffer + 14) = '\0';
id = 1024;
Storage_AllocateBlock(&store, 10+DB_keyLen, &id);
Storage_Put(&store, id, 0, 10+DB_keyLen, buffer);
}
db_loadDB_bu();
return 0;
}
/*
* Deletes the database content and frees all static structures.
*
* Remove of all non-database structures connected to the
* database first.
*/
UINT16
db_dbTerminate (void)
{
db_serviceMode = TRUE;
db_saveDB_bu();
db_deleteContent(db_root, FALSE);
OSConnectorFree(db_root);
OSConnectorFree(refHashTable);
Storage_Finalize(&store);
return 0;
}
/* ============================================ */
/* ======= persistent memory handling ======= */
/* ======= write-through ======= */
/* ============================================ */
/*
STRUCTURE
Structure of write-through blocks at presistet memory:
type structure[size bytes]
---------------------------------
rec
set nextId[4] childBlockId[4] flags[2] key[DB_keyLen]
int nextId[4] data[4] flags[2] key[DB_keyLen]
str
mem nextId[4] dataLength[4] flags[2] key[DB_keyLen] data[dataLength]
If the DB_flag_key is not set, the key field is missing.
The processor endian will affect the byte order within each
field, but not the functionality.
There is no type corresponding to pointer fields! The pointers
point to structures outside of the database and the system has
been restarted. They make no sense and are unsafe. Convert the
pointer to an integer and save it as an integer element if
using write-through elements.
Elements are save in reversed order. The first element in each
list is placed last when saved. This is faster and makes it
easier to reconstruct the structure when rebuilding.
The flag DB_flag_lastElement is not updated for the persistent
copy since it uses its own structure. The flag is reconstructed
when the copy is read.
FUNCTIONS
This module contains four main functions:
db_deleteElement_wt Remove the block on persistent memory pointed
out by the 'blockId' field. Called from
db_deleteElement.
db_saveElement_wt Save an element to prestent memory. Called from
db_createNewElement and db_createNewRecord.
db_rebuildDB_wt Read database from presistent memory and
recreate it. Called by db_loadDB_wt.
db_loadDB_wt Trigger recreation of database. Called by
db_dbInit.
*/
/*
* Return blockId field of 'element'.
*/
static UINT32
db_getBlockId (ElementPtr element)
{
if ((element->flags & DB_mask_dataUse) == DB_flag_dataChild)
return ((RecordBlockPtr) element)->blockId;
else
if (element->flags & DB_mask_key)
return ((FieldBlockPtr) element)->blockId;
else
return ((ElementBlockPtr) element)->blockId;
}
/*
* Delete a write-through block. If 'prev_wt' is NULL, 'current'
* is first in list and the record block conncted to 'parent' is
* updated instead. 'prev' must point to previous write-through
* element.
*/
static void
db_deleteElement_wt (RecordPtr parent, ElementPtr current, ElementPtr prev)
{
ElementPtr nextElem;
UINT32 nextId;
if (parent == db_root) {
/* Cannot trust prev to point out an write-through element at root level, recalculate */
prev = db_findPrev_wt(current);
/* Search for next write-through element. */
for (nextElem = current->next; nextElem != (ElementPtr) parent;
nextElem = nextElem->next)
if (nextElem->flags & DB_mask_writeThrough)
break;
} else
nextElem = current->next;
if (prev == NULL)
nextId = 0;
else
nextId = db_getBlockId(prev);
if (nextElem == (ElementPtr) parent) /* = head in the reversed persistent list */
DB_SET_CHILD_ID(((RecordBlockPtr) parent)->blockId, nextId)
else
DB_SET_NEXT_ID(nextElem, nextId)
Storage_DeleteBlock(&store, db_getBlockId(current));
}
/*
* Save an element marked as write-through to persistent memory.
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -