{*********************************************************

 SlavaNap source code.

 Copyright 2001,2002 by SlavaNap development team
 Released under GNU General Public License

 Latest version is available at
 http://www.slavanap.org

**********************************************************

 Unit: dirs

 Directories list. Each file is splitted in filename/directory
 and directories are stored in one global huge list to make
 memory usage lower by joining duplicates

*********************************************************}
unit dirs;

interface

uses
 Windows, SysUtils, stypes;

type
 PDirectoryItem = ^TDirectoryItem;
 TDirectoryItem = packed record
   directory    : String; // directory name
   crc          : Integer; // directory CRC (StringCRC)
   count        : Integer; // number of files that match directory
   prev         : PDirectoryItem; // previous item in list
   next         : PDirectoryItem; // next item in list
 end;

function AddDirectory(folder: String): PDirectoryItem;
function GetDirectory(dir: PDirectoryItem): String;
procedure DelDirectory(dir: PDirectoryItem);

implementation

uses
 handler, vars;

const
  DIRS_MAX       = 8192;

var
  lists: Array[0..DIRS_MAX-1] of PDirectoryItem; // pointers to first items in arrays

function DirectoryCRC(folder: String): Integer;
var
 len: Integer;
begin // count CRC of folder. for indexing
 tmp_pos:=1589;
 len:=Length(folder);
 result:=len;
 if len>0 then
  inc(result,Ord(folder[1]) shl 3);
 if len>1 then
  inc(result,Ord(folder[2]) shl 6);
 if len>2 then
  inc(result,Ord(folder[3]) shl 9);
 result:=result and (DIRS_MAX-1);
 tmp_pos:=1590;
end;

function AddDirectory(folder: String): PDirectoryItem;
var
 p: PDirectoryItem;
 crc, strcrc: Integer;
 found: Boolean;
begin
 tmp_pos:=1591;
 if Length(folder)<1 then
 begin
  result:=nil;
  exit;
 end;
 crc:=DirectoryCRC(folder);
 strcrc:=StringCRC(folder,false);
 p:=lists[crc];
 found:=false;
 tmp_pos:=1592;
 while not found do
 begin
   if p=nil then
    found:=true
   else
    if p^.crc=strcrc then
     if p^.directory=folder then
       found:=true;
   if not found then
    p:=p^.next;
 end;
 tmp_pos:=1593;
 if p<>nil then
 begin
   inc(p^.count);
   result:=p;
   // set result to first item so next time search would be faster
   if lists[crc]<>p then
   begin
     tmp_pos:=1594;
     p:=lists[crc];
     lists[crc]:=result;
     // deleting "result" from list
     if result^.next<>nil then
      result^.next^.prev:=result^.prev;
     if result^.prev<>nil then
      result^.prev^.next:=result^.next;
     // moving "p" to second place
     p^.prev:=result;
     result^.prev:=nil;
     result^.next:=p;
   end;
   exit;
 end;
 // no such item. creating new one
 tmp_pos:=1595;
 p:=AllocMem(sizeof(TDirectoryItem));
 Pointer(p^.directory):=nil;
 p^.directory:=folder;
 p^.crc:=strcrc;
 p^.count:=1;
 p^.prev:=nil;
 p^.next:=lists[crc];
 if lists[crc]<>nil then
  lists[crc]^.prev:=p;
 lists[crc]:=p;
 result:=p;
 tmp_pos:=1596;
end;

function GetDirectory(dir: PDirectoryItem): String;
begin
 tmp_pos:=1597;
 if dir=nil then result:=''
 else result:=dir^.directory;
 tmp_pos:=1598;
end;

procedure DelDirectory(dir: PDirectoryItem);
var
 crc: Integer;
begin
 tmp_pos:=1599;
 if (dir=nil) or (not running) then exit;
 dec(dir^.count);
 if dir^.count>0 then exit;
 // item is empty. deleting it.
 tmp_pos:=1600;
 if dir^.next<>nil then
  dir^.next^.prev:=dir^.prev;
 if dir^.prev<>nil then
  dir^.prev^.next:=dir^.next
 else // first item in list
 begin
   crc:=DirectoryCRC(dir^.directory);
   lists[crc]:=dir^.next;
 end;
 tmp_pos:=1601;
 dir^.directory:='';
 FreeMem(dir,sizeof(TDirectoryItem));
 tmp_pos:=1602;
end;

procedure InitDirs;
var
 i: Integer;
begin
 for i:=0 to DIRS_MAX-1 do
  lists[i]:=nil;
end;

procedure FreeDirs;
begin
 // right now all items are freed when it is unshared. 
end;

initialization
begin
  InitDirs;
end;

finalization
begin
  FreeDirs;
end;

end.
