This commit is contained in:
dmose%mozilla.org 1999-11-02 06:56:28 +00:00
Родитель da2f224dee
Коммит a7b7757e6d
10 изменённых файлов: 90 добавлений и 1593 удалений

Просмотреть файл

@ -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>
*/