You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
595 lines
18 KiB
595 lines
18 KiB
#ifndef _UNIQUE_HPP_
|
|
#define _UNIQUE_HPP_
|
|
// Ruler
|
|
// 1 2 3 4 5 6 7 8
|
|
//345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* The standard layout. */
|
|
/* */
|
|
/* The standard layout for 'hpp' files for this code is as */
|
|
/* follows: */
|
|
/* */
|
|
/* 1. Include files. */
|
|
/* 2. Constants exported from the class. */
|
|
/* 3. Data structures exported from the class. */
|
|
/* 4. Forward references to other data structures. */
|
|
/* 5. Class specifications (including inline functions). */
|
|
/* 6. Additional large inline functions. */
|
|
/* */
|
|
/* Any portion that is not required is simply omitted. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
#include "Global.hpp"
|
|
|
|
#include "Hash.hpp"
|
|
#include "List.hpp"
|
|
#include "Pool.hpp"
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Constants exported from the class. */
|
|
/* */
|
|
/* The constants supplied here control how unique strings */
|
|
/* are constructed. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
CONST SBIT32 MinDetails = 16;
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Class forward references. */
|
|
/* */
|
|
/* We need to refer to the following classes before they are */
|
|
/* fully specified so here we list them as forward references. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> class UNIQUE;
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* A string description. */
|
|
/* */
|
|
/* All unique strings have a string drescription which is */
|
|
/* into either the active or free list. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
class DETAIL : public LIST
|
|
{
|
|
private:
|
|
//
|
|
// Friend classes.
|
|
//
|
|
friend class UNIQUE<NO_LOCK>;
|
|
friend class UNIQUE<PARTIAL_LOCK>;
|
|
friend class UNIQUE<FULL_LOCK>;
|
|
|
|
//
|
|
// Private data.
|
|
//
|
|
BOOLEAN Active;
|
|
SBIT32 Size;
|
|
CHAR *Text;
|
|
SBIT32 Uses;
|
|
};
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* A unique string. */
|
|
/* */
|
|
/* Almost all the other classes in the library offer valuable */
|
|
/* free-standing functionality. However, this class is really */
|
|
/* just a support class for the variable length string class. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK=PARTIAL_LOCK> class UNIQUE : public HASH<SBIT32,POINTER>
|
|
{
|
|
//
|
|
// Private data.
|
|
//
|
|
LIST Active;
|
|
LIST Free;
|
|
|
|
DETAIL *Default;
|
|
POOL<DETAIL> Details;
|
|
LOCK Sharelock;
|
|
|
|
public:
|
|
//
|
|
// Public functions.
|
|
//
|
|
UNIQUE( VOID );
|
|
|
|
DETAIL *CreateString( CHAR *String,SBIT32 Size );
|
|
|
|
SBIT32 CompareStrings( DETAIL *Detail1,DETAIL *Detail2 );
|
|
|
|
DETAIL *CopyString( DETAIL *Detail1,DETAIL *Detail2 );
|
|
|
|
VOID DeleteString( DETAIL *Detail );
|
|
|
|
~UNIQUE( VOID );
|
|
|
|
//
|
|
// Public inline functions.
|
|
//
|
|
INLINE DETAIL *DefaultString( VOID )
|
|
{ return Default; }
|
|
|
|
INLINE SBIT32 Size( DETAIL *Detail )
|
|
{ return Detail -> Size; }
|
|
|
|
INLINE CHAR *Value( DETAIL *Detail )
|
|
{ return Detail -> Text; }
|
|
|
|
private:
|
|
//
|
|
// Private functions.
|
|
//
|
|
VIRTUAL SBIT32 ComputeHashKey
|
|
(
|
|
CONST SBIT32 & Key
|
|
);
|
|
|
|
VIRTUAL BOOLEAN MatchingKeys
|
|
(
|
|
CONST SBIT32 & Key1,
|
|
CONST SBIT32 & Key2
|
|
);
|
|
|
|
//
|
|
// Disabled operations.
|
|
//
|
|
UNIQUE( CONST UNIQUE & Copy );
|
|
|
|
VOID operator=( CONST UNIQUE & Copy );
|
|
};
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Class constructor. */
|
|
/* */
|
|
/* Create a new unique string table. This call is not thread */
|
|
/* safe and should only be made in a single thread environment. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> UNIQUE<LOCK>::UNIQUE( VOID )
|
|
{
|
|
//
|
|
// Create the default string.
|
|
//
|
|
Default = CreateString( "",0 );
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Create a new unique string. */
|
|
/* */
|
|
/* When we are handed a new string we need to find out whether */
|
|
/* it is unique (and needs to be added to the table) or just a */
|
|
/* duplicate of an existing string. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> DETAIL *UNIQUE<LOCK>::CreateString
|
|
(
|
|
CHAR *String,
|
|
SBIT32 Size
|
|
)
|
|
{
|
|
AUTO DETAIL *Detail1;
|
|
AUTO DETAIL *Detail2;
|
|
|
|
//
|
|
// Claim an exclusive lock (if enabled).
|
|
//
|
|
Sharelock.ClaimExclusiveLock();
|
|
|
|
//
|
|
// Let us assume that the string is unique and
|
|
// build an entry to for it. If we later find
|
|
// it is not then we just back out the changes.
|
|
//
|
|
if ( Free.EndOfList() )
|
|
{
|
|
REGISTER SBIT32 Count;
|
|
|
|
//
|
|
// Create a several new string descriptions
|
|
// and link them into the free list.
|
|
//
|
|
for ( Count=0;Count < MinDetails;Count ++ )
|
|
{
|
|
//
|
|
// Create a new description and add it
|
|
// to the free list.
|
|
//
|
|
Detail1 = new DETAIL;
|
|
|
|
Detail1 -> Active = False;
|
|
|
|
Detail1 -> Insert( & Free );
|
|
}
|
|
}
|
|
|
|
//
|
|
// We know that the free list must contain
|
|
// least one element (if not we would have
|
|
// just made some). We extract the oldest
|
|
// here.
|
|
//
|
|
if ( (Detail1 = ((DETAIL*) Free.Last())) -> Active )
|
|
{
|
|
//
|
|
// Delete any existing value when we
|
|
// recycle an old and unused string
|
|
// description. Remember to remove
|
|
// it from the hash before deleting
|
|
// the string as the hash uses the
|
|
// string.
|
|
//
|
|
RemoveFromHash( ((SBIT32) Detail1) );
|
|
|
|
delete [] Detail1 -> Text;
|
|
|
|
Detail1 -> Active = False;
|
|
}
|
|
|
|
//
|
|
// We now setup the string description for the
|
|
// new string.
|
|
//
|
|
Detail1 -> Size = Size;
|
|
Detail1 -> Text = String;
|
|
Detail1 -> Uses = 1;
|
|
|
|
//
|
|
// We are now ready to search the hash table for
|
|
// a matching string. We do this by overloading
|
|
// the hash table key comparision (see later).
|
|
//
|
|
if ( ! FindInHash( ((SBIT32) Detail1),((POINTER*) & Detail2) ) )
|
|
{
|
|
//
|
|
// We have found a new string so we need to
|
|
// make the string description active and
|
|
// insert it in the active list.
|
|
//
|
|
(Detail2 = Detail1) -> Active = True;
|
|
|
|
Detail1 -> Delete( & Free );
|
|
Detail1 -> Insert( & Active );
|
|
|
|
//
|
|
// Add the new unique string the the hash
|
|
// table so we can find it later.
|
|
//
|
|
AddToHash( ((SBIT32) Detail1),((POINTER) Detail1) );
|
|
|
|
//
|
|
// We know the string is unique so lets
|
|
// allocate some space for it and copy it
|
|
// into the new area.
|
|
//
|
|
Detail1 -> Text =
|
|
(
|
|
strncpy
|
|
(
|
|
new CHAR [ (Size + 1) ],
|
|
String,
|
|
Size
|
|
)
|
|
);
|
|
|
|
Detail1 -> Text[ Size ] = '\0';
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Increment the use count for an existing
|
|
// string.
|
|
//
|
|
if ( Detail2 != Default )
|
|
{
|
|
//
|
|
// We may be lucky and find an unused
|
|
// string. If so we need to add it to
|
|
// the active list again.
|
|
//
|
|
if ( (Detail2 -> Uses ++) == 0 )
|
|
{
|
|
//
|
|
// Add an unused string back to the
|
|
// active list again.
|
|
//
|
|
Detail1 -> Delete( & Free );
|
|
Detail1 -> Insert( & Active );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release any lock we got earlier.
|
|
//
|
|
Sharelock.ReleaseExclusiveLock();
|
|
|
|
return Detail2;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Compare two strings. */
|
|
/* */
|
|
/* Compare two strings and find the relationship between them. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> SBIT32 UNIQUE<LOCK>::CompareStrings
|
|
(
|
|
DETAIL *Detail1,
|
|
DETAIL *Detail2
|
|
)
|
|
{
|
|
//
|
|
// We know that all strings are unique so if the
|
|
// string pointers match then they must be the
|
|
// the same string.
|
|
//
|
|
if ( Detail1 != Detail2 )
|
|
{
|
|
REGISTER SBIT32 Result =
|
|
(
|
|
strncmp
|
|
(
|
|
Detail1 -> Text,
|
|
Detail2 -> Text,
|
|
(Detail1 -> Size < Detail2 -> Size)
|
|
? Detail1 -> Size
|
|
: Detail2 -> Size
|
|
)
|
|
);
|
|
|
|
//
|
|
// If the strings match pick the longest.
|
|
//
|
|
if ( Result == 0 )
|
|
{ Result = ((Detail1 -> Size < Detail2 -> Size) ? -1 : 1); }
|
|
|
|
return Result;
|
|
}
|
|
else
|
|
{ return 0; }
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Compute a hash key. */
|
|
/* */
|
|
/* Compute a hash key for the supplied key. This hash key */
|
|
/* is used to select the hash chain to search. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> SBIT32 UNIQUE<LOCK>::ComputeHashKey
|
|
(
|
|
CONST SBIT32 & Key
|
|
)
|
|
{
|
|
REGISTER SBIT32 Count;
|
|
REGISTER DETAIL *Detail = ((DETAIL*) Key);
|
|
REGISTER SBIT32 HashKey = 2964557531;
|
|
|
|
for ( Count=0;Count < Detail -> Size;Count ++ )
|
|
{
|
|
REGISTER SBIT32 Value = ((SBIT32) Detail -> Text[ Count ]);
|
|
|
|
HashKey = ((HashKey * Value) + Value);
|
|
}
|
|
|
|
return (HashKey & 0x3fffffff);
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Copy a string. */
|
|
/* */
|
|
/* All strings are unique so there is no need to copy a string. */
|
|
/* Nonetheless, we still have to update the use counts. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> DETAIL *UNIQUE<LOCK>::CopyString
|
|
(
|
|
DETAIL *Detail1,
|
|
DETAIL *Detail2
|
|
)
|
|
{
|
|
//
|
|
// Claim an exclusive lock (if enabled).
|
|
//
|
|
Sharelock.ClaimExclusiveLock();
|
|
|
|
//
|
|
// Decrement the use count for old string.
|
|
//
|
|
if ( Detail1 != Default )
|
|
{ Detail1 -> Uses --; }
|
|
|
|
//
|
|
// Increment the use count for new string.
|
|
//
|
|
if ( Detail2 != Default )
|
|
{ Detail2 -> Uses ++; }
|
|
|
|
//
|
|
// Release any lock we got earlier.
|
|
//
|
|
Sharelock.ReleaseExclusiveLock();
|
|
|
|
return Detail2;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Compare two hash keys. */
|
|
/* */
|
|
/* Compare two hash keys to see if they match. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> BOOLEAN UNIQUE<LOCK>::MatchingKeys
|
|
(
|
|
CONST SBIT32 & Key1,
|
|
CONST SBIT32 & Key2
|
|
)
|
|
{
|
|
REGISTER DETAIL *Detail1 = ((DETAIL*) Key1);
|
|
REGISTER DETAIL *Detail2 = ((DETAIL*) Key2);
|
|
|
|
return
|
|
(
|
|
(Detail1 -> Size == Detail2 -> Size)
|
|
&&
|
|
(strncmp( Detail1 -> Text,Detail2 -> Text,Detail1 -> Size ) == 0)
|
|
);
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Delete a string. */
|
|
/* */
|
|
/* Delete a text string. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> VOID UNIQUE<LOCK>::DeleteString( DETAIL *Detail )
|
|
{
|
|
//
|
|
// Claim an exclusive lock (if enabled).
|
|
//
|
|
Sharelock.ClaimExclusiveLock();
|
|
|
|
//
|
|
// Decrement the use count for the string.
|
|
//
|
|
if ( Detail != Default )
|
|
{
|
|
//
|
|
// Decrement the use count and ensure that
|
|
// this is not the last use of the string.
|
|
//
|
|
if ( (-- Detail -> Uses) == 0 )
|
|
{
|
|
//
|
|
// When we delete the last use of
|
|
// a string we add it to the free
|
|
// list. The string can be reclaimed
|
|
// if it is recreated before it is
|
|
// deleted.
|
|
//
|
|
Detail -> Delete( & Active );
|
|
Detail -> Insert( & Free );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release any lock we got earlier.
|
|
//
|
|
Sharelock.ReleaseExclusiveLock();
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Class destructor. */
|
|
/* */
|
|
/* Destory the unique string table. This call is not thread */
|
|
/* safe and should only be made in a single thread environment. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
template <class LOCK> UNIQUE<LOCK>::~UNIQUE( VOID )
|
|
{
|
|
//
|
|
// Delete all active strings.
|
|
//
|
|
while ( ! Active.EndOfList() )
|
|
{
|
|
REGISTER DETAIL *Detail = ((DETAIL*) Active.First());
|
|
|
|
//
|
|
// Delete from the list and add to the free
|
|
// pool just to be tidy.
|
|
//
|
|
Detail -> Delete( & Active );
|
|
|
|
//
|
|
// The string description may be contain an
|
|
// previous value and require some cleanup.
|
|
//
|
|
if ( Detail -> Active )
|
|
{
|
|
//
|
|
// Delete any existing value. Remember
|
|
// to remove it from the hash before
|
|
// deleting the string as the hash uses
|
|
// the string.
|
|
//
|
|
RemoveFromHash( ((SBIT32) Detail) );
|
|
|
|
delete [] Detail -> Text;
|
|
|
|
Detail -> Active = False;
|
|
}
|
|
|
|
//
|
|
// Push back into the pool.
|
|
//
|
|
Details.PushPool( Detail );
|
|
}
|
|
|
|
//
|
|
// Delete all free strings.
|
|
//
|
|
while ( ! Free.EndOfList() )
|
|
{
|
|
REGISTER DETAIL *Detail = ((DETAIL*) Free.First());
|
|
|
|
//
|
|
// Delete from the list and add to the free
|
|
// pool just to be tidy.
|
|
//
|
|
Detail -> Delete( & Free );
|
|
|
|
//
|
|
// The string description may be contain an
|
|
// previous value and require some cleanup.
|
|
//
|
|
if ( Detail -> Active )
|
|
{
|
|
//
|
|
// Delete any existing value. Remember
|
|
// to remove it from the hash before
|
|
// deleting the string as the hash uses
|
|
// the string.
|
|
//
|
|
RemoveFromHash( ((SBIT32) Detail) );
|
|
|
|
delete [] Detail -> Text;
|
|
|
|
Detail -> Active = False;
|
|
}
|
|
|
|
//
|
|
// Push back into the pool.
|
|
//
|
|
Details.PushPool( Detail );
|
|
}
|
|
}
|
|
#endif
|