📄 wipdb.c
字号:
* If element->blockId == 0, then this is the first time this
* element is saved. A new id then is allocated and set.
*
* Return TRUE if successful.
*/
static BOOL
db_saveElement_wt (RecordPtr parent, ElementPtr element, ElementPtr prev)
{
char buffer[10+DB_keyLen];
ElementPtr tempElem;
ElementPtr nextElem;
UINT32 id;
UINT32 nextId;
UINT32 newId;
UINT32 childId;
UINT32 len;
UINT32 dataLen;
UINT32 blockLen;
UINT16 type = element->flags & DB_mask_type;
UINT16 dataUse = element->flags & DB_mask_dataUse;
UINT16 keyLen = (element->flags & DB_mask_key) ? DB_keyLen : 0;
/* block len */
len = 10 + keyLen;
if (type == DB_flag_str) {
dataLen = strlen((char*) element->data.s) + 1;
len += dataLen;
} else if (type == DB_flag_mem) {
dataLen = *(UINT16*) element->data.p;
len += dataLen;
}
/* Allocate block */
id = db_getBlockId(element);
if (id == 0) { /* First time */
newId = 0;
if (! Storage_AllocateBlock(&store, len, &newId))
/* Out of persistent memory */
return FALSE;
} else {
blockLen = Storage_GetBlockSize(&store, id);
if (len <= blockLen && len > blockLen - 8) { /* Rounded up, 8 byte alignment */
/* Reuse block */
newId = id;
} else {
/* New block */
newId = 0;
if (! Storage_AllocateBlock(&store, len, &newId))
/* Out of persistent memory */
return FALSE;
}
}
if (parent == db_root)
/* Cannot trust prev to point out an write-through element at root level, recalculate */
prev = db_findPrev_wt(element);
if (prev == NULL)
nextId = 0;
else
nextId = db_getBlockId(prev);
*(UINT32*) (buffer+0) = nextId;
*(UINT16*) (buffer+8) = element->flags;
/* Set data (buffer+4) */
if (dataUse == DB_flag_dataChild) { /* rec, set */
if (((RecordPtr) element)->data.e == element)
childId = 0;
else {
/* End element is head in the reversed persistent list */
for (tempElem = ((RecordPtr) element)->data.e; tempElem != element;
tempElem = tempElem->next)
prev = tempElem;
childId = db_getBlockId(prev);
}
*(UINT32*) (buffer+4) = childId;
} else if (dataUse == DB_flag_dataInternal) /* int */
*(UINT32*) (buffer+4) = element->data.i;
else /* str, mem */
*(UINT32*) (buffer+4) = len;
if (keyLen != 0)
memcpy(buffer+10, ((FieldPtr) element)->key, DB_keyLen);
Storage_Put(&store, newId, 0, 10 + keyLen, buffer);
if (dataUse == DB_flag_dataExternal)
Storage_Put(&store, newId, 10 + keyLen, dataLen, element->data.p);
/* Insert into list in persistent memory */
if (newId != id) {
/* Find next write-through element in the same list as 'element'. */
if (parent == db_root) {
/* At top level, not all elements are write-through. Search for
* next write-through element. */
for (nextElem = element->next; nextElem != (ElementPtr) parent;
nextElem = nextElem->next)
if (nextElem->flags & DB_mask_writeThrough)
break;
} else
nextElem = element->next;
if (nextElem == (ElementPtr) parent)/* End element is head in the reversed persistent list */
DB_SET_CHILD_ID(((RecordBlockPtr) parent)->blockId, newId)
else
DB_SET_NEXT_ID(nextElem, newId)
if (id != 0)
Storage_DeleteBlock(&store, id);
}
/* Update blockId */
if (dataUse == DB_flag_dataChild)
((RecordBlockPtr) element)->blockId = newId;
else
if (keyLen != 0)
((FieldBlockPtr) element)->blockId = newId;
else
((ElementBlockPtr) element)->blockId = newId;
return TRUE;
}
/*
* Rebuilds database by reading write-through data. Recursive;
* adds fields to an already existing record.
*
* 'parent' is a reference to the record corresponding to the
* parent of 'id'.
*
* If the function returns FALSE, the reconstruction has failed
* and the database has only been partially reconstructed.
*/
static BOOL
db_rebuildDB_wt (UINT32 id, DB_ref parent)
{
BYTE buf[BUFSIZE];
DataUnion data;
ElementPtr element;
DB_ref newRef;
BYTE* key;
UINT32 blockLen;
UINT32 offset;
UINT32 readLen;
UINT32 nextId;
UINT16 flags;
UINT8 error;
while (id != 0) {
blockLen = Storage_GetBlockSize(&store, id); /* blockLen rounded up */
readLen = MIN(BUFSIZE, blockLen);
Storage_Get(&store, id, 0, readLen, buf);
nextId = *(UINT32*) (buf + 0);
flags = *(UINT16*) (buf + 8);
/* DB_flag_lastElement contains trash value. It is recalculated
* in db_setItem, db_createRecord. */
if (flags & DB_mask_key) {
key = buf + 10;
offset = 10 + DB_keyLen;
} else
offset = 10;
switch (flags & DB_mask_dataUse) {
case DB_flag_dataChild:
newRef = db_createRecord(parent, key, flags, &error);
if (error)
return FALSE;
((RecordBlockPtr) currentRecord)->blockId = id;
if ( ! db_rebuildDB_wt(*(UINT32*)(buf + 4), newRef))
return FALSE;
goto Next_iteration;
case DB_flag_dataExternal:
blockLen = *(UINT32*) (buf + 4); /* Exact blockLen */
readLen = MIN(BUFSIZE, blockLen);
data.s = (BYTE*) OSConnectorAlloc(blockLen - offset);
#ifndef HAS_SETJMP
if (data.s == NULL)
return FALSE;
#endif
memcpy(data.s, buf + offset, (size_t)(readLen - offset));
if (readLen < blockLen)
Storage_Get(&store, id, readLen, blockLen - readLen, data.s + readLen - offset);
element = db_setItem(parent, key, flags, data, &error);
if (error) {
OSConnectorFree(data.s);
return FALSE;
}
break;
case DB_flag_dataInternal:
data.i = *(UINT32*) (buf + 4);
element = db_setItem(parent, key, flags, data, &error);
if (error)
return FALSE;
break;
}
if (flags & DB_mask_key) {
((FieldBlockPtr) element)->blockId = id;
} else {
((ElementBlockPtr) element)->blockId = id;
}
Next_iteration:
db_markId(id);
id = nextId;
}
return TRUE;
}
/*
* Load database from persistent memory and rebuld structure.
* Only elements saved as write-through are recovered.
*
* Return FALSE if there is nothing to load.
*/
static BOOL
db_loadDB_wt (void)
{
UINT32 id;
UINT16 len;
BOOL clearAll = FALSE;
int i;
if (! Storage_GetAllBlockIds(&store, &idArr, &len) || len == 0)
return FALSE;
db_serviceMode = TRUE; /* Turn of write-through while rebulding */
Storage_Get(&store, 1024, 4, 4, &id);
db_markId(1024);
if (! db_rebuildDB_wt(id, DB_root)) {
/* Rebuild has failed. Delete everything. */
db_clearRecord(db_root->ref);
clearAll = TRUE;
}
db_serviceMode = FALSE;
for (i=0; i < len; i++) {
if ((idArr[i] <= 0x80000000 || clearAll) && idArr[i] > 1024) {
/* Found a loose block */
Storage_DeleteBlock(&store, idArr[i] & 0x7fffffff);
}
}
OSConnectorFree(idArr);
return ! clearAll;
}
/* ============================================ */
/* ======= persistent memory handling ======= */
/* ======= backup ======= */
/* ============================================ */
/*
When saving the elements, the persistent flags of the flags field are reused. The DB_flag_backup must be set
and DB_flag_writeThrough must be cleared. This means they can be used for storing other information when
saving, since these flags can be reconstructed when the database is reloaded.
FUNCTIONS
This module contains four main functions:
db_writeData_bu
db_rebuildDB_bu
db_saveDB_bu
db_loadDB_bu
*/
/*
*
*
*/
static void
db_writeDB_bu (wap_cvt_t* obj)
{
ElementPtr current;
ElementPtr nextCurrent;
BYTE* s;
UINT16 flags;
UINT16 len;
short direction;
short nextDirection;
short level;
short offset;
level = 0;
current = db_root->data.e;
direction = GOING_DOWN;
while (current != (ElementPtr) db_root) {
if (! (current->flags & DB_mask_backup)) { /* Only save backup element subtrees */
current = current->next;
continue;
}
/* Calculate next element */
if (direction != GOING_UP && (current->flags & DB_mask_dataUse) == DB_flag_dataChild &&
current->data.e != current) {
nextDirection = GOING_DOWN;
nextCurrent = current->data.e;
offset = 1;
} else {
if (current->flags & DB_mask_lastElement) {
nextDirection = GOING_UP;
offset = -1;
} else {
nextDirection = GOING_RIGHT;
offset = 0;
}
nextCurrent = current->next;
}
/* Save element */
if (direction != GOING_UP) {
/* Write flags */
flags = current->flags & ~ (DB_mask_backup | DB_mask_lastElement | DB_mask_lastElement2) |
(nextDirection == GOING_DOWN ? DB_flag_hasChild : 0) |
(current->flags & DB_flag_lastElement ? DB_flag_lastElement2 : 0) |
(level == 0 ? DB_flag_rootLevel : 0);
/* Set DB_flag_hasChild if next element is a child and
* copy DB_flag_lastElement to DB_flag_lastElement2.
* See comment in db_rebuildDB_bu for use of
* DB_flag_lastElement2. DB_flag_rootLevel is set on root
* level.
*
* DB_flag_hasChild and DB_flag_rootLevel are temprary flags,
* reusing other flags. See definitions. */
wap_cvt_uint16(obj, &flags);
/* Write key */
if (current->flags & DB_mask_key)
wap_cvt_static_bytevector(obj, DB_keyLen, ((FieldPtr) current)->key);
/* Write data */
if ((current->flags & DB_mask_dataUse) != DB_flag_dataChild) {
if ((current->flags & DB_mask_type) == DB_flag_int) { /* int */
wap_cvt_uint32(obj, ¤t->data.i);
} else {
if ((current->flags & DB_mask_type) == DB_flag_str) { /* str */
len = strlen((char*) current->data.s) + 1;
s = current->data.s;
} else { /* mem */
len = *(UINT16*) current->data.s - 2;
s = current->data.s + 2;
}
wap_cvt_uint16(obj, &len);
wap_cvt_static_bytevector(obj, len, s);
}
}
}
/* Move to next element */
direction = nextDirection;
current = nextCurrent;
level += offset;
}
}
/*
*
*/
void
db_saveDB_bu (void)
{
BYTE* buf;
wap_cvt_t obj;
UINT32 id;
UINT16 wt = DB_flag_endOfBuffer;
wap_cvt_init(&obj, WAP_CVT_ENCODE_SIZE, NULL, 0);
db_writeDB_bu(&obj);
if (obj.pos == 0)
return;
buf = OSConnectorAlloc(obj.pos + 2);
#ifndef HAS_SETJMP
if (buf == NULL)
return;
#endif
wap_cvt_init(&obj, WAP_CVT_ENCODE, buf, obj.pos + 2);
db_writeDB_bu(&obj);
wap_cvt_uint16(&obj, &wt);
id = 1023;
Storage_DeleteBlock(&store, id);
Storage_AllocateBlock(&store, obj.pos, &id);
Storage_Put(&store, 1023, 0, obj.pos, buf);
OSConnectorFree(buf);
}
/*
* Rebuilds database by reading backup data. Recursive;
* adds fields to an already existing record.
*
* If the function returns FALSE, the reconstruction has failed
* and the database has only been partially reconstructed.
*/
static BOOL
db_rebuildDB_bu (void)
{
ElementPtr toElement;
ElementPtr newElement;
RecordPtr toRec;
BYTE* buf;
wap_cvt_t obj;
DataUnion data;
UINT32 size;
short direction;
UINT8 error;
BOOL skipSubtree;
UINT16 len;
UINT16 flags;
BYTE key[DB_keyLen];
size = Storage_GetBlockSize(&store, 1023);
buf = OSConnectorAlloc(size);
Storage_Get(&store, 1023, 0, size, buf);
wap_cvt_init(&obj, WAP_CVT_DECODE, buf, size);
toRec = db_root;
toElement = (ElementPtr) toRec;
direction = GOING_DOWN;
skipSubtree = FALSE;
while (TRUE) {
/* Load element */
if (direction != GOING_UP) {
/* Read flags */
wap_cvt_uint16(&obj, &flags);
if (flags & DB_mask_endOfBuffer)
break;
if (flags & DB_mask_rootLevel)
skipSubtree = FALSE; /* Turn skipping off, if on; subtree finished. */
/* Read key */
if (flags & DB_mask_key) {
wap_cvt_static_bytevector(&obj, DB_keyLen, key);
if (flags & DB_mask_rootLevel) {
currentRef = DB_root;
currentRecord = db_root;
if (db_findField (key) != NULL)
skipSubtree = TRUE; /* A record with the same name has been saved
* using write-through after the backup. The
* other copy is newer, use it instead. */
}
}
/* Create and add new element */
if (skipSubtree) {
if ((flags & DB_mask_dataUse) == DB_flag_dataExternal) {
wap_cvt_uint16(&obj, &len);
obj.pos += len;
} else if ((flags & DB_mask_type) == DB_flag_int)
obj.pos += 4;
continue;
}
if ((flags & DB_mask_dataUse) == DB_flag_dataChild) /* ------- rec, set */
newElement = (ElementPtr) db_createNewRecord(toRec, key, flags,
direction == GOING_DOWN ? NULL : toElement, &error);
else { /* ---------------------------------------------------- int, str, mem */
/* Read data */
if ((flags & DB_mask_type) == DB_flag_int) /* int */
wap_cvt_uint32(&obj, &data.i);
else {
wap_cvt_uint16(&obj, &len);
if ((flags & DB_mask_type) == DB_flag_mem) { /* mem */
obj.pos -= 2; /* The length is to be included in the copy */
len += 2;
}
wap_cvt_bytevector(&obj, len, &data.s);
if ((flags & DB_mask_type) == DB_flag_mem) { /* mem */
*(UINT16*) data.s = len; /* Maybe different endian */
}
}
newElement = db_createNewElement(toRec, key, flags, data,
direction == GOING_DOWN ? NULL : toElement, NULL, &error);
}
if (error != DB_err_success)
return FALSE;
toElement = newElement;
}
/* Move to next element */
if (direction != GOING_UP && flags & DB_mask_hasChild) {
direction = GOING_DOWN;
toRec = (RecordPtr) toElement;
} else {
if (! (toElement->flags & DB_mask_lastElement2) || toRec == db_root) {
/* Note: check lastElement2 instead of lastElement since
* lastElement is recalculated in db_createNewRecord
* and db_createNewElement. lastElement2 is what is found
* on persistent memory, lastElement is the current status.*/
direction = GOING_RIGHT;
} else {
direction = GOING_UP;
toElement = toElement->next;
toRec = (RecordPtr) toRec->next;
}
}
}
OSConnectorFree(buf);
return TRUE;
}
/*
* Load database from persistent memory and rebuld structure.
* Only elements saved as backup are recovered.
*
* Return FALSE if there is nothing to load.
*/
static BOOL
db_loadDB_bu (void)
{
int dummy;
if (! Storage_Get(&store, 1023, 0, 0, &dummy))
return FALSE;
if (! db_rebuildDB_bu()) {
}
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -