unit dtypes; interface uses SysUtils, SyncObjs; type GListNode = class prev, next : GListNode; element : pointer; refcount : integer; constructor Create(e : pointer; p, n : GListNode); end; GDLinkedList = class size : integer; head, tail : GListnode; lock : TCriticalSection; 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; constructor Create; destructor Destroy; override; end; GPrimes = array of integer; GString = class refcount : integer; value : string; constructor Create(s : string); end; GHASH_FUNC = function(size, prime : cardinal; key : string) : integer; GHashTable = class hashsize : cardinal; hashprime : cardinal; bucketList : array of GDLinkedList; hashFunc : GHASH_FUNC; function getUsed : integer; function getHash(key : string) : integer; procedure setHashFunc(func : GHASH_FUNC); function findPrimes(n : integer) : GPrimes; procedure hashStats; virtual; procedure hashPointer(ptr : pointer; key : string); constructor Create(size : integer); destructor Destroy; override; end; GHashObject = class(GHashTable) procedure hashObject(obj : TObject; key : string); end; GHashString = class(GHashTable) procedure hashString(str : GString; key : string); procedure hashStats; override; end; GException = class(Exception) e_location : string; constructor Create(location, msg : string); procedure show; end; const STR_HASH_SIZE = 1024; var str_hash : GHashString; 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 uses mudsystem; // GListNode constructor GListNode.Create(e : pointer; p, n : GListNode); begin inherited Create; element := e; next := n; prev := p; refcount := 1; end; // GDLinkedList constructor GDLinkedList.Create; begin inherited Create; head := nil; tail := nil; size := 0; 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); 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(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(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); 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; // GString constructor GString.Create(s : string); begin inherited Create; value := s; 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 : string) : integer; begin Result := hashFunc(hashsize, hashprime, key); end; procedure GHashTable.setHashFunc(func : GHASH_FUNC); begin hashFunc := func; end; procedure GHashTable.hashPointer(ptr : pointer; key : string); var hash : integer; begin hash := getHash(key); bucketList[hash].insertLast(ptr); end; function GHashTable.getUsed : integer; var i : integer; total : integer; begin total := 0; for i := 0 to hashsize - 1 do begin total := total + bucketList[i].getSize; end; getUsed := total; 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; 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; // GHashObject procedure GHashObject.hashObject(obj : TObject; key : string); begin hashPointer(obj, key); end; // GHashString procedure GHashString.hashString(str : GString; key : string); begin hashPointer(str, key); end; procedure GHashString.hashStats; var bytesused, wouldhave : integer; i, s : cardinal; node : GListNode; begin inherited hashStats; bytesused := 0; wouldhave := 0; for i := 0 to hashsize - 1 do begin node := bucketList[i].head; while (node <> nil) do begin s := length(GString(node.element).value); bytesused := bytesused + (s + 1); wouldhave := wouldhave + (node.refcount * (s + 1)); node := node.next; end; end; writeln('Byte savings (used/saved): ', inttostr(bytesused), '/', inttostr(wouldhave - bytesused), ' (', (bytesused * 100) div wouldhave, '%/', ((wouldhave - bytesused) * 100) div wouldhave, '%)'); end; function hash_string(src : string) : PString; var hash : integer; node, fnode : GListNode; g : GString; begin hash := str_hash.getHash(src); node := str_hash.bucketList[hash].head; fnode := nil; while (node <> nil) do begin if (comparestr(GString(node.element).value, src) = 0) then begin fnode := node; break; end; node := node.next; end; if (fnode <> nil) then begin g := fnode.element; hash_string := @g.value; fnode.refcount := fnode.refcount + 1; end else begin g := GString.Create(src); str_hash.bucketList[hash].insertLast(g); hash_string := @g.value; end; end; function hash_string(src : PString) : PString; var hash : integer; node, fnode : GListNode; g : GString; begin hash := str_hash.getHash(src^); node := str_hash.bucketList[hash].head; fnode := nil; while (node <> nil) do begin if (comparestr(GString(node.element).value, src^) = 0) then begin fnode := node; break; end; node := node.next; end; if (fnode <> nil) then begin g := fnode.element; hash_string := @g.value; fnode.refcount := fnode.refcount + 1; end else begin g := GString.Create(src^); str_hash.bucketList[hash].insertLast(g); hash_string := @g.value; end; end; procedure unhash_string(var src : PString); var hash : integer; node, fnode : GListNode; g : GString; begin if (src = nil) then exit; hash := str_hash.getHash(src^); node := str_hash.bucketList[hash].head; fnode := nil; while (node <> nil) do begin if (comparestr(GString(node.element).value, src^) = 0) then begin fnode := node; break; end; node := node.next; end; if (fnode <> nil) then begin dec(fnode.refcount); if (fnode.refcount <= 0) then begin GString(fnode.element).Free; str_hash.bucketList[hash].remove(fnode); end; src := nil; end; end; // GException constructor GException.Create(location, msg : string); begin inherited Create(msg); end; procedure GException.show; begin write_console('Exception ' + Message + ' @ ' + e_location); end; begin str_hash := GHashString.Create(STR_HASH_SIZE); end.