📄 wipdb.c
字号:
/*
* Copyright (C) Ericsson Mobile Communications AB, 2000.
* Licensed to AU-System AB.
* All rights reserved.
*
* This software is covered by the license agreement between
* the end user and AU-System AB, and may be used and copied
* only in accordance with the terms of the said agreement.
*
* Neither Ericsson Mobile Communications AB nor AU-System AB
* assumes any responsibility or liability for any errors or inaccuracies in
* this software, or any consequential, incidental or indirect damage arising
* out of the use of the Generic WAP Client software.
*/
/*
* wipdb.c
*
* Simple database system.
*
*
* Created by Niclas Kellgren 000606.
*
* Revision history:
* 000703 NKE first version
* 000728 NKE data channel handling added
* 000804 NKE data channel handling updated
* 000807 NKE new data channel functions added
* 000809 NKE bugfixes
* 000817 NKE the object template is changed into a general object
* 000822 NKE constants for default fields changed
* 000906 NKE pattern matching added for getDataChannel
* 000914 NKE data channel handling moved to dbFunct.c
* 001013 NKE added persistent memory handling
* 001101 NKE write through rewritten
* 001114 NKE bugfixes and minor updates
* 001212 NKE corrected db_saveElement_wt
* 010219 NKE added db prefix to functions
* 010301 NKE major restructuring: changed underlying datastructure,
removed recursion and added db_getElementData
* 010302 NKE added persistent backup handling
* 010309 NKE added db_createStr and db_createMem
* 010314 NKE corrected db_setItem
* 010320 NKE corrected db_getElementData
* 010321 NKE minor corrections
* 010514 NKE added db_moveRecord and some enhancements to handle backup/write-through
* conflicts generated by the new function.
*/
#include "wipdb.h"
/*************
* Constants *
*************/
/* 16-bit flag field
pos len use
------------------
0-2 3 type
3 1 key present
4 1 backup
5 1 write-through
6-7 2 data use, compactation of 'type'; what the 'data' field is used for
rec,set = has child; int, ptr = internal data; str,mem = external data
8 1 last element in list
9 1 temporary flag. Signals last element in list and is a copy of bit 8,
during the backup process. Is also used as a signal to db_copyRecord.
Only one of the backup and write-through flags can be set at the same time.
If more flags are added, be careful to update all places where they are used.
Search for BOOKMARK:FLAGS.
*/
enum DB_ElementType {
DB_flag_rec = 0x0000,
DB_flag_set = 0x0001,
DB_flag_int = 0x0002,
DB_flag_str = 0x0003,
DB_flag_mem = 0x0004,
DB_flag_ptr = 0x0005,
DB_flag_key = 0x0008,
DB_flag_backup = 0x0010,
DB_flag_writeThrough = 0x0020,
DB_flag_dataChild = 0x0000,
DB_flag_dataInternal = 0x0040,
DB_flag_dataExternal = 0x0080,
DB_flag_lastElement = 0x0100,
DB_flag_temporary = 0x0200,
/* These flags are only used during backup. They reuse backup
* and writeThrough bits. */
DB_flag_lastElement2 = DB_flag_temporary,
DB_flag_hasChild = DB_flag_backup,
DB_flag_endOfBuffer = DB_flag_writeThrough,
DB_flag_rootLevel = DB_flag_lastElement,
/* The temporary flag is in db_copyRecord to treat str and
* mem pointers as ptr, i.e. only copy the pointer and not
* the data pointed out. */
DB_flag_move = DB_flag_temporary
};
enum MaskType {
DB_mask_type = 0x0007,
DB_mask_key = DB_flag_key,
DB_mask_backup = DB_flag_backup,
DB_mask_writeThrough = DB_flag_writeThrough,
DB_mask_persistent = DB_flag_backup | DB_flag_writeThrough,
DB_mask_dataUse = 0x00C0,
DB_mask_lastElement = DB_flag_lastElement,
DB_mask_lastElement2 = DB_flag_lastElement2,
DB_mask_hasChild = DB_flag_hasChild,
DB_mask_endOfBuffer = DB_flag_endOfBuffer,
DB_mask_rootLevel = DB_flag_rootLevel,
DB_mask_move = DB_flag_move
};
enum {
DB_max = DB_root-1, /* Maximum number of created records. */
DB_refHashSize = 32, /* Must be 2**n, unsigned int n, n > 0. */
DB_refHashMask = 32 - 1, /* Must be DB_refHashSize - 1. */
DB_keyLen = 6 /* Element key length. Must be at least 6 long. */
};
#define BUFSIZE 64 /* Size of persistent storage buffer. Must fit
* everything but the data and be multiple of 8. */
#define GOING_UP 0
#define GOING_DOWN 1
#define GOING_RIGHT 2
/*********
* Types *
*********/
typedef union {
UINT32 i;
void* p;
BYTE* s;
struct RecordStruct* r;
struct FieldStruct* f;
struct ElementStruct* e;
} DataUnion;
typedef struct ElementStruct* ElementPtr;
typedef struct ElementStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
} Element;
typedef struct ElementBlockStruct* ElementBlockPtr;
typedef struct ElementBlockStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
UINT32 blockId;
} ElementBlock;
typedef struct FieldStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
BYTE key[DB_keyLen];
} Field, *FieldPtr;
typedef struct FieldBlockStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
BYTE key[DB_keyLen];
UINT32 blockId;
} FieldBlock, *FieldBlockPtr;
typedef struct RecordStruct* RecordPtr;
typedef struct RecordStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
BYTE key[DB_keyLen];
UINT16 ref;
RecordPtr nextRec;
} Record;
typedef struct RecordBlockStruct* RecordBlockPtr;
typedef struct RecordBlockStruct {
ElementPtr next;
DataUnion data;
UINT16 flags;
BYTE key[DB_keyLen];
UINT16 ref;
RecordPtr nextRec;
UINT32 blockId;
} RecordBlock;
/*************
* Variables *
*************/
static RecordPtr* refHashTable; /* Hash table of liked list. RecordPtr.nextRec. */
static DB_ref recordNbr = 0; /* Record counter. Null reference is number 0. */
static RecordPtr db_root; /* Pointer to database root element. */
static RecordPtr currentRecord = NULL; /* Used by db_findField/db_deleteElement. */
static DB_ref currentRef = DB_null; /* Matches currentRecord. Used for caching. */
static ElementPtr currentElement; /* Carries information between several functions:
* from db_findField and db_deleteItem_ptr to
* db_findRecord, db_deleteElement. */
static ElementPtr prevElement; /* Carries information between several functions:
* from db_findField and db_deleteItem_ptr to
* db_deleteElement */
static StorageObject store; /* Main object used by storage.c */
static BOOL db_serviceMode; /* Is set to TRUE at startup or shutdown. Protects
* presistent memory during rebuild or close-down. */
static BOOL db_saveDataMode; /* When set to TRUE, db_deleteItem, db_deleteItem_ptr
* and db_clearRecord will not remove data poined out
* by a mem or str type. They are treated as ptr. */
static UINT32* idArr; /* Pointer to list of ids of elments on persistent
* memory. Only used during startup. */
/************************
* Forward declarations *
************************/
UINT8 db_deleteItem_ptr (RecordPtr rec, ElementPtr element);
static UINT32 db_getBlockId(ElementPtr element);
static void db_deleteElement_wt(RecordPtr parent, ElementPtr current, ElementPtr prev);
static BOOL db_saveElement_wt(RecordPtr parent, ElementPtr element, ElementPtr prev);
static BOOL db_loadDB_wt(void);
static void db_saveDB_bu(void);
static BOOL db_loadDB_bu (void);
/*************
* Functions *
*************/
/* ============================================ */
/* ========== Main functionality ============ */
/* ============================================ */
/*
This module contains the main functionality of the database.
It has five parts:
* help functions
* get
* set
* structural operations
* administration
FUNCTIONS
A list of the most important functions:
* help functions
db_findRecord
db_findField
db_findPrev_wt
db_addBelow
db_addAfter
db_deleteContent
db_deleteElement
* get
db_getElementData
db_getRef
db_getType
* set
db_createNewElement
db_setItem
db_setInt
db_setStr
db_setMem
db_setPtr
* structural operations
db_createNewRecord
db_createRecord
db_copyRecord
db_deleteItem
db_deleteItem_ptr
db_clearRecord
db_moveToFront
* administration
db_dbInit
db_dbTerminate
*/
/* ======== help functions ======== */
/*
* Search hash structure for record. Sets currentRecord and
* currentRef. Must be called prior to db_findField.
*/
RecordPtr
db_findRecord (DB_ref record)
{
RecordPtr rec;
if (record == currentRef) /* Speed up the most common case by caching */
return currentRecord;
rec = refHashTable[record & DB_refHashMask];
while (TRUE) {
if (rec == NULL) {
currentRef = DB_null;
break;
} else if (rec->ref == record) {
currentRef = record;
break;
} else
rec = rec->nextRec;
}
currentRecord = rec;
return currentRecord;
}
/*
* Find field named 'key' if present, else return NULL. Sets
* currentElement and prevElement. currentRecord defines the record
* to search and db_findRecord must be called first to set it.
*
* Can only be used on records, NOT on sets. Depends on currentRecord.
*/
FieldPtr
db_findField (const BYTE* key)
{
const BYTE* t;
BYTE* s;
prevElement = NULL;
if (key == NULL)
return NULL;
if ((currentRecord->flags & DB_mask_type) != DB_flag_rec)
return NULL;
currentElement = currentRecord->data.e;
while (currentElement != (ElementPtr) currentRecord) {
for (s = ((FieldPtr) currentElement)->key, t = key; *s == *t; s++, t++)
if (*s == '\0')
return (FieldPtr) currentElement;
prevElement = currentElement;
currentElement = currentElement->next;
}
currentElement = NULL;
return NULL;
}
/*
* Find the closest predecessor to currentElement with
* write-through flag set at root level.
*/
static ElementPtr
db_findPrev_wt (ElementPtr element)
{
ElementPtr prev;
ElementPtr current;
prev = NULL;
current = db_root->data.e;
while (current != (ElementPtr) db_root) {
if (current == element)
return prev;
if (current->flags & DB_mask_lastElement)
prev = current;
current = current->next;
}
return NULL;
}
/*
* Insert a new element 'newElement' below 'rec'.
*/
static void
db_addBelow (RecordPtr rec, ElementPtr newElement)
{
if (rec->data.e == (ElementPtr) rec)
newElement->flags |= DB_flag_lastElement;
else
newElement->flags &= ~ DB_flag_lastElement;
newElement->next = rec->data.e;
rec->data.e = newElement;
}
/*
* Insert a new element 'newElement' after 'element'.
*/
static void
db_addAfter (ElementPtr element, ElementPtr newElement)
{
if (element->flags & DB_mask_lastElement) {
element->flags &= ~ DB_flag_lastElement;
newElement->flags |= DB_flag_lastElement;
} else
newElement->flags &= ~ DB_flag_lastElement;
newElement->next = element->next;
element->next = newElement;
}
/*
* Remove 'current' element from reference hash list. Used by
* db_deleteContent and db_deleteElement;
*/
static void
db_removeElementRef (ElementPtr current)
{
RecordPtr rec;
RecordPtr* prevPtr;
prevPtr = refHashTable + (((RecordPtr) current)->ref & DB_refHashMask);
rec = *prevPtr;
while (rec->ref != ((RecordPtr) current)->ref) {
prevPtr = &rec->nextRec;
rec = rec->nextRec;
}
*prevPtr = rec->nextRec;
}
/*
* Delete entire subtree, excluding 'rec' root element. Does
* not update rec->data.e. Preserves ref hash structure.
*
* Elements are removed bottom up non-recursively. If 'saveData'
* is TRUE, then external data is not deleted.
*
* This is a utility function, intended to be used by other
* functions. Use db_clearRecord if you want to delete all
* fields of a record and preserv write-through consistency.
*/
static void
db_deleteContent (RecordPtr rec, BOOL saveData)
{
ElementPtr current;
ElementPtr prev;
UINT32 id;
current = rec->data.e;
while (current != (ElementPtr) rec) {
switch (current->flags & DB_mask_dataUse) {
case DB_flag_dataChild:
if (current->data.e != current) {
/* This element has a child and cannot be removed (yet) */
current = current->data.e;
continue;
} else
db_removeElementRef(current);
break;
case DB_flag_dataExternal:
if (! saveData)
OSConnectorFree(current->data.s);
}
if (current->flags & DB_mask_writeThrough && ! db_serviceMode) {
id = db_getBlockId(current);
Storage_DeleteBlock(&store, id);
}
prev = current;
current = current->next;
if (prev->flags & DB_mask_lastElement) /* Finished one level */
current->data.e = current; /* Mark this record as empty, i.e. deletable */
OSConnectorFree(prev);
}
}
/*
* Delete currentElement. If it is a record or set, use
* db_deleteContent to remove the subtree. Preserves ref hash
* structure. If 'saveData' is TRUE, then external data is
* not deleted.
*
* Depends on currentElement, currentRecord, prevElement. If
* you change currentRecord, don't forget to update currentRef.
*/
static void
db_deleteElement (BOOL saveData)
{
if (currentElement->flags & DB_mask_writeThrough && ! db_serviceMode)
db_deleteElement_wt(currentRecord, currentElement, prevElement);
if (prevElement == NULL)
currentRecord->data.e = currentElement->next;
else {
prevElement->next = currentElement->next;
if (prevElement->next == (ElementPtr) currentRecord)
prevElement->flags |= DB_flag_lastElement;
}
switch (currentElement->flags & DB_mask_dataUse) {
case DB_flag_dataChild:
db_removeElementRef(currentElement);
if (currentElement->data.e != currentElement)
db_deleteContent((RecordPtr) currentElement, saveData);
break;
case DB_flag_dataExternal:
if (! saveData)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -