зеркало из https://github.com/mozilla/pjs.git
updating license to xPL 1.1
This commit is contained in:
Родитель
da2f224dee
Коммит
a7b7757e6d
|
@ -1,553 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that add or remove things to the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "tdbdebug.h"
|
||||
#include "prprf.h"
|
||||
static PRBool makedots = PR_FALSE; /* If true, print out dot-graphs of
|
||||
every step of balancing, to help my
|
||||
poor mind debug. */
|
||||
static int dotcount = 0;
|
||||
#endif
|
||||
|
||||
PRStatus TDBAdd(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
PRStatus status = PR_FAILURE;
|
||||
TDBRecord* record;
|
||||
TDBPtr position;
|
||||
PRInt32 tree;
|
||||
PRInt32 i;
|
||||
PRInt64 cmp;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
|
||||
/* First, see if we already have this triple around... */
|
||||
tree = 0; /* Hard-coded knowledge that tree zero does
|
||||
things in [0], [1], [2] order. ### */
|
||||
|
||||
position = db->roots[tree];
|
||||
while (position) {
|
||||
record = tdbLoadRecord(db, position);
|
||||
PR_ASSERT(record);
|
||||
if (!record) {
|
||||
goto DONE;
|
||||
}
|
||||
PR_ASSERT(record->position == position);
|
||||
if (record->position != position) {
|
||||
goto DONE;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cmp = tdbCompareNodes(triple[i], record->data[i]);
|
||||
if (cmp < 0) {
|
||||
position = record->entry[tree].child[0];
|
||||
break;
|
||||
} else if (cmp > 0) {
|
||||
position = record->entry[tree].child[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (position == record->position) {
|
||||
/* This means that our new entry exactly matches this one. So,
|
||||
we're done. */
|
||||
status = PR_SUCCESS;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
tdbThrowOutCursorCaches(db);
|
||||
record = tdbAllocateRecord(db, triple);
|
||||
if (record == NULL) {
|
||||
goto DONE;
|
||||
}
|
||||
for (tree=0 ; tree<NUMTREES ; tree++) {
|
||||
status = tdbAddToTree(db, record, tree);
|
||||
if (status != PR_SUCCESS) goto DONE;
|
||||
}
|
||||
status = tdbQueueMatchingCallbacks(db, record, TDBACTION_ADDED);
|
||||
|
||||
|
||||
DONE:
|
||||
if (status == PR_SUCCESS) {
|
||||
tdbFlush(db);
|
||||
} else {
|
||||
tdbThrowAwayUnflushedChanges(db);
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBReplace(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
/* Write me correctly!!! This works, but is inefficient. ### */
|
||||
|
||||
PRStatus status;
|
||||
TDBNodeRange range[3];
|
||||
range[0].min = triple[0];
|
||||
range[0].max = triple[0];
|
||||
range[1].min = triple[1];
|
||||
range[1].max = triple[1];
|
||||
range[2].min = NULL;
|
||||
range[2].max = NULL;
|
||||
status = TDBRemove(db, range);
|
||||
if (status == PR_SUCCESS) {
|
||||
status = TDBAdd(db, triple);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
PRStatus TDBRemove(TDB* db, TDBNodeRange range[3])
|
||||
{
|
||||
/* This could definitely be faster. We're querying the database for
|
||||
a matching item, then we go search for it again when we delete it.
|
||||
The two operations probably ought to be merged. ### */
|
||||
PRStatus status;
|
||||
TDBCursor* cursor;
|
||||
TDBTriple* triple;
|
||||
TDBPtr position;
|
||||
TDBRecord* record;
|
||||
PRInt32 tree;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
tdbThrowOutCursorCaches(db);
|
||||
for (;;) {
|
||||
cursor = tdbQueryNolock(db, range, NULL);
|
||||
if (!cursor) goto FAIL;
|
||||
triple = tdbGetResultNolock(cursor);
|
||||
if (triple) {
|
||||
/* Probably ought to play refcnt games with this to prevent it
|
||||
from being removed from the cache. ### */
|
||||
position = cursor->lasthit->position;
|
||||
}
|
||||
tdbFreeCursorNolock(cursor);
|
||||
cursor = NULL;
|
||||
if (!triple) {
|
||||
/* No more hits; all done. */
|
||||
break;
|
||||
}
|
||||
record = tdbLoadRecord(db, position);
|
||||
if (!record) goto FAIL;
|
||||
|
||||
for (tree=0 ; tree<NUMTREES ; tree++) {
|
||||
status = tdbRemoveFromTree(db, record, tree);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
}
|
||||
status = tdbAddToTree(db, record, -1);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
status = tdbQueueMatchingCallbacks(db, record, TDBACTION_REMOVED);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
tdbFlush(db);
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_SUCCESS;
|
||||
FAIL:
|
||||
tdbThrowAwayUnflushedChanges(db);
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef MIN
|
||||
#undef MIN
|
||||
#endif
|
||||
#ifdef MAX
|
||||
#undef MAX
|
||||
#endif
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static PRBool
|
||||
rotateOnce(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRInt32 dir)
|
||||
{
|
||||
PRInt32 otherdir = 1 - dir;
|
||||
PRBool heightChanged;
|
||||
TDBPtr otherptr;
|
||||
TDBRecord* other;
|
||||
PRInt8 oldrootbal;
|
||||
PRInt8 otherbal;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (makedots) {
|
||||
char* filename;
|
||||
filename = PR_smprintf("/tmp/balance%d-%d.dot", tree, dotcount++);
|
||||
TDBMakeDotGraph(db, filename, tree);
|
||||
PR_smprintf_free(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
PR_ASSERT(dir == 0 || dir == 1);
|
||||
if (dir != 0 && dir != 1) return PR_FALSE;
|
||||
|
||||
otherptr = oldroot->entry[tree].child[otherdir];
|
||||
if (otherptr == 0) {
|
||||
tdbMarkCorrupted(db);
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
other = tdbLoadRecord(db, otherptr);
|
||||
heightChanged = (other->entry[tree].balance != 0);
|
||||
|
||||
*rootptr = otherptr;
|
||||
|
||||
oldroot->entry[tree].child[otherdir] = other->entry[tree].child[dir];
|
||||
other->entry[tree].child[dir] = oldroot->position;
|
||||
|
||||
/* update balances */
|
||||
oldrootbal = oldroot->entry[tree].balance;
|
||||
otherbal = other->entry[tree].balance;
|
||||
if (dir == 0) {
|
||||
oldrootbal -= 1 + MAX(otherbal, 0);
|
||||
otherbal -= 1 - MIN(oldrootbal, 0);
|
||||
} else {
|
||||
oldrootbal += 1 - MIN(otherbal, 0);
|
||||
otherbal += 1 + MAX(oldrootbal, 0);
|
||||
}
|
||||
oldroot->entry[tree].balance = oldrootbal;
|
||||
other->entry[tree].balance = otherbal;
|
||||
|
||||
oldroot->dirty = PR_TRUE;
|
||||
other->dirty = PR_TRUE;
|
||||
|
||||
return heightChanged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
rotateTwice(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRInt32 dir)
|
||||
{
|
||||
PRInt32 otherdir = 1 - dir;
|
||||
TDBRecord* child;
|
||||
|
||||
PR_ASSERT(dir == 0 || dir == 1);
|
||||
if (dir != 0 && dir != 1) return;
|
||||
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[otherdir]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return;
|
||||
|
||||
rotateOnce(db, &(oldroot->entry[tree].child[otherdir]), child, tree,
|
||||
otherdir);
|
||||
oldroot->dirty = PR_TRUE;
|
||||
rotateOnce(db, rootptr, oldroot, tree, dir);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static PRStatus
|
||||
balance(TDB* db, TDBPtr* rootptr, TDBRecord* oldroot, PRInt32 tree,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRInt8 oldbalance;
|
||||
TDBRecord* child;
|
||||
*heightchange = PR_FALSE;
|
||||
|
||||
oldbalance = oldroot->entry[tree].balance;
|
||||
|
||||
if (oldbalance < -1) { /* need a right rotation */
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[0]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return PR_FAILURE;
|
||||
if (child->entry[tree].balance == 1) {
|
||||
rotateTwice(db, rootptr, oldroot, tree, 1); /* double RL rotation
|
||||
needed */
|
||||
*heightchange = PR_TRUE;
|
||||
} else { /* single RR rotation needed */
|
||||
*heightchange = rotateOnce(db, rootptr, oldroot, tree, 1);
|
||||
}
|
||||
} else if (oldbalance > 1) { /* need a left rotation */
|
||||
child = tdbLoadRecord(db, oldroot->entry[tree].child[1]);
|
||||
PR_ASSERT(child);
|
||||
if (child == NULL) return PR_FAILURE;
|
||||
if (child->entry[tree].balance == -1) {
|
||||
rotateTwice(db, rootptr, oldroot, tree, 0); /* double LR rotation
|
||||
needed */
|
||||
*heightchange = PR_TRUE;
|
||||
} else { /* single LL rotation needed */
|
||||
*heightchange = rotateOnce(db, rootptr, oldroot, tree, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus doAdd(TDB* db, TDBRecord* record, PRInt32 tree,
|
||||
PRInt32 comparerule, TDBPtr* rootptr,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRBool increase = PR_FALSE;
|
||||
PRInt64 cmp;
|
||||
PRInt32 kid;
|
||||
PRStatus status;
|
||||
TDBRecord* cur;
|
||||
TDBPtr origptr;
|
||||
if (*rootptr == 0) {
|
||||
*rootptr = record->position;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
cur = tdbLoadRecord(db, *rootptr);
|
||||
if (!cur) return PR_FAILURE;
|
||||
cmp = tdbCompareRecords(record, cur, comparerule);
|
||||
PR_ASSERT(cmp != 0); /* We carefully should never insert a
|
||||
record that we already have. */
|
||||
if (cmp == 0) return PR_FAILURE;
|
||||
kid = (cmp < 0) ? 0 : 1;
|
||||
origptr = cur->entry[tree].child[kid];
|
||||
status = doAdd(db, record, tree, comparerule,
|
||||
&(cur->entry[tree].child[kid]), &increase);
|
||||
if (origptr != cur->entry[tree].child[kid]) {
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (increase) {
|
||||
cur->entry[tree].balance += (kid == 0 ? -1 : 1);
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (status != PR_SUCCESS) return status;
|
||||
if (increase && cur->entry[tree].balance != 0) {
|
||||
status = balance(db, rootptr, cur, tree, &increase);
|
||||
*heightchange = ! increase; /* If we did a rotate that absorbed
|
||||
the height change, then we don't want
|
||||
to propagate it on up. */
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbAddToTree(TDB* db, TDBRecord* record, PRInt32 tree)
|
||||
{
|
||||
TDBTreeEntry* entry;
|
||||
PRBool checklinks;
|
||||
PRBool ignore;
|
||||
PRStatus status = PR_SUCCESS;
|
||||
TDBPtr* rootptr;
|
||||
TDBPtr origroot;
|
||||
PRInt32 comparerule = tree;
|
||||
|
||||
PR_ASSERT(record != NULL);
|
||||
if (record == NULL) return PR_FAILURE;
|
||||
|
||||
if (tree == -1) {
|
||||
tree = 0;
|
||||
rootptr = &(db->freeroot);
|
||||
} else {
|
||||
PR_ASSERT(tree >= 0 && tree < NUMTREES);
|
||||
if (tree < 0 || tree >= NUMTREES) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
rootptr = &(db->roots[tree]);
|
||||
}
|
||||
|
||||
/* Check that this record does not seem to be already in this tree. */
|
||||
entry = record->entry + tree;
|
||||
checklinks = (entry->child[0] == 0 &&
|
||||
entry->child[1] == 0 &&
|
||||
entry->balance == 0);
|
||||
PR_ASSERT(checklinks);
|
||||
if (!checklinks) return PR_FAILURE;
|
||||
|
||||
origroot = *rootptr;
|
||||
if (origroot == 0) {
|
||||
*rootptr = record->position;
|
||||
} else {
|
||||
status = doAdd(db, record, tree, comparerule, rootptr, &ignore);
|
||||
}
|
||||
if (origroot != *rootptr) {
|
||||
db->rootdirty = PR_TRUE;
|
||||
}
|
||||
db->dirty = PR_TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus doRemove(TDB* db, TDBRecord* record, PRInt32 tree,
|
||||
PRInt32 comparerule, TDBPtr* rootptr,
|
||||
TDBPtr** foundleafptr, TDBRecord** foundleaf,
|
||||
PRBool* heightchange)
|
||||
{
|
||||
PRStatus status;
|
||||
TDBRecord* cur;
|
||||
TDBPtr* leafptr;
|
||||
TDBRecord* leaf;
|
||||
PRInt32 kid;
|
||||
TDBPtr origptr;
|
||||
TDBPtr kid0;
|
||||
TDBPtr kid1;
|
||||
PRInt64 cmp;
|
||||
PRBool decrease = PR_FALSE;
|
||||
PRInt32 tmp;
|
||||
PRInt8 tmpbal;
|
||||
PRInt32 i;
|
||||
PR_ASSERT(*rootptr != 0);
|
||||
if (*rootptr == 0) return PR_FAILURE;
|
||||
cur = tdbLoadRecord(db, *rootptr);
|
||||
if (!cur) return PR_FAILURE;
|
||||
if (record == NULL) {
|
||||
cur->dirty = PR_TRUE; /* Oh, what a bad, bad hack. Need a better
|
||||
way to make sure not to miss a parent
|
||||
pointer that we've changed. ### */
|
||||
}
|
||||
kid0 = cur->entry[tree].child[0];
|
||||
kid1 = cur->entry[tree].child[1];
|
||||
if (record == NULL) {
|
||||
/* We're looking for the smallest possible leaf node. */
|
||||
PR_ASSERT(foundleafptr != NULL && foundleaf != NULL);
|
||||
if (kid0) cmp = -1;
|
||||
else {
|
||||
cmp = 0;
|
||||
*foundleafptr = rootptr;
|
||||
*foundleaf = cur;
|
||||
}
|
||||
} else {
|
||||
PR_ASSERT(foundleafptr == NULL && foundleaf == NULL);
|
||||
cmp = tdbCompareRecords(record, cur, comparerule);
|
||||
}
|
||||
if (cmp == 0) {
|
||||
PR_ASSERT(record == cur || record == NULL);
|
||||
if (record != cur && record != NULL) return PR_FAILURE;
|
||||
if (kid0 == 0 && kid1 == 0) {
|
||||
/* We're a leaf node. */
|
||||
*rootptr = 0;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
} else if (kid0 == 0 || kid1 == 0) {
|
||||
/* Replace us with our single child. */
|
||||
*rootptr = kid0 != 0 ? kid0 : kid1;
|
||||
cur->entry[tree].child[0] = 0;
|
||||
cur->entry[tree].child[1] = 0;
|
||||
cur->entry[tree].balance = 0;
|
||||
cur->dirty = PR_TRUE;
|
||||
*heightchange = PR_TRUE;
|
||||
return PR_SUCCESS;
|
||||
} else {
|
||||
/* Ick. We're a node in the middle of the tree. Find who to
|
||||
replace us with. */
|
||||
kid = 1; /* Informs balancing code later that we are
|
||||
taking things from the right subtree. */
|
||||
status = doRemove(db, NULL, tree, comparerule,
|
||||
&(cur->entry[tree].child[1]),
|
||||
&leafptr, &leaf, &decrease);
|
||||
/* Swap us in the tree with leaf. Don't use the kid0/kid1
|
||||
variables any more, as they may no longer be valid. */
|
||||
|
||||
if (record != NULL) {
|
||||
leaf->entry[tree].child[0] = cur->entry[tree].child[0];
|
||||
leaf->entry[tree].child[1] = cur->entry[tree].child[1];
|
||||
leaf->entry[tree].balance = cur->entry[tree].balance;
|
||||
cur->entry[tree].child[0] = 0;
|
||||
cur->entry[tree].child[1] = 0;
|
||||
cur->entry[tree].balance = 0;
|
||||
cur->dirty = PR_TRUE;
|
||||
*rootptr = leaf->position;
|
||||
cur = leaf;
|
||||
cur->dirty = PR_TRUE;
|
||||
} else {
|
||||
*foundleaf = cur;
|
||||
*foundleafptr = leafptr;
|
||||
PR_ASSERT(**foundleafptr == leaf->position);
|
||||
*leafptr = cur->position;
|
||||
*rootptr = leaf->position;
|
||||
for (i=0 ; i<2 ; i++) {
|
||||
tmp = leaf->entry[tree].child[i];
|
||||
leaf->entry[tree].child[i] = cur->entry[tree].child[i];
|
||||
cur->entry[tree].child[i] = tmp;
|
||||
}
|
||||
tmpbal = leaf->entry[tree].balance;
|
||||
leaf->entry[tree].balance = cur->entry[tree].balance;
|
||||
cur->entry[tree].balance = tmpbal;
|
||||
cur->dirty = PR_TRUE;
|
||||
leaf->dirty = PR_TRUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kid = (cmp < 0) ? 0 : 1;
|
||||
origptr = cur->entry[tree].child[kid];
|
||||
status = doRemove(db, record, tree, comparerule,
|
||||
&(cur->entry[tree].child[kid]), foundleafptr,
|
||||
foundleaf, &decrease);
|
||||
if (origptr != cur->entry[tree].child[kid]) {
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (decrease) {
|
||||
cur->entry[tree].balance += (kid == 0 ? 1 : -1);
|
||||
cur->dirty = PR_TRUE;
|
||||
}
|
||||
if (status != PR_SUCCESS) return status;
|
||||
if (decrease) {
|
||||
if (cur->entry[tree].balance != 0) {
|
||||
status = balance(db, rootptr, cur, tree, &decrease);
|
||||
*heightchange = decrease;
|
||||
} else {
|
||||
*heightchange = PR_TRUE;
|
||||
}
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
PRStatus tdbRemoveFromTree(TDB* db, TDBRecord* record, PRInt32 tree)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 comparerule = tree;
|
||||
TDBPtr* rootptr;
|
||||
TDBPtr origroot;
|
||||
PRBool ignore;
|
||||
|
||||
PR_ASSERT(record != NULL);
|
||||
if (record == NULL) return PR_FAILURE;
|
||||
if (tree == -1) {
|
||||
tree = 0;
|
||||
rootptr = &(db->freeroot);
|
||||
} else {
|
||||
PR_ASSERT(tree >= 0 && tree < NUMTREES);
|
||||
if (tree < 0 || tree >= NUMTREES) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
rootptr = &(db->roots[tree]);
|
||||
}
|
||||
origroot = *rootptr;
|
||||
PR_ASSERT(origroot != 0);
|
||||
if (origroot == 0) return PR_FAILURE;
|
||||
status = doRemove(db, record, tree, comparerule, rootptr, NULL, NULL,
|
||||
&ignore);
|
||||
|
||||
if (origroot != *rootptr) {
|
||||
db->rootdirty = PR_TRUE;
|
||||
}
|
||||
db->dirty = PR_TRUE;
|
||||
record->dirty = PR_TRUE;
|
||||
return status;
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that query things from the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
static void
|
||||
tdbFreeCallbackInfo(TDBCallbackInfo* info)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (info->range[i].min != NULL) TDBFreeNode(info->range[i].min);
|
||||
if (info->range[i].max != NULL) TDBFreeNode(info->range[i].max);
|
||||
}
|
||||
PR_Free(info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tdbFreePendingCall(TDBPendingCall* call)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (call->triple.data[i] != NULL) {
|
||||
TDBFreeNode(call->triple.data[i]);
|
||||
}
|
||||
}
|
||||
PR_Free(call);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tdbCallbackThread(void* closure)
|
||||
{
|
||||
PRStatus status;
|
||||
TDB* db = (TDB*) closure;
|
||||
|
||||
PRLock* mutex = db->mutex;
|
||||
PRCondVar* cvar = db->callbackcvargo;
|
||||
|
||||
TDBPendingCall* call;
|
||||
TDBPendingCall* tmp;
|
||||
PR_Lock(mutex);
|
||||
while (1) {
|
||||
while (db->firstpendingcall == NULL) {
|
||||
db->callbackidle = PR_TRUE;
|
||||
PR_NotifyAllCondVar(db->callbackcvaridle); /* Inform anyone who
|
||||
cares that we are
|
||||
idle. */
|
||||
if (db->killcallbackthread) {
|
||||
/* This db is being closed; go away now. */
|
||||
PR_Unlock(db->mutex);
|
||||
return;
|
||||
}
|
||||
status = PR_WaitCondVar(cvar, PR_INTERVAL_NO_TIMEOUT);
|
||||
db->callbackidle = PR_FALSE;
|
||||
}
|
||||
call = db->firstpendingcall;
|
||||
db->firstpendingcall = NULL;
|
||||
db->lastpendingcall = NULL;
|
||||
PR_Unlock(db->mutex);
|
||||
while (call) {
|
||||
(*call->func)(db, call->closure, &(call->triple), call->action);
|
||||
tmp = call;
|
||||
call = call->next;
|
||||
tdbFreePendingCall(tmp);
|
||||
}
|
||||
PR_Lock(db->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbQueueMatchingCallbacks(TDB* db, TDBRecord* record,
|
||||
PRInt32 action)
|
||||
{
|
||||
TDBCallbackInfo* info;
|
||||
TDBPendingCall* call;
|
||||
PRInt32 i;
|
||||
for (info = db->firstcallback ; info ; info = info->nextcallback) {
|
||||
if (tdbMatchesRange(record, info->range)) {
|
||||
call = PR_NEWZAP(TDBPendingCall);
|
||||
if (!call) return PR_FAILURE;
|
||||
call->func = info->func;
|
||||
call->closure = info->closure;
|
||||
call->action = action;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
call->triple.data[i] = tdbNodeDup(record->data[i]);
|
||||
if (call->triple.data[i] == NULL) {
|
||||
tdbFreePendingCall(call);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (db->lastpendingcall) {
|
||||
db->lastpendingcall->next = call;
|
||||
}
|
||||
db->lastpendingcall = call;
|
||||
if (db->firstpendingcall == NULL) {
|
||||
db->firstpendingcall = call;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (db->firstpendingcall != NULL) {
|
||||
/* Kick the background thread. */
|
||||
PR_NotifyAllCondVar(db->callbackcvargo);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBAddCallback(TDB* db, TDBNodeRange range[3],
|
||||
TDBCallbackFunction func, void* closure)
|
||||
{
|
||||
TDBCallbackInfo* info;
|
||||
PRInt32 i;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
info = PR_NEWZAP(TDBCallbackInfo);
|
||||
if (!info) return PR_FAILURE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min) {
|
||||
info->range[i].min = tdbNodeDup(range[i].min);
|
||||
if (info->range[i].min == NULL) goto FAIL;
|
||||
}
|
||||
if (range[i].max) {
|
||||
info->range[i].max = tdbNodeDup(range[i].max);
|
||||
if (info->range[i].max == NULL) goto FAIL;
|
||||
}
|
||||
}
|
||||
info->func = func;
|
||||
info->closure = closure;
|
||||
info->nextcallback = db->firstcallback;
|
||||
PR_Lock(db->mutex);
|
||||
db->firstcallback = info;
|
||||
PR_Unlock(db->mutex);
|
||||
return PR_SUCCESS;
|
||||
|
||||
FAIL:
|
||||
tdbFreeCallbackInfo(info);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBRemoveCallback(TDB* db, TDBNodeRange range[3],
|
||||
TDBCallbackFunction func, void* closure)
|
||||
{
|
||||
PRStatus status = PR_FAILURE;
|
||||
TDBCallbackInfo** ptr;
|
||||
TDBCallbackInfo* info;
|
||||
PRInt32 i;
|
||||
PRBool match;
|
||||
PR_ASSERT(db != NULL);
|
||||
if (db == NULL) return PR_FAILURE;
|
||||
PR_Lock(db->mutex);
|
||||
for (ptr = &(db->firstcallback) ; *ptr ; ptr = &((*ptr)->nextcallback)) {
|
||||
info = *ptr;
|
||||
if (info->func == func && info->closure == closure) {
|
||||
match = PR_TRUE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min != info->range[i].min &&
|
||||
(range[i].min == NULL || info->range[i].min == NULL ||
|
||||
tdbCompareNodes(range[i].min, info->range[i].min) != 0)) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
if (range[i].max != info->range[i].max &&
|
||||
(range[i].max == NULL || info->range[i].max == NULL ||
|
||||
tdbCompareNodes(range[i].max, info->range[i].max) != 0)) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
*ptr = info->nextcallback;
|
||||
tdbFreeCallbackInfo(info);
|
||||
status = PR_SUCCESS;
|
||||
|
||||
/* We now make sure that we have no outstanding callbacks
|
||||
queued up to the callback we just removed. It would be
|
||||
real bad to call that callback after we return. So, we
|
||||
make sure to call it now. */
|
||||
while (db->firstpendingcall) {
|
||||
PR_NotifyAllCondVar(db->callbackcvargo);
|
||||
PR_WaitCondVar(db->callbackcvaridle,
|
||||
PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,503 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Routines that query things from the database. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
/* Argh, this same static array appears in record.c. Don't do that. ### */
|
||||
|
||||
static int key[NUMTREES][3] = {
|
||||
{0, 1, 2},
|
||||
{1, 0, 2},
|
||||
{2, 1, 0},
|
||||
{1, 2, 0}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
freeParentChain(TDBCursor* cursor)
|
||||
{
|
||||
TDBParentChain* tmp;
|
||||
while (cursor->parent) {
|
||||
tmp = cursor->parent->next;
|
||||
cursor->parent->record->refcnt--;
|
||||
PR_ASSERT(cursor->parent->record->refcnt >= 0);
|
||||
PR_Free(cursor->parent);
|
||||
cursor->parent = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PRStatus
|
||||
moveCursorForward(TDBCursor* cursor)
|
||||
{
|
||||
TDBRecord* cur;
|
||||
PRBool found;
|
||||
PRInt32 fwd;
|
||||
PRInt32 rev;
|
||||
TDBPtr ptr;
|
||||
PRInt32 tree = cursor->tree;
|
||||
TDBParentChain* parent;
|
||||
TDBParentChain* tmp;
|
||||
|
||||
cur = cursor->cur;
|
||||
|
||||
PR_ASSERT(cur != NULL);
|
||||
if (cur == NULL) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
cur->refcnt--;
|
||||
PR_ASSERT(cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
fwd = cursor->reverse ? 0 : 1;
|
||||
rev = cursor->reverse ? 1 : 0;
|
||||
ptr = cur->entry[tree].child[fwd];
|
||||
if (ptr != 0) {
|
||||
do {
|
||||
cur->refcnt++;
|
||||
parent = PR_NEWZAP(TDBParentChain);
|
||||
parent->record = cur;
|
||||
parent->next = cursor->parent;
|
||||
cursor->parent = parent;
|
||||
cur = tdbLoadRecord(cursor->db, ptr);
|
||||
PR_ASSERT(cur);
|
||||
if (!cur) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
ptr = cur->entry[tree].child[rev];
|
||||
} while (ptr != 0);
|
||||
} else {
|
||||
found = PR_FALSE;
|
||||
while (cursor->parent) {
|
||||
ptr = cur->position;
|
||||
cur = cursor->parent->record;
|
||||
cur->refcnt--;
|
||||
PR_ASSERT(cur->refcnt >= 0);
|
||||
tmp = cursor->parent->next;
|
||||
PR_Free(cursor->parent);
|
||||
cursor->parent = tmp;
|
||||
if (cur->entry[tree].child[rev] == ptr) {
|
||||
found = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
if (cur->entry[tree].child[fwd] != ptr) {
|
||||
tdbMarkCorrupted(cursor->db);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
}
|
||||
if (!found) cur = NULL;
|
||||
}
|
||||
if (cur) cur->refcnt++;
|
||||
cursor->cur = cur;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static PRStatus findFirstNode(TDBCursor* cursor, TDBNodeRange range[3])
|
||||
{
|
||||
PRInt32 fwd;
|
||||
PRInt32 rev;
|
||||
PRBool reverse;
|
||||
PRInt32 tree;
|
||||
TDBParentChain* parent;
|
||||
TDBPtr curptr;
|
||||
TDBRecord* cur;
|
||||
PRInt64 cmp;
|
||||
|
||||
reverse = cursor->reverse;
|
||||
fwd = reverse ? 0 : 1;
|
||||
rev = reverse ? 1 : 0;
|
||||
tree = cursor->tree;
|
||||
|
||||
freeParentChain(cursor);
|
||||
|
||||
curptr = cursor->db->roots[tree];
|
||||
cur = NULL;
|
||||
while (curptr) {
|
||||
if (cur) {
|
||||
cur->refcnt++;
|
||||
parent = PR_NEWZAP(TDBParentChain);
|
||||
parent->record = cur;
|
||||
parent->next = cursor->parent;
|
||||
cursor->parent = parent;
|
||||
}
|
||||
cur = tdbLoadRecord(cursor->db, curptr);
|
||||
PR_ASSERT(cur);
|
||||
if (!cur) return PR_FAILURE;
|
||||
cmp = tdbCompareToRange(cur, range, tree);
|
||||
if (reverse) cmp = -cmp;
|
||||
if (cmp >= 0) {
|
||||
curptr = cur->entry[tree].child[rev];
|
||||
} else {
|
||||
curptr = cur->entry[tree].child[fwd];
|
||||
}
|
||||
}
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
}
|
||||
cursor->cur = cur;
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt++;
|
||||
}
|
||||
while (cursor->cur != NULL &&
|
||||
tdbCompareToRange(cursor->cur, range, tree) < 0) {
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TDBCursor* tdbQueryNolock(TDB* db, TDBNodeRange range[3],
|
||||
TDBSortSpecification* sortspec)
|
||||
{
|
||||
PRInt32 rangescore[3];
|
||||
PRInt32 tree;
|
||||
PRInt32 curscore;
|
||||
PRInt32 bestscore = -1;
|
||||
PRInt32 i;
|
||||
TDBCursor* result;
|
||||
PRBool reverse;
|
||||
PRStatus status;
|
||||
|
||||
result = PR_NEWZAP(TDBCursor);
|
||||
if (!result) return NULL;
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min) {
|
||||
result->range[i].min = tdbNodeDup(range[i].min);
|
||||
if (result->range[i].min == NULL) goto FAIL;
|
||||
}
|
||||
if (range[i].max) {
|
||||
result->range[i].max = tdbNodeDup(range[i].max);
|
||||
if (result->range[i].max == NULL) goto FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
for (tree = 0 ; tree < NUMTREES ; tree++) {
|
||||
if (key[tree][0] == sortspec->keyorder[0] &&
|
||||
key[tree][1] == sortspec->keyorder[1] &&
|
||||
key[tree][2] == sortspec->keyorder[2]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tree >= NUMTREES) {
|
||||
/* The passed in keyorder was not valid (which, in fact, is the usual
|
||||
case). Go find the best tree to use. */
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
rangescore[i] = 0;
|
||||
if (range[i].min != NULL || range[i].max != NULL) {
|
||||
/* Hey, some limitations were specified, we like this key
|
||||
some. */
|
||||
rangescore[i]++;
|
||||
if (range[i].min != NULL && range[i].max != NULL) {
|
||||
/* Ooh, we were given both minimum and maximum, that's
|
||||
better than only getting one.*/
|
||||
rangescore[i]++;
|
||||
if (tdbCompareNodes(range[i].min, range[i].max) == 0) {
|
||||
/* Say! This key was exactly specified. We like it
|
||||
best. */
|
||||
rangescore[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
curscore = rangescore[key[i][0]] * 100 +
|
||||
rangescore[key[i][1]] * 10 +
|
||||
rangescore[key[i][2]];
|
||||
if (bestscore < curscore) {
|
||||
bestscore = curscore;
|
||||
tree = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reverse = sortspec != NULL && sortspec->reverse;
|
||||
|
||||
result->reverse = reverse;
|
||||
result->db = db;
|
||||
result->tree = tree;
|
||||
status = findFirstNode(result, range);
|
||||
if (status != PR_SUCCESS) goto FAIL;
|
||||
|
||||
result->nextcursor = db->firstcursor;
|
||||
if (result->nextcursor) {
|
||||
result->nextcursor->prevcursor = result;
|
||||
}
|
||||
db->firstcursor = result;
|
||||
|
||||
tdbFlush(db);
|
||||
return result;
|
||||
|
||||
|
||||
FAIL:
|
||||
tdbFreeCursorNolock(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TDBCursor* TDBQuery(TDB* db, TDBNodeRange range[3],
|
||||
TDBSortSpecification* sortspec)
|
||||
{
|
||||
TDBCursor* result;
|
||||
PR_Lock(db->mutex);
|
||||
result = tdbQueryNolock(db, range, sortspec);
|
||||
PR_Unlock(db->mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PRStatus tdbFreeCursorNolock(TDBCursor* cursor)
|
||||
{
|
||||
PRInt32 i;
|
||||
TDB* db;
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return PR_FAILURE;
|
||||
db = cursor->db;
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
PR_ASSERT(cursor->lasthit->refcnt >= 0);
|
||||
}
|
||||
freeParentChain(cursor);
|
||||
if (db) tdbFlush(db);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_FREEIF(cursor->range[i].min);
|
||||
PR_FREEIF(cursor->range[i].max);
|
||||
}
|
||||
if (cursor->prevcursor) {
|
||||
cursor->prevcursor->nextcursor = cursor->nextcursor;
|
||||
} else {
|
||||
if (db) db->firstcursor = cursor->nextcursor;
|
||||
}
|
||||
if (cursor->nextcursor) {
|
||||
cursor->nextcursor->prevcursor = cursor->prevcursor;
|
||||
}
|
||||
PR_Free(cursor);
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus TDBFreeCursor(TDBCursor* cursor)
|
||||
{
|
||||
PRStatus status;
|
||||
TDB* db;
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return PR_FAILURE;
|
||||
db = cursor->db;
|
||||
PR_Lock(db->mutex);
|
||||
status = tdbFreeCursorNolock(cursor);
|
||||
#ifdef DEBUG
|
||||
if (db->firstcursor == 0) {
|
||||
/* There are no more cursors. No other thread can be in the middle of
|
||||
writing stuff, because we have the mutex. And so, there shouldn't
|
||||
be anything left in our cache of records; they should all be
|
||||
flushed out. So... */
|
||||
PR_ASSERT(db->firstrecord == NULL);
|
||||
}
|
||||
#endif
|
||||
PR_Unlock(db->mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
TDBTriple* tdbGetResultNolock(TDBCursor* cursor)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 i;
|
||||
PRInt64 cmp;
|
||||
TDBNodeRange range[3];
|
||||
PR_ASSERT(cursor);
|
||||
if (!cursor) return NULL;
|
||||
if (cursor->cur == NULL && cursor->lasthit == NULL &&
|
||||
cursor->triple.data[0] != NULL) {
|
||||
/* Looks like someone did a write to the database since we last were
|
||||
here, and therefore threw away all our cached information about
|
||||
where we were. Go find our place again. */
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
range[i].min = cursor->triple.data[i];
|
||||
range[i].max = NULL;
|
||||
if (cursor->reverse) {
|
||||
range[i].max = range[i].min;
|
||||
range[i].min = NULL;
|
||||
}
|
||||
}
|
||||
status = findFirstNode(cursor, range);
|
||||
if (status != PR_SUCCESS) return NULL;
|
||||
if (cursor->cur) {
|
||||
PRBool match = PR_TRUE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (tdbCompareNodes(cursor->cur->data[i],
|
||||
cursor->triple.data[i]) != 0) {
|
||||
match = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
/* OK, this node we found was exactly the one we were at
|
||||
last time. Bump it up one. */
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_FREEIF(cursor->triple.data[i]);
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
cursor->lasthit = NULL;
|
||||
}
|
||||
if (cursor->cur == NULL) return NULL;
|
||||
while (cursor->cur != NULL) {
|
||||
if (tdbMatchesRange(cursor->cur, cursor->range)) {
|
||||
break;
|
||||
}
|
||||
cmp = tdbCompareToRange(cursor->cur, cursor->range, cursor->tree);
|
||||
if (cursor->reverse ? (cmp < 0) : (cmp > 0)) {
|
||||
/* We're off the end of the range, all done. */
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
break;
|
||||
}
|
||||
cursor->misses++;
|
||||
moveCursorForward(cursor);
|
||||
}
|
||||
if (cursor->cur == NULL) {
|
||||
tdbFlush(cursor->db);
|
||||
return NULL;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
cursor->triple.data[i] = tdbNodeDup(cursor->cur->data[i]);
|
||||
}
|
||||
cursor->lasthit = cursor->cur;
|
||||
cursor->lasthit->refcnt++;
|
||||
moveCursorForward(cursor);
|
||||
cursor->hits++;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
TDBParentChain* tmp;
|
||||
PR_ASSERT(cursor->cur == NULL || cursor->cur->refcnt > 0);
|
||||
for (tmp = cursor->parent ; tmp ; tmp = tmp->next) {
|
||||
PR_ASSERT(tmp->record->refcnt > 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tdbFlush(cursor->db);
|
||||
return &(cursor->triple);
|
||||
}
|
||||
|
||||
|
||||
TDBTriple* TDBGetResult(TDBCursor* cursor)
|
||||
{
|
||||
TDBTriple* result;
|
||||
PR_ASSERT(cursor && cursor->db);
|
||||
if (!cursor || !cursor->db) return NULL;
|
||||
PR_Lock(cursor->db->mutex);
|
||||
result = tdbGetResultNolock(cursor);
|
||||
PR_Unlock(cursor->db->mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Determines where this item falls within the range of items defined.
|
||||
Negative means this item is too early, and positive means too late.
|
||||
Zero means that it seems to fall within the range, but you need to do
|
||||
a call to tdbMatchesRange() to really make sure. The idea here is that
|
||||
as you slowly walk along the appropriate tree in the DB, this routine
|
||||
will always return a nondecreasing result. */
|
||||
|
||||
PRInt64 tdbCompareToRange(TDBRecord* record, TDBNodeRange* range,
|
||||
PRInt32 comparerule)
|
||||
{
|
||||
int i;
|
||||
int k;
|
||||
PR_ASSERT(record != NULL && range != NULL);
|
||||
if (record == NULL || range == NULL) return PR_FALSE;
|
||||
PR_ASSERT(comparerule >= 0 && comparerule < NUMTREES);
|
||||
if (comparerule < 0 || comparerule >= NUMTREES) {
|
||||
return 0;
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
k = key[comparerule][i];
|
||||
if (range[k].min != NULL &&
|
||||
tdbCompareNodes(record->data[k], range[k].min) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (range[k].max != NULL &&
|
||||
tdbCompareNodes(record->data[k], range[k].max) > 0) {
|
||||
return 1;
|
||||
}
|
||||
if (range[k].min == NULL || range[k].max == NULL ||
|
||||
tdbCompareNodes(range[k].min, range[k].max) != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PRBool tdbMatchesRange(TDBRecord* record, TDBNodeRange* range)
|
||||
{
|
||||
PRInt32 i;
|
||||
PR_ASSERT(record != NULL && range != NULL);
|
||||
if (record == NULL || range == NULL) return PR_FALSE;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
if (range[i].min != NULL &&
|
||||
tdbCompareNodes(record->data[i], range[i].min) < 0) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
if (range[i].max != NULL &&
|
||||
tdbCompareNodes(record->data[i], range[i].max) > 0) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
void tdbThrowOutCursorCaches(TDB* db)
|
||||
{
|
||||
TDBCursor* cursor;
|
||||
for (cursor = db->firstcursor ; cursor ; cursor = cursor->nextcursor) {
|
||||
freeParentChain(cursor);
|
||||
if (cursor->cur) {
|
||||
cursor->cur->refcnt--;
|
||||
PR_ASSERT(cursor->cur->refcnt >= 0);
|
||||
cursor->cur = NULL;
|
||||
}
|
||||
if (cursor->lasthit) {
|
||||
cursor->lasthit->refcnt--;
|
||||
PR_ASSERT(cursor->lasthit->refcnt >= 0);
|
||||
cursor->lasthit = NULL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
/* Operations that do things on file record. */
|
||||
|
||||
#include "tdbtypes.h"
|
||||
|
||||
|
||||
|
||||
|
||||
TDBRecord* tdbLoadRecord(TDB* db, TDBPtr position)
|
||||
{
|
||||
TDBRecord* result;
|
||||
char buf[sizeof(PRUint32)];
|
||||
char* ptr;
|
||||
PRInt32 i;
|
||||
PRInt32 num;
|
||||
PRInt32 numtoread;
|
||||
|
||||
for (result = db->firstrecord ; result ; result = result->next) {
|
||||
if (result->position == position) {
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (PR_Seek(db->fid, position, PR_SEEK_SET) < 0) {
|
||||
goto DONE;
|
||||
}
|
||||
num = PR_Read(db->fid, buf, sizeof(buf));
|
||||
if (num != sizeof(buf)) {
|
||||
goto DONE;
|
||||
}
|
||||
result = PR_NEWZAP(TDBRecord);
|
||||
if (result == NULL) goto DONE;
|
||||
result->position = position;
|
||||
ptr = buf;
|
||||
result->length = tdbGetInt32(&ptr);
|
||||
if (result->length < MINRECORDSIZE || result->length > MAXRECORDSIZE) {
|
||||
tdbMarkCorrupted(db);
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
if (tdbGrowIobuf(db, result->length) != PR_SUCCESS) {
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
numtoread = result->length - sizeof(PRInt32);
|
||||
num = PR_Read(db->fid, db->iobuf, numtoread);
|
||||
if (num < numtoread) {
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
ptr = db->iobuf;
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
result->entry[i].child[0] = tdbGetInt32(&ptr);
|
||||
result->entry[i].child[1] = tdbGetInt32(&ptr);
|
||||
result->entry[i].balance = tdbGetInt8(&ptr);
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
result->data[i] = tdbGetNode(db, &ptr);
|
||||
if (result->data[i] == NULL) {
|
||||
while (--i >= 0) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr - db->iobuf != numtoread) {
|
||||
tdbMarkCorrupted(db);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
result = NULL;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
PR_ASSERT(db->firstrecord != result);
|
||||
result->next = db->firstrecord;
|
||||
db->firstrecord = result;
|
||||
|
||||
DONE:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbSaveRecord(TDB* db, TDBRecord* record)
|
||||
{
|
||||
PRStatus status;
|
||||
PRInt32 i;
|
||||
char* ptr;
|
||||
if (PR_Seek(db->fid, record->position, PR_SEEK_SET) < 0) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
status = tdbGrowIobuf(db, record->length);
|
||||
if (status != PR_SUCCESS) return status;
|
||||
ptr = db->iobuf;
|
||||
tdbPutInt32(&ptr, record->length);
|
||||
for (i=0 ; i<NUMTREES ; i++) {
|
||||
tdbPutInt32(&ptr, record->entry[i].child[0]);
|
||||
tdbPutInt32(&ptr, record->entry[i].child[1]);
|
||||
tdbPutInt8(&ptr, record->entry[i].balance);
|
||||
}
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
tdbPutNode(db, &ptr, record->data[i]);
|
||||
}
|
||||
PR_ASSERT(ptr - db->iobuf == record->length);
|
||||
if (PR_Write(db->fid, db->iobuf, record->length) != record->length) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
PRStatus tdbFreeRecord(TDBRecord* record)
|
||||
{
|
||||
PRInt32 i;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(record->data[i]);
|
||||
}
|
||||
PR_Free(record);
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TDBRecord* tdbAllocateRecord(TDB* db, TDBNodePtr triple[3])
|
||||
{
|
||||
PRInt32 i;
|
||||
PRInt32 size;
|
||||
TDBRecord* result = NULL;
|
||||
TDBRecord* tmp;
|
||||
TDBPtr position;
|
||||
|
||||
size = sizeof(PRInt32) +
|
||||
NUMTREES * (sizeof(PRInt32) + sizeof(PRInt32) + sizeof(PRInt8)) +
|
||||
tdbNodeSize(triple[0]) +
|
||||
tdbNodeSize(triple[1]) +
|
||||
tdbNodeSize(triple[2]);
|
||||
|
||||
position = db->freeroot;
|
||||
if (position > 0) {
|
||||
do {
|
||||
result = tdbLoadRecord(db, position);
|
||||
if (result->length > size) {
|
||||
position = result->entry[0].child[0];
|
||||
} else if (result->length < size) {
|
||||
position = result->entry[0].child[1];
|
||||
} else {
|
||||
/* Hey, we found one! */
|
||||
if (tdbRemoveFromTree(db, result, -1) != PR_SUCCESS) {
|
||||
tdbMarkCorrupted(db);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (position != 0);
|
||||
}
|
||||
if (position == 0) {
|
||||
result = PR_NEWZAP(TDBRecord);
|
||||
if (!result) return NULL;
|
||||
position = db->filelength;
|
||||
db->filelength += size;
|
||||
PR_ASSERT(db->firstrecord != result);
|
||||
result->next = db->firstrecord;
|
||||
db->firstrecord = result;
|
||||
} else {
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
tmp = result->next;
|
||||
memset(result, 0, sizeof(TDBRecord));
|
||||
result->next = tmp;
|
||||
}
|
||||
result->position = position;
|
||||
result->length = size;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
result->data[i] = tdbNodeDup(triple[i]);
|
||||
if (result->data[i] == NULL) {
|
||||
while (--i >= 0) {
|
||||
PR_Free(result->data[i]);
|
||||
}
|
||||
PR_Free(result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
result->dirty = PR_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Argh, this same static array appears in query.c. Don't do that. ### */
|
||||
|
||||
static int key[4][3] = {
|
||||
{0, 1, 2},
|
||||
{1, 0, 2},
|
||||
{2, 1, 0},
|
||||
{1, 2, 0}
|
||||
};
|
||||
|
||||
PRInt64 tdbCompareRecords(TDBRecord* r1, TDBRecord* r2, PRInt32 comparerule)
|
||||
{
|
||||
int i, k;
|
||||
PRInt64 cmp;
|
||||
if (comparerule == -1) {
|
||||
cmp = r1->length - r2->length;
|
||||
if (cmp != 0) return cmp;
|
||||
return r1->position - r2->position;
|
||||
}
|
||||
PR_ASSERT(comparerule >= 0 && comparerule < NUMTREES);
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
k = key[comparerule][i];
|
||||
cmp = tdbCompareNodes(r1->data[k], r2->data[k]);
|
||||
if (cmp != 0) return cmp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS"
|
||||
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the TripleDB database library.
|
||||
*
|
||||
*
|
||||
* The Initial Developer of the Original Code is Geocast Network Systems.
|
||||
* Portions created by Geocast are Copyright (C) 1999 Geocast Network Systems.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Portions created by Geocast are
|
||||
* Copyright (C) 1999 Geocast Network Systems. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
*/
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче