// $Id: dtypes.pas,v 1.18 2001/06/01 21:10:10 druid Exp $
unit dtypes;
interface
uses
SysUtils,
SyncObjs;
type
GString = class
value : string;
constructor Create(s : string);
end;
GInteger = class
value : integer;
constructor Create(s : integer);
end;
GIterator = class
function hasNext() : boolean; virtual; abstract;
function next() : TObject; virtual; abstract;
end;
GListNode = class
prev, next : GListNode;
element : pointer;
constructor Create(e : pointer; p, n : GListNode);
end;
GDLinkedList = class
size : integer;
head, tail : GListnode;
lock : TCriticalSection;
serial : integer;
function insertLast(element : pointer) : GListNode;
function insertAfter(tn : GListNode; element : pointer) : GListNode;
function insertBefore(tn : GListNode; element : pointer) : GListNode;
procedure remove(node : GListNode);
procedure clean();
procedure smallClean();
function getSize() : integer;
function iterator() : GIterator;
constructor Create;
destructor Destroy; override;
end;
GPrimes = array of integer;
GHASH_FUNC = function(size, prime : cardinal; key : string) : integer;
GHashValue = class
key : variant;
value : TObject;
refcount : integer;
end;
// loosely based on the Java2 hashing classes
GHashTable = class
hashsize : cardinal;
hashprime : cardinal;
bucketList : array of GDLinkedList;
hashFunc : GHASH_FUNC;
procedure clear();
function isEmpty() : boolean;
function size() : integer;
function iterator() : GIterator;
function _get(key : variant) : GHashValue;
function get(key : variant) : TObject;
procedure put(key : variant; value : TObject);
procedure remove(key : variant);
function getHash(key : variant) : integer;
procedure setHashFunc(func : GHASH_FUNC);
function findPrimes(n : integer) : GPrimes;
procedure hashStats(); virtual;
constructor Create(size : integer);
destructor Destroy; override;
end;
GException = class(Exception)
e_location : string;
constructor Create(location, msg : string);
procedure show();
end;
const STR_HASH_SIZE = 1024;
var
str_hash : GHashTable;
function hash_string(src : string) : PString; overload;
function hash_string(src : PString) : PString; overload;
procedure unhash_string(var src : PString);
function defaultHash(size, prime : cardinal; key : string) : integer;
function firstHash(size, prime : cardinal; key : string) : integer;
implementation
{$IFDEF Grendel}
uses
mudsystem;
{$ENDIF}
// GDLinkedListIterator
type
GDLinkedListIterator = class(GIterator)
private
current : GListNode;
published
constructor Create(list : GDLinkedList);
function hasNext() : boolean; override;
function next() : TObject; override;
end;
GHashTableIterator = class(GIterator)
private
tbl : GHashTable;
cursor : integer;
current : GListNode;
published
constructor Create(table : GHashTable);
function hasNext() : boolean; override;
function next() : TObject; override;
end;
// GString
constructor GString.Create(s : string);
begin
inherited Create;
value := s;
end;
// GInteger
constructor GInteger.Create(s : integer);
begin
inherited Create;
value := s;
end;
// GListNode
constructor GListNode.Create(e : pointer; p, n : GListNode);
begin
inherited Create;
element := e;
next := n;
prev := p;
end;
// GDLinkedListIterator
constructor GDLinkedListIterator.Create(list : GDLinkedList);
begin
inherited Create;
current := list.head;
end;
function GDLinkedListIterator.hasNext() : boolean;
begin
Result := (current <> nil);
end;
function GDLinkedListIterator.next() : TObject;
begin
Result := nil;
if (hasNext()) then
begin
Result := current;
current := current.next;
end;
end;
// GHashTableIterator
constructor GHashTableIterator.Create(table : GHashTable);
begin
inherited Create;
tbl := table;
current := nil;
cursor := 0;
while (current = nil) and (cursor < tbl.hashSize) do
begin
if (tbl.bucketlist[cursor].head <> nil) then
current := tbl.bucketList[cursor].head;
inc(cursor);
end;
end;
function GHashTableIterator.hasNext() : boolean;
begin
Result := (current <> nil);
end;
function GHashTableIterator.next() : TObject;
begin
Result := nil;
if (hasNext()) then
begin
Result := current;
current := current.next;
if (current = nil) then
begin
inc(cursor);
while (current = nil) and (cursor < tbl.hashSize) do
begin
if (tbl.bucketlist[cursor].head <> nil) then
current := tbl.bucketList[cursor].head;
inc(cursor);
end;
end;
end;
end;
// GDLinkedList
constructor GDLinkedList.Create;
begin
inherited Create;
head := nil;
tail := nil;
size := 0;
serial := 1;
lock := TCriticalSection.Create;
end;
destructor GDLinkedList.Destroy;
begin
lock.Free;
inherited Destroy;
end;
function GDLinkedList.insertLast(element : pointer) : GListNode;
var
node : GListNode;
begin
try
lock.Acquire;
node := GListNode.Create(element, tail, nil);
if (head = nil) then
head := node
else
tail.next := node;
tail := node;
insertLast := node;
inc(size);
inc(serial);
finally
lock.Release;
end;
end;
function GDLinkedList.insertAfter(tn : GListNode; element : pointer) : GListNode;
var
node : GListNode;
begin
try
lock.Acquire;
node := GListNode.Create(element, tn, tn.next);
if (tn.next <> nil) then
tn.next.prev := node;
tn.next := node;
if (tail = tn) then
tail := node;
insertAfter := node;
inc(serial);
inc(size);
finally
lock.Release;
end;
end;
function GDLinkedList.insertBefore(tn : GListNode; element : pointer) : GListNode;
var
node : GListNode;
begin
try
lock.Acquire;
node := GListNode.Create(element, tn.prev, tn);
if (tn.prev <> nil) then
tn.prev.next := node;
tn.prev := node;
if (head = tn) then
head := node;
insertBefore := node;
inc(serial);
inc(size);
finally
lock.Release;
end;
end;
procedure GDLinkedList.remove(node : GListNode);
begin
try
lock.Acquire;
if (node.prev = nil) then
head := node.next
else
node.prev.next := node.next;
if (node.next = nil) then
tail := node.prev
else
node.next.prev := node.prev;
dec(size);
inc(serial);
node.Free;
finally
lock.Release;
end;
end;
function GDLinkedList.getSize : integer;
begin
getSize := size;
end;
procedure GDLinkedList.clean;
var
node : GListNode;
begin
while (true) do
begin
node := tail;
if (node = nil) then
exit;
TObject(node.element).Free;
remove(node);
end;
end;
// doesn't free elements
procedure GDLinkedList.smallClean;
var
node : GListNode;
begin
while (true) do
begin
node := head;
if (node = nil) then
exit;
remove(node);
end;
end;
function GDLinkedList.iterator() : GIterator;
begin
Result := GDLinkedListIterator.Create(Self);
end;
// GHashTable
function defaultHash(size, prime : cardinal; key : string) : integer;
var
i : integer;
val : cardinal;
begin
val := 0;
for i := 1 to length(key) do
val := val * prime + byte(key[i]);
defaultHash := val mod size;
end;
function firstHash(size, prime : cardinal; key : string) : integer;
begin
if (length(key) >= 1) then
Result := (byte(key[1]) * prime) mod size
else
Result := 0;
end;
function GHashTable.findPrimes(n : integer) : GPrimes;
var
i, j : integer;
limit : double;
numbers : GPrimes;
numberpool : array of boolean;
begin
setlength(numberpool, n);
for i := 2 to n - 1 do
numberpool[i] := true;
limit := sqrt(n);
j := 2;
i := j + j;
while (i < n) do
begin
numberpool[i] := false;
i := i + j;
end;
j := 3;
while (j <= limit) do
begin
if (numberpool[j] = true) then
begin
i := j + j;
while (i < n) do
begin
numberpool[i] := false;
i := i + j;
end;
end;
j := j + 2;
end;
j := 0;
for i := 0 to n - 1 do
begin
if (numberpool[i]) then
begin
setLength(numbers, j + 1);
numbers[j] := i;
j := j + 1;
end;
end;
findPrimes := numbers;
end;
function GHashTable.getHash(key : variant) : integer;
begin
Result := 0;
if (varType(key) = varString) then
Result := hashFunc(hashsize, hashprime, key)
else
if (varType(key) = varInteger) then
Result := (key * hashprime) mod hashsize;
end;
procedure GHashTable.setHashFunc(func : GHASH_FUNC);
begin
hashFunc := func;
end;
function GHashTable._get(key : variant) : GHashValue;
var
hash : integer;
node : GListNode;
begin
Result := nil;
hash := getHash(key);
node := bucketList[hash].head;
while (node <> nil) do
begin
if (GHashValue(node.element).key = key) then
begin
Result := node.element;
break;
end;
node := node.next;
end;
end;
function GHashTable.get(key : variant) : TObject;
var
hv : GHashValue;
begin
Result := nil;
hv := _get(key);
if (hv <> nil) then
Result := hv.value;
end;
procedure GHashTable.put(key : variant; value : TObject);
var
hash : integer;
hv : GHashValue;
begin
hv := _get(key);
if (hv <> nil) then
begin
inc(hv.refcount);
end
else
begin
hash := getHash(key);
hv := GHashValue.Create;
hv.refcount := 1;
hv.key := key;
hv.value := value;
bucketList[hash].insertLast(hv);
end;
end;
procedure GHashTable.remove(key : variant);
var
hash : integer;
fnode, node : GListNode;
begin
fnode := nil;
hash := getHash(key);
node := bucketList[hash].head;
while (node <> nil) do
begin
if (GHashValue(node.element).key = key) then
begin
fnode := node;
break;
end;
node := node.next;
end;
if (fnode <> nil) then
bucketList[hash].remove(fnode);
end;
function GHashTable.size() : integer;
var
i : integer;
total : integer;
begin
total := 0;
for i := 0 to hashsize - 1 do
begin
total := total + bucketList[i].getSize;
end;
Result := total;
end;
function GHashTable.isEmpty() : boolean;
begin
Result := size() = 0;
end;
procedure GHashTable.hashStats;
var
i : integer;
total : integer;
load : single;
min, max : integer;
begin
total := 0;
min := 65536;
max := 0;
for i := 0 to hashsize - 1 do
begin
total := total + bucketList[i].getSize;
if (bucketList[i].getSize < min) then
min := bucketList[i].getSize;
if (bucketList[i].getSize > max) then
max := bucketList[i].getSize;
end;
load := total / hashsize;
writeln('Hash size ' + inttostr(hashsize) + ' with key ' + inttostr(hashprime));
writeln('Total hash items : ' + inttostr(total));
writeln('Load factor : ' + floattostrf(load, ffFixed, 7, 4));
end;
procedure GHashTable.clear();
var
i : integer;
begin
for i := 0 to hashsize - 1 do
begin
bucketList[i].clean;
end;
end;
constructor GHashTable.Create(size : integer);
var
n : integer;
primes : GPrimes;
begin
inherited Create;
primes := findPrimes(size + 32);
randomize;
hashsize := primes[length(primes) - 1];
hashprime := primes[random(length(primes))];
setlength(bucketList, hashsize);
for n := 0 to hashsize - 1 do
bucketList[n] := GDLinkedList.Create;
hashFunc := defaultHash;
end;
destructor GHashTable.Destroy;
var
n : integer;
begin
for n := 0 to hashsize - 1 do
begin
bucketList[n].clean;
bucketList[n].Free;
end;
setlength(bucketList, 0);
inherited Destroy;
end;
function GHashTable.iterator() : GIterator;
begin
Result := GHashTableIterator.Create(Self);
end;
function hash_string(src : string) : PString;
var
hv : GHashValue;
g : GString;
begin
hv := str_hash._get(src);
if (hv <> nil) then
begin
hash_string := @GString(hv.value).value;
inc(hv.refcount);
end
else
begin
g := GString.Create(src);
str_hash.put(src, g);
hash_string := @g.value;
end;
end;
function hash_string(src : PString) : PString;
var
hv : GHashValue;
g : GString;
begin
hv := str_hash._get(src^);
if (hv <> nil) then
begin
hash_string := @GString(hv.value).value;
inc(hv.refcount);
end
else
begin
g := GString.Create(src^);
str_hash.put(src^, g);
hash_string := @g.value;
end;
end;
procedure unhash_string(var src : PString);
var
hv : GHashValue;
begin
if (src = nil) then
exit;
hv := str_hash._get(src^);
if (hv <> nil) then
begin
dec(hv.refcount);
if (hv.refcount <= 0) then
begin
str_hash.remove(src^);
hv.value.Free;
end;
end;
src := nil;
end;
// GException
constructor GException.Create(location, msg : string);
begin
inherited Create(msg);
end;
procedure GException.show;
begin
{$IFDEF Grendel}
write_console('Exception ' + Message + ' @ ' + e_location);
{$ELSE}
writeln('Exception ' + Message + ' @ ' + e_location);
{$ENDIF}
end;
begin
str_hash := GHashTable.Create(STR_HASH_SIZE);
end.