2 {Take tracks of files in store}
3 {just simple, no cleaning, etc}
8 tfid
=array [0..19] of byte;
9 tStoreObjectInfo
=object
10 final
:boolean; {hash matched}
11 rc
:Word; {0=no error 1=not found, other}
12 length
:LongWord
; {the whole file}
13 seglen
:longword
; {from cur to end of segment}
14 offset
:LongWord
; {only valid when reading}
17 procedure Open(const fid
:tfid
);
19 procedure ReadSeg(into
:pointer; ofs
:LongWord
; len
:word);
20 function SegmentLength(ofs
:LongWord
): LongWord
;
21 procedure GetSegAfter(ofs
:LongWord
; out base
:LongWord
; out limit
:LongWord
);
22 procedure EnableWrite(const fid
:tFID
);
23 procedure SetFLength(len
:LongWord
);
24 procedure WriteSeg(ofs
:LongWord
;len
:word;data
:pointer);
25 procedure VerifyAndReset
;
26 procedure GetMiss(out ofs
:LongWord
; out len
:LongWord
; var state
:pointer); unimplemented
;
27 procedure GetMiss(out ofs
:LongWord
; out len
:LongWord
); deprecated
;
29 dh
:tHandle
; {handle to the data file}
31 segi
:pointer{to seg info obj};
32 procedure SegSeek(ofs
:longword
); deprecated
;
34 tObjectInfo
=tStoreObjectInfo
;
36 operator
:=(a
:string) r
:tFID
;
37 operator
:=(a
:tFID
) r
:string;
38 {Should consult Download on non-final files}
44 tSegStatic
=packed object
48 tSeg
=object(tSegStatic
)
51 tSegInfo_ptr
=^tSegInfo
;
52 pSegInfo
=tSegInfo_ptr
;
59 procedure SetSeg(ofs
,len
:LongWord
; state
:boolean);
60 function GetSegLen(ofs
:LongWord
):LongWord
;
63 var SegInfoChain
:^tSegInfo
;
65 type tFileNameVar
=(fvFinal
,fvPart
,fvInfo
);
66 procedure mkfilen(var d
:string; flag
:tFileNameVar
; const fid
:tfid
);
67 function hc(b
:byte):char;
69 if b
<10 then hc
:=char(ord('0')+b
)
70 else hc
:=char(ord('a')-10+b
);
78 for i
:=0 to 19 do begin
79 d
[b
+(i
*2)]:=hc(fid
[i
] shr 4);
80 d
[b
+(i
*2)+1]:=hc(fid
[i
] and $F);
89 function GetSegInfo(const fid
:tFID
):tSegInfo_ptr
;
91 var fh
:file of tSegStatic
;
96 while assigned(result
) do begin
97 if CompareWord(result
^.name
,fid
,10)=0 then goto nocr
;
100 mkfilen(fn
,fvInfo
,fid
);
102 with result
^ do begin
107 SegInfoChain
:=result
;
109 {$I-}ReSet(fh
);{$I+}if ioresult
=0 then begin
110 while not eof(fh
) do begin
123 procedure tStoreObjectInfo
.Open(const fid
:tfid
);
125 mkfilen(filename
,fvFinal
,fid
);
129 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
133 length
:=FileSeek(dh
,0,fsFromEnd
);
134 FileSeek(dh
,0,fsFromBeginning
);
136 mkfilen(filename
,fvPart
,fid
);
138 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
142 length
:=FileSeek(dh
,0,fsFromEnd
);
143 FileSeek(dh
,0,fsFromBeginning
);
144 segi
:=GetSegInfo(fid
);
145 if tSegInfo(segi
^).finalized
then begin
150 Writeln('Store1: open failed for file ',filename
,', ioresult=',IOResult
);
156 procedure tStoreObjectInfo
.EnableWrite(const fid
:tFID
);
158 writeln('Store1: enaling write');
159 assert((dh
=-1)or(not final
));
161 {file was close, create}
162 dh
:=FileCreate(filename
);
164 Writeln('Store1: create failed for file ',filename
,', ioresult=',IOResult
);
166 {init length and segments}
168 segi
:=GetSegInfo(fid
);
171 {file was open, close and reopen rw}
173 dh
:=FileOpen(filename
,fmOpenReadWrite
or fmShareDenyWrite
);
175 if dh
=-1 then rc
:=2 else rc
:=0;
177 procedure tStoreObjectInfo
.SetFLength(len
:LongWord
);
180 //writeln('Store1: SetFLength ',len);
183 FileSeek(dh
,len
,fsFromBeginning
);
184 FileSeek(dh
,0,fsFromBeginning
);
186 procedure tSegInfo
.SetSeg(ofs
,len
:LongWord
; state
:boolean);
191 procedure Dump(c
:char);
194 writeln('Store1: dumpCache ',c
,' ',LongWord(@self
));
195 while assigned(cp
) do begin
196 writeln(cp
^.first
,'-',cp
^.after
);
206 //writeln('Store1: Add: ',ofs,'-',after);
207 while assigned(cp
) do begin
209 if (ofs
<=cp
^.first
)and(after
>=cp
^.after
) then begin
210 {merge complete-encase}
216 if cp
^.after
=ofs
then begin
217 {merge left-matching}
224 if cp
^.first
=after
then begin
225 {merge right-matching}
232 if (after
>cp
^.first
)and(ofs
<=cp
^.first
)and(after
<=cp
^.after
) then begin writeln('k'); after
:=cp
^.first
; end;
233 if (ofs
<cp
^.after
)and(after
>=cp
^.after
)and(ofs
>=cp
^.first
) then begin writeln('l'); ofs
:=cp
^.after
;end;
234 if not op
then pcp
:=@cp
^.next
;
239 if ofs
<>after
then begin
248 procedure tStoreObjectInfo
.WriteSeg(ofs
:LongWord
;len
:word;data
:pointer);
251 FileSeek(dh
,ofs
,fsFromBeginning
);
252 FileWrite(dh
,data
^,len
);
253 tSegInfo(segi
^).SetSeg(ofs
,len
,true);
255 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
; var state
:pointer);
256 var cp
,cp1
,cp2
:^tSeg
;
257 begin with tSegInfo(segi
^) do begin
258 {find seg with lowest base, return 0..base-1}
261 ofs
:=LongWord(state
);
262 cp
:=cache
; while assigned(cp
) do begin
263 if ((cp1
=nil)or(cp
^.first
<cp1
^.first
))and(cp
^.first
>=ofs
) then cp1
:=cp
;
265 if assigned(cp1
) then begin
266 cp
:=cache
; while assigned(cp
) do begin
267 if ((cp2
=nil)or(cp
^.first
<cp2
^.first
))and(cp
^.first
>cp1
^.first
)and(cp
^.first
>=ofs
) then cp2
:=cp
;
269 if assigned(cp2
) then begin
274 len
:=self
.length
-ofs
;
276 end else len
:=self
.length
-ofs
;
277 state
:=pointer(ofs
+len
);
279 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
);
283 GetMiss(ofs
,len
,state
);
287 procedure tStoreObjectInfo
.ReadSeg(into
:pointer; ofs
:LongWord
; len
:word);
292 red
:=FileRead(dh
,into
^,len
);
295 if red
=len
then rc
:=0 else begin
297 writeln('Store1: read ',red
,' out of ',len
,' requested bytes');
301 procedure tSegInfo
.Free
;
302 var fh
:file of tSegStatic
;
306 Dec(refc
); if refc
>0 then begin writeln('Not saving, ',refc
); exit
;end;
307 {save segs, free segs, free}
308 mkfilen(on
,fvInfo
,name
);
310 writeln('Store1: Saving segment info');
312 while assigned(cache
) do begin
314 if not finalized
then write(fh
,cp
^);
319 if finalized
then begin
320 writeln('Store1: segi finalized, renaming datafile, erasing infofile');
321 mkfilen(on
,fvPart
,name
);
322 mkfilen(nn
,fvFinal
,name
);
326 FreeMem(@self
,sizeof(self
));
328 procedure tStoreObjectInfo
.Close
;
330 if assigned(segi
) then tSegInfo(segi
^).Free
;
335 procedure tStoreObjectInfo
.VerifyAndReset
;
336 var ctx
:tSHA1Context
;
337 var digest
:tSHA1Digest
;
338 var buf
: array [1..2048] of byte;
342 if seglen
<length
then begin writeln('Not complete! ',length
-seglen
); exit
;end;
343 {if check segi... then exit};
345 while seglen
>0 do begin
347 if red
>seglen
then red
:=seglen
;
348 red
:=FileRead(dh
,buf
,red
);
350 if red
<0 then exit
; {todo}
351 SHA1Update( ctx
, buf
, red
);
353 SHA1Final( ctx
, digest
);
354 assert(sizeof(digest
)=sizeof(tfid
));
355 if CompareWord(name
,digest
,10)=0 then begin
356 writeln('Store1: hash match');
357 {todo: mark final-verified in segi, rename on segi done}
359 assert(assigned(segi
));
360 with tSegInfo(segi
^) do begin
361 assert( (cache
^.first
=0) and (cache
^.after
=length
) and (cache
^.next
=nil) );
365 {set some invalid values to prevent doing anything}
366 length
:=0; {the object MUST be closed now} seglen
:=0;
367 end else writeln('Hash not matching ',sha1print(digest
),' ',sha1print(name
));
370 function tSegInfo
.GetSegLen(ofs
:LongWord
):LongWord
;
374 while assigned(cp
) do begin
375 if (cp
^.first
<=ofs
)and(cp
^.after
>ofs
) then begin
376 GetSegLen
:=cp
^.after
-ofs
;
382 procedure tStoreObjectInfo
.GetSegAfter(ofs
:LongWord
; out base
:LongWord
; out limit
:LongWord
);
386 cp
:=tSegInfo(segi
^).cache
; {FIXME}
387 while assigned(cp
) do begin
388 if (cp
^.first
>ofs
) then begin
390 limit
:=cp
^.after
-base
-1;
395 procedure tStoreObjectInfo
.SegSeek(ofs
:longword
);
398 if ofs
<=length
then begin
400 if FileSeek(dh
,ofs
,fsFromBeginning
)=ofs
then begin
405 end else if assigned(segi
) then begin
406 seglen
:=tSegInfo(segi
^).GetSegLen(ofs
);
407 if seglen
=0 then rc
:=4 else if FileSeek(dh
,ofs
,fsFromBeginning
)<>ofs
then rc
:=3 else rc
:=0;
411 function tStoreObjectInfo
.SegmentLength(ofs
:LongWord
): LongWord
;
413 if ofs
>self
.length
then begin result
:=0;exit
end;
414 if Final
then result
:=self
.Length
-ofs
else begin
415 result
:=tSegInfo(segi
^).GetSegLen(ofs
);
419 operator
:=(a
:string) r
:tFID
;
421 function unhex(c
:char):byte;
424 if (c
<='F')and(c
>='A') then unhex
:=(ord(c
)-ord('A'))+10
425 else unhex
:=ord(c
)-ord('0');
428 for i
:=0 to 19 do r
[i
]:=(unhex(a
[i
*2+1])shl 4)or(unhex(a
[i
*2+2]));
430 operator
:=(a
:tFID
) r
:string;