diff --git a/src/sfs/sfs.pas b/src/sfs/sfs.pas
index 0f2ff81379c0802bb6936867fa1ee693d17dc1e5..157fbac3c2ccb3d20d5a811fecc34e9a1b6ea142 100644 (file)
--- a/src/sfs/sfs.pas
+++ b/src/sfs/sfs.pas
// streaming file system (virtual)
{$MODE DELPHI}
{.$R-}
+{.$DEFINE SFS_VOLDEBUG}
unit sfs;
interface
// ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà.
function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+// äîáàâèòü ñáîðíèê âðåìåííî
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+
// äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds.
// åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà
// óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè.
// ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-)
function SFSFileList (const dataFileName: TSFSString): TSFSFileList;
+// çàïðåòèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCDisable ();
+
+// ðàçðåøèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCEnable ();
+
+// for completeness sake
+procedure sfsGCCollect ();
+
function SFSReplacePathDelims (const s: TSFSString; newDelim: TSFSChar): TSFSString;
-// èãíîðèðóåò ðåãèñòð ñèìâîëîâ
-function SFSStrEqu (const s0, s1: TSFSString): Boolean;
// ðàçîáðàòü òîëñòîå èìÿ ôàéëà, âåðíóòü âèðòóàëüíîå èìÿ ïîñëåäíåãî ñïèñêà
// èëè ïóñòóþ ñòîðîêó, åñëè ñïèñêîâ íå áûëî.
function SFSGetLastVirtualName (const fn: TSFSString): string;
-// ïðåîáðàçîâàòü ÷èñëî â ñòðîêó, êðàñèâî ðàçáàâëÿÿ çàïÿòûìè
-function Int64ToStrComma (i: Int64): string;
-
-// `name` will be modified
-// return `true` if file was found
-function sfsFindFileCI (path: string; var name: string): Boolean;
-
// Wildcard matching
// this code is meant to allow wildcard pattern matches. tt is VERY useful
// for matching filename wildcard patterns. tt allows unix grep-like pattern
function WildListMatch (wildList, text: TSFSString; delimChar: AnsiChar=':'): Integer;
function HasWildcards (const pattern: TSFSString): Boolean;
-// this will compare only last path element from sfspath
-function SFSDFPathEqu (sfspath: string; path: string): Boolean;
-
-function SFSUpCase (ch: Char): Char;
-
var
// ïðàâäà: ðàçðåøåíî èñêàòü ôàéëî íå òîëüêî â ôàéëàõ äàííûõ, íî è íà äèñêå.
implementation
uses
- xstreams;
-
-
-function Int64ToStrComma (i: Int64): string;
-var
- f: Integer;
-begin
- Str(i, result);
- f := Length(result)+1;
- while f > 4 do
- begin
- Dec(f, 3); Insert(',', result, f);
- end;
-end;
-
-
-// `name` will be modified
-function sfsFindFileCI (path: string; var name: string): Boolean;
-var
- sr: TSearchRec;
- bestname: string = '';
-begin
- if length(path) = 0 then path := '.';
- while (length(path) > 0) and (path[length(path)] = '/') do Delete(path, length(path), 1);
- if (length(path) = 0) or (path[length(path)] <> '/') then path := path+'/';
- if FileExists(path+name) then begin result := true; exit; end;
- if FindFirst(path+'*', faAnyFile, sr) = 0 then
- repeat
- if (sr.name = '.') or (sr.name = '..') then continue;
- if (sr.attr and faDirectory) <> 0 then continue;
- if sr.name = name then
- begin
- FindClose(sr);
- result := true;
- exit;
- end;
- if (length(bestname) = 0) and SFSStrEqu(sr.name, name) then bestname := sr.name;
- until FindNext(sr) <> 0;
- FindClose(sr);
- if length(bestname) > 0 then begin result := true; name := bestname; end else result := false;
-end;
+ xstreams, utils;
const
var
factories: TObjectList; // TSFSVolumeFactory
volumes: TObjectList; // TVolumeInfo
+ gcdisabled: Integer = 0; // >0: disabled
+
+
+procedure sfsGCCollect ();
+var
+ f, c: Integer;
+ vi: TVolumeInfo;
+ used: Boolean;
+begin
+ // collect garbage
+ f := 0;
+ while f < volumes.Count do
+ begin
+ vi := TVolumeInfo(volumes[f]);
+ if vi = nil then continue;
+ if (not vi.fPermanent) and (vi.fVolume.fRC = 0) and (vi.fOpenedFilesCount = 0) then
+ begin
+ // this volume probably can be removed
+ used := false;
+ c := volumes.Count-1;
+ while not used and (c >= 0) do
+ begin
+ if (c <> f) and (volumes[c] <> nil) then
+ begin
+ used := (TVolumeInfo(volumes[c]).fStream = vi.fStream);
+ if not used then used := (TVolumeInfo(volumes[c]).fVolume.fFileStream = vi.fStream);
+ if used then break;
+ end;
+ Dec(c);
+ end;
+ if not used then
+ begin
+ {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+ volumes.extract(vi); // remove from list
+ vi.Free; // and kill
+ f := 0;
+ continue;
+ end;
+ end;
+ Inc(f); // next volume
+ end;
+end;
+
+procedure sfsGCDisable ();
+begin
+ Inc(gcdisabled);
+end;
+
+procedure sfsGCEnable ();
+begin
+ Dec(gcdisabled);
+ if gcdisabled <= 0 then
+ begin
+ gcdisabled := 0;
+ sfsGCCollect();
+ end;
+end;
// ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
vi := TVolumeInfo(volumes[f]);
if not onlyPerm or vi.fPermanent then
begin
- if SFSStrEqu(vi.fPackName, dataFileName) then
+ if StrEquCI1251(vi.fPackName, dataFileName) then
begin
result := f;
exit;
end;
end;
-function SFSUpCase (ch: Char): Char;
-begin
- if ch < #128 then
- begin
- if (ch >= 'a') and (ch <= 'z') then Dec(ch, 32);
- end
- else
- begin
- if (ch >= #224) and (ch <= #255) then
- begin
- Dec(ch, 32);
- end
- else
- begin
- case ch of
- #184, #186, #191: Dec(ch, 16);
- #162, #179: Dec(ch);
- end;
- end;
- end;
- result := ch;
-end;
-
-function SFSStrEqu (const s0, s1: TSFSString): Boolean;
-var
- i: Integer;
-begin
- //result := (AnsiCompareText(s0, s1) == 0);
- result := false;
- if length(s0) <> length(s1) then exit;
- for i := 1 to length(s0) do
- begin
- if SFSUpCase(s0[i]) <> SFSUpCase(s1[i]) then exit;
- end;
- result := true;
-end;
-
-// this will compare only last path element from sfspath
-function SFSDFPathEqu (sfspath: string; path: string): Boolean;
-{var
- i: Integer;}
-begin
- result := SFSStrEqu(sfspath, path);
-(*
- if not result and (length(sfspath) > 1) then
- begin
- i := length(sfspath);
- while i > 1 do
- begin
- while (i > 1) and (sfspath[i-1] <> '/') do Dec(i);
- if i <= 1 then exit;
- writeln('{', sfspath, '} [', Copy(sfspath, i, length(sfspath)), '] : [', path, ']');
- result := SFSStrEqu(Copy(sfspath, i, length(sfspath)), path);
- end;
- end;
-*)
-end;
// adds '/' too
function normalizePath (fn: string): string;
if fOwner <> nil then
begin
Dec(fOwner.fOpenedFilesCount);
- if not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
+ if (gcdisabled = 0) and not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
begin
f := volumes.IndexOf(fOwner);
- if f <> -1 then volumes[f] := nil; // this will destroy the volume
+ if f <> -1 then
+ begin
+ {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+ volumes[f] := nil; // this will destroy the volume
+ end;
end;
end;
end;
ReadDirectory();
fFiles.Pack();
- for f := 0 to fFiles.Count-1 do
+ f := 0;
+ while f < fFiles.Count do
begin
sfi := TSFSFileInfo(fFiles[f]);
// normalize name & path
sfi.fPath := sfi.fPath+tmp;
end;
sfi.fPath := normalizePath(sfi.fPath);
+ if (length(sfi.fPath) = 0) and (length(sfi.fName) = 0) then sfi.Free else Inc(f);
end;
removeCommonPath();
end;
Dec(result);
if fFiles[result] <> nil then
begin
- if SFSStrEqu(fPath, TSFSFileInfo(fFiles[result]).fPath) and
- SFSStrEqu(fName, TSFSFileInfo(fFiles[result]).fName) then exit;
+ if StrEquCI1251(fPath, TSFSFileInfo(fFiles[result]).fPath) and
+ StrEquCI1251(fName, TSFSFileInfo(fFiles[result]).fName) then exit;
end;
end;
result := -1;
if fVolume <> nil then Dec(fVolume.fRC);
Dec(TVolumeInfo(volumes[f]).fOpenedFilesCount);
// óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
- if not TVolumeInfo(volumes[f]).fPermanent and
- (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then volumes[f] := nil;
+ if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then
+ begin
+ {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+ volumes[f] := nil;
+ end;
inherited Destroy();
end;
except
FreeAndNil(st);
// óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
- if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
+ if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
raise;
end;
// óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
end;
if ds <> nil then st := ds
- else st := TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite);
+ else st := TFileStream.Create(fn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone);
st.Position := 0;
volumes.Pack();
vi.fOpenedFilesCount := 0;
end;
-function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream;
- top: Boolean = false): Boolean;
+function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream; top: Boolean=false): Boolean;
var
tv: Integer;
begin
end;
end;
-function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean = false): Boolean;
+function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
var
tv: Integer;
begin
end;
end;
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+var
+ tv: Integer;
+begin
+ try
+ if top then tv := -1 else tv := 1;
+ SFSAddDataFileEx(dataFileName, nil, tv, 0);
+ result := true;
+ except
+ result := false;
+ end;
+end;
+
+
function SFSExpandDirName (const s: TSFSString): TSFSString;
var
cdir := SFSReplacePathDelims(SFSExpandDirName(cdir), '/');
if cdir[Length(cdir)] <> '/' then cdir := cdir+'/';
try
- result := TFileStream.Create(cdir+dfn, fmOpenRead or fmShareDenyWrite);
+ result := TFileStream.Create(cdir+dfn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone);
exit;
except
end;
ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true);
except
result.Free();
- if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+ if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
result := CheckDisk(); // îáëîì ñ datafile, ïðîâåðèì äèñê
if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
exit;
result := TSFSFileList.Create(vi.fVolume);
Inc(vi.fVolume.fRC);
except
- if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+ if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
end;
end;