692 lines
14 KiB
C++
692 lines
14 KiB
C++
|
#include "StringTree.h"
|
||
|
|
||
|
#include "minorGems/util/SimpleVector.h"
|
||
|
#include "minorGems/util/stringUtils.h"
|
||
|
|
||
|
|
||
|
#include <stdio.h>
|
||
|
|
||
|
// for platform-independent ptr -> unsigned int casting
|
||
|
#include <stdint.h>
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct valueHolder {
|
||
|
void *value;
|
||
|
char mark;
|
||
|
} valueHolder;
|
||
|
|
||
|
|
||
|
#define B 2000
|
||
|
|
||
|
class ValueHashTable {
|
||
|
public:
|
||
|
|
||
|
~ValueHashTable();
|
||
|
|
||
|
void insert( valueHolder *inHolder );
|
||
|
|
||
|
void remove( valueHolder *inHolder );
|
||
|
|
||
|
valueHolder *lookup( void *inValue );
|
||
|
|
||
|
protected:
|
||
|
|
||
|
SimpleVector<valueHolder *> mBins[B];
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
ValueHashTable::~ValueHashTable() {
|
||
|
for( int b=0; b<B; b++ ) {
|
||
|
SimpleVector<valueHolder *> *v = &( mBins[b] );
|
||
|
|
||
|
int numHits = v->size();
|
||
|
|
||
|
for( int i=0; i<numHits; i++ ) {
|
||
|
delete *( v->getElement( i ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void ValueHashTable::insert( valueHolder *inHolder ) {
|
||
|
int b = (uintptr_t)( inHolder->value ) % B;
|
||
|
|
||
|
mBins[b].push_back( inHolder );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void ValueHashTable::remove( valueHolder *inHolder ) {
|
||
|
int b = (uintptr_t)( inHolder->value ) % B;
|
||
|
|
||
|
mBins[b].deleteElementEqualTo( inHolder );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
valueHolder *ValueHashTable::lookup( void *inValue ) {
|
||
|
int b = (uintptr_t)( inValue ) % B;
|
||
|
|
||
|
SimpleVector<valueHolder *> *v = &( mBins[b] );
|
||
|
|
||
|
int numHits = v->size();
|
||
|
|
||
|
for( int i=0; i<numHits; i++ ) {
|
||
|
valueHolder *holder = *( v->getElement( i ) );
|
||
|
|
||
|
if( holder->value == inValue ) {
|
||
|
return holder;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class StringTreeNode {
|
||
|
public:
|
||
|
StringTreeNode( char inChar, StringTreeNode *inParent );
|
||
|
|
||
|
~StringTreeNode();
|
||
|
|
||
|
|
||
|
void insert( const char *inString, void *inValue,
|
||
|
ValueHashTable *inHashTable );
|
||
|
|
||
|
|
||
|
// removes value at this node, and recursively deletes upward if
|
||
|
// this node is now empty
|
||
|
void remove( valueHolder *inHolder );
|
||
|
|
||
|
|
||
|
StringTreeNode *search( const char *inString );
|
||
|
|
||
|
|
||
|
// is this node empty
|
||
|
char isEmpty();
|
||
|
|
||
|
|
||
|
// use inDownOnly for root node of a match (to get everything below
|
||
|
// that node, but not to left or right of that node)
|
||
|
int countValuesBelow( char inDownOnly );
|
||
|
|
||
|
|
||
|
|
||
|
// pointers for inNumToGet and inNumToSkip because they are
|
||
|
// used for tracking progress in recursion (modified during call)
|
||
|
// returns num gotten
|
||
|
void getValuesBelow( int *inNumToGet, int *inNumToSkip,
|
||
|
SimpleVector<void *> *outValues,
|
||
|
char inDownOnly );
|
||
|
|
||
|
|
||
|
void unmarkValuesBelow( char inDownOnly );
|
||
|
|
||
|
|
||
|
void print();
|
||
|
|
||
|
char mChar;
|
||
|
SimpleVector<valueHolder*> mValues;
|
||
|
|
||
|
StringTreeNode *mParent;
|
||
|
|
||
|
StringTreeNode *mLeft;
|
||
|
StringTreeNode *mDown;
|
||
|
StringTreeNode *mRight;
|
||
|
|
||
|
protected:
|
||
|
|
||
|
void removeChild( StringTreeNode *inChild );
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
StringTreeNode::StringTreeNode( char inChar, StringTreeNode *inParent )
|
||
|
: mChar( inChar ),
|
||
|
mParent( inParent ), mLeft( NULL ), mDown( NULL), mRight( NULL ) {
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
StringTreeNode::~StringTreeNode() {
|
||
|
if( mLeft != NULL ) {
|
||
|
delete mLeft;
|
||
|
}
|
||
|
if( mDown != NULL ) {
|
||
|
delete mDown;
|
||
|
}
|
||
|
if( mRight != NULL ) {
|
||
|
delete mRight;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
extern void printResourceRecord( void *inR );
|
||
|
|
||
|
|
||
|
void StringTreeNode::insert( const char *inString, void *inValue,
|
||
|
ValueHashTable *inHashTable ) {
|
||
|
|
||
|
if( inString[0] == mChar ) {
|
||
|
// match
|
||
|
|
||
|
if( inString[1] == '\0' ) {
|
||
|
|
||
|
// ends here
|
||
|
valueHolder *v = inHashTable->lookup( inValue );
|
||
|
|
||
|
if( v == NULL ) {
|
||
|
v = new valueHolder;
|
||
|
v->value = inValue;
|
||
|
v->mark = false;
|
||
|
inHashTable->insert( v );
|
||
|
}
|
||
|
|
||
|
mValues.push_back( v );
|
||
|
}
|
||
|
else {
|
||
|
// pass down, skipping matched char
|
||
|
|
||
|
if( mDown == NULL ) {
|
||
|
// create it
|
||
|
mDown = new StringTreeNode( inString[1], this );
|
||
|
}
|
||
|
|
||
|
mDown->insert( &( inString[1] ), inValue, inHashTable );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// non-match
|
||
|
|
||
|
if( inString[0] < mChar ) {
|
||
|
// left
|
||
|
if( mLeft == NULL ) {
|
||
|
mLeft = new StringTreeNode( inString[0], this );
|
||
|
}
|
||
|
|
||
|
mLeft->insert( inString, inValue, inHashTable );
|
||
|
}
|
||
|
else if( inString[0] > mChar ) {
|
||
|
// right
|
||
|
if( mRight == NULL ) {
|
||
|
mRight = new StringTreeNode( inString[0], this );
|
||
|
}
|
||
|
|
||
|
mRight->insert( inString, inValue, inHashTable );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
char StringTreeNode::isEmpty() {
|
||
|
if( mValues.size() == 0
|
||
|
&& mLeft == NULL
|
||
|
&& mDown == NULL
|
||
|
&& mRight == NULL ) {
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void StringTreeNode::remove( valueHolder *inHolder ) {
|
||
|
|
||
|
mValues.deleteElementEqualTo( inHolder );
|
||
|
|
||
|
|
||
|
if( isEmpty() ) {
|
||
|
|
||
|
if( mParent != NULL ) {
|
||
|
mParent->removeChild( this );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void StringTreeNode::removeChild( StringTreeNode *inChild ) {
|
||
|
|
||
|
if( mLeft == inChild ) {
|
||
|
delete mLeft;
|
||
|
mLeft = NULL;
|
||
|
}
|
||
|
if( mDown == inChild ) {
|
||
|
delete mDown;
|
||
|
mDown = NULL;
|
||
|
}
|
||
|
if( mRight == inChild ) {
|
||
|
delete mRight;
|
||
|
mRight = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( isEmpty() ) {
|
||
|
|
||
|
if( mParent != NULL ) {
|
||
|
mParent->removeChild( this );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
StringTreeNode *StringTreeNode::search( const char *inString ) {
|
||
|
if( inString[0] == mChar ) {
|
||
|
// match
|
||
|
|
||
|
if( inString[1] == '\0' ) {
|
||
|
// ends here
|
||
|
return this;
|
||
|
}
|
||
|
else {
|
||
|
// pass down, skipping matched char
|
||
|
|
||
|
if( mDown == NULL ) {
|
||
|
// not found
|
||
|
return NULL;
|
||
|
}
|
||
|
return mDown->search( &( inString[1] ) );
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// non-match
|
||
|
|
||
|
if( inString[0] < mChar ) {
|
||
|
// left
|
||
|
if( mLeft == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return mLeft->search( inString );
|
||
|
}
|
||
|
else {
|
||
|
// right
|
||
|
if( mRight == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return mRight->search( inString );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int StringTreeNode::countValuesBelow( char inDownOnly ) {
|
||
|
|
||
|
int num = 0;
|
||
|
|
||
|
if( !inDownOnly && mLeft != NULL ) {
|
||
|
num += mLeft->countValuesBelow( false );
|
||
|
}
|
||
|
|
||
|
int numHere = mValues.size();
|
||
|
|
||
|
for( int i=0; i<numHere; i++ ) {
|
||
|
valueHolder *v = *( mValues.getElement( i ) );
|
||
|
|
||
|
if( !v->mark ) {
|
||
|
// mark it
|
||
|
v->mark = true;
|
||
|
|
||
|
// count it
|
||
|
num++;
|
||
|
}
|
||
|
// else already counted... skip it
|
||
|
}
|
||
|
|
||
|
|
||
|
if( mDown != NULL ) {
|
||
|
num += mDown->countValuesBelow( false );
|
||
|
}
|
||
|
|
||
|
if( !inDownOnly && mRight != NULL ) {
|
||
|
num += mRight->countValuesBelow( false );
|
||
|
}
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void StringTreeNode::getValuesBelow( int *inNumToGet, int *inNumToSkip,
|
||
|
SimpleVector<void *> *outValues,
|
||
|
char inDownOnly ) {
|
||
|
|
||
|
if( !inDownOnly && mLeft != NULL ) {
|
||
|
mLeft->getValuesBelow( inNumToGet, inNumToSkip,
|
||
|
outValues, false );
|
||
|
}
|
||
|
|
||
|
if( *inNumToGet == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// self
|
||
|
int numHere = mValues.size();
|
||
|
|
||
|
// newest values first
|
||
|
for( int i=numHere-1; i>=0 && *inNumToGet > 0; i-- ) {
|
||
|
|
||
|
valueHolder *v = *( mValues.getElement( i ) );
|
||
|
|
||
|
if( !v->mark ) {
|
||
|
// mark it
|
||
|
v->mark = true;
|
||
|
|
||
|
if( *inNumToSkip > 0 ) {
|
||
|
(*inNumToSkip) --;
|
||
|
}
|
||
|
else {
|
||
|
// done skipping
|
||
|
|
||
|
// take value
|
||
|
outValues->push_back( v->value );
|
||
|
|
||
|
(*inNumToGet) --;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
if( *inNumToGet == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( mDown != NULL ) {
|
||
|
mDown->getValuesBelow( inNumToGet, inNumToSkip,
|
||
|
outValues, false );
|
||
|
}
|
||
|
|
||
|
|
||
|
if( *inNumToGet == 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( !inDownOnly && mRight != NULL ) {
|
||
|
mRight->getValuesBelow( inNumToGet, inNumToSkip,
|
||
|
outValues, false );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void StringTreeNode::unmarkValuesBelow( char inDownOnly ) {
|
||
|
|
||
|
if( !inDownOnly && mLeft != NULL ) {
|
||
|
mLeft->unmarkValuesBelow( false );
|
||
|
}
|
||
|
|
||
|
int numHere = mValues.size();
|
||
|
|
||
|
for( int i=0; i<numHere; i++ ) {
|
||
|
valueHolder *v = *( mValues.getElement( i ) );
|
||
|
v->mark = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( mDown != NULL ) {
|
||
|
mDown->unmarkValuesBelow( false );
|
||
|
}
|
||
|
|
||
|
if( !inDownOnly && mRight != NULL ) {
|
||
|
mRight->unmarkValuesBelow( false );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void StringTreeNode::print() {
|
||
|
|
||
|
// Graphviz format
|
||
|
|
||
|
char *valueString = stringDuplicate( "" );
|
||
|
|
||
|
/*
|
||
|
for( int i=0; i<mValues.size(); i++ ) {
|
||
|
char *newString =
|
||
|
autoSprintf( "%s%s, ", valueString,
|
||
|
(char*)( *( mValues.getElement( i ) ) )->value );
|
||
|
|
||
|
delete [] valueString;
|
||
|
valueString = newString;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
printf( "n%p; n%p [label = \"%c (%s) {%p}\"]; ",
|
||
|
(void *)this, (void *)this, mChar, valueString,
|
||
|
(void *)this );
|
||
|
|
||
|
delete [] valueString;
|
||
|
|
||
|
if( mLeft != NULL ) {
|
||
|
mLeft->print();
|
||
|
printf( "n%p -> n%p [label = \"L\", weight=1]; ",
|
||
|
(void *)this, (void *)mLeft );
|
||
|
}
|
||
|
if( mDown != NULL ) {
|
||
|
mDown->print();
|
||
|
printf( "n%p -> n%p [label = \"D\", weight=4]; ",
|
||
|
(void *)this, (void *)mDown );
|
||
|
}
|
||
|
if( mRight != NULL ) {
|
||
|
mRight->print();
|
||
|
printf( "n%p -> n%p [label = \"R\", weight=1]; ",
|
||
|
(void *)this, (void *)mRight );
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
StringTree::StringTree()
|
||
|
: mTreeRoot( NULL ),
|
||
|
mHashTable( new ValueHashTable ) {
|
||
|
}
|
||
|
|
||
|
|
||
|
StringTree::~StringTree() {
|
||
|
if( mTreeRoot != NULL ) {
|
||
|
delete mTreeRoot;
|
||
|
}
|
||
|
delete mHashTable;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void StringTree::insert( const char *inString, void *inValue ) {
|
||
|
if( mTreeRoot == NULL ) {
|
||
|
mTreeRoot = new StringTreeNode( inString[0], NULL );
|
||
|
}
|
||
|
|
||
|
|
||
|
// insert all suffixes
|
||
|
int numChars = strlen( inString );
|
||
|
|
||
|
|
||
|
for( int i=0; i<numChars; i++ ) {
|
||
|
mTreeRoot->insert( &( inString[i] ), inValue, mHashTable );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
printf( "After inseriting %s, tree is:\n", inString );
|
||
|
mTreeRoot->print();
|
||
|
|
||
|
printf( "\n\n\n" );
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
|
||
|
void StringTree::remove( const char *inString, void *inValue ) {
|
||
|
if( mTreeRoot == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
valueHolder *holder = mHashTable->lookup( inValue );
|
||
|
|
||
|
if( holder != NULL ) {
|
||
|
|
||
|
|
||
|
// search for all suffixes to find nodes that hold our value
|
||
|
int numChars = strlen( inString );
|
||
|
|
||
|
for( int i=0; i<numChars; i++ ) {
|
||
|
|
||
|
StringTreeNode *matchNode = mTreeRoot->search( &( inString[i] ) );
|
||
|
|
||
|
if( matchNode != NULL ) {
|
||
|
|
||
|
matchNode->remove( holder );
|
||
|
}
|
||
|
}
|
||
|
mHashTable->remove( holder );
|
||
|
|
||
|
delete holder;
|
||
|
}
|
||
|
|
||
|
if( mTreeRoot->isEmpty() ) {
|
||
|
delete mTreeRoot;
|
||
|
mTreeRoot = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
printf( "After removing %s, tree is:\n", inString );
|
||
|
if( mTreeRoot != NULL ) {
|
||
|
mTreeRoot->print();
|
||
|
}
|
||
|
else {
|
||
|
printf( "NULL" );
|
||
|
}
|
||
|
printf( "\n\n\n" );
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
|
||
|
int StringTree::countMatches( const char *inSearch ) {
|
||
|
if( mTreeRoot == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
StringTreeNode *matchNode;
|
||
|
char downOnly;
|
||
|
|
||
|
if( inSearch[0] == '\0' ) {
|
||
|
// empty search, whole tree
|
||
|
matchNode = mTreeRoot;
|
||
|
downOnly = false;
|
||
|
}
|
||
|
else {
|
||
|
matchNode = mTreeRoot->search( inSearch );
|
||
|
downOnly = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( matchNode != NULL ) {
|
||
|
int numMatches = matchNode->countValuesBelow( downOnly );
|
||
|
|
||
|
matchNode->unmarkValuesBelow( downOnly );
|
||
|
|
||
|
return numMatches;
|
||
|
}
|
||
|
else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int StringTree::getMatches( const char *inSearch,
|
||
|
int inNumToSkip, int inNumToGet,
|
||
|
void **outValues ) {
|
||
|
|
||
|
if( mTreeRoot == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
StringTreeNode *matchNode;
|
||
|
char downOnly;
|
||
|
|
||
|
if( inSearch[0] == '\0' ) {
|
||
|
// empty search, whole tree
|
||
|
matchNode = mTreeRoot;
|
||
|
downOnly = false;
|
||
|
}
|
||
|
else {
|
||
|
matchNode = mTreeRoot->search( inSearch );
|
||
|
downOnly = true;
|
||
|
}
|
||
|
|
||
|
if( matchNode == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
SimpleVector<void *> results;
|
||
|
|
||
|
// these are modified by call
|
||
|
int numToGet = inNumToGet;
|
||
|
int numToSkip = inNumToSkip;
|
||
|
|
||
|
matchNode->getValuesBelow( &numToGet, &numToSkip,
|
||
|
&results, downOnly );
|
||
|
|
||
|
matchNode->unmarkValuesBelow( downOnly );
|
||
|
|
||
|
|
||
|
int numResults = results.size();
|
||
|
|
||
|
|
||
|
void **resultArray = results.getElementArray();
|
||
|
|
||
|
int numToCopy = numResults;
|
||
|
if( inNumToGet < numToCopy ) {
|
||
|
numToCopy = inNumToGet;
|
||
|
}
|
||
|
|
||
|
|
||
|
memcpy( outValues, resultArray, numToCopy * sizeof( void * ) );
|
||
|
|
||
|
delete [] resultArray;
|
||
|
|
||
|
|
||
|
return numToCopy;
|
||
|
}
|
||
|
|
||
|
|