⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wipdb.c

📁 是一个手机功能的模拟程序
💻 C
📖 第 1 页 / 共 4 页
字号:
		*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 + -