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 {Should consult Download on non-final files}
43 tSegStatic
=packed object
47 tSeg
=object(tSegStatic
)
50 tSegInfo_ptr
=^tSegInfo
;
51 pSegInfo
=tSegInfo_ptr
;
58 procedure SetSeg(ofs
,len
:LongWord
; state
:boolean);
59 function GetSegLen(ofs
:LongWord
):LongWord
;
62 var SegInfoChain
:^tSegInfo
;
64 type tFileNameVar
=(fvFinal
,fvPart
,fvInfo
);
65 procedure mkfilen(var d
:string; flag
:tFileNameVar
; const fid
:tfid
);
66 function hc(b
:byte):char;
68 if b
<10 then hc
:=char(ord('0')+b
)
69 else hc
:=char(ord('a')-10+b
);
77 for i
:=0 to 19 do begin
78 d
[b
+(i
*2)]:=hc(fid
[i
] shr 4);
79 d
[b
+(i
*2)+1]:=hc(fid
[i
] and $F);
88 function GetSegInfo(const fid
:tFID
):tSegInfo_ptr
;
90 var fh
:file of tSegStatic
;
95 while assigned(result
) do begin
96 if CompareWord(result
^.name
,fid
,10)=0 then goto nocr
;
99 mkfilen(fn
,fvInfo
,fid
);
101 with result
^ do begin
106 SegInfoChain
:=result
;
108 {$I-}ReSet(fh
);{$I+}if ioresult
=0 then begin
109 while not eof(fh
) do begin
122 procedure tStoreObjectInfo
.Open(const fid
:tfid
);
124 mkfilen(filename
,fvFinal
,fid
);
128 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
132 length
:=FileSeek(dh
,0,fsFromEnd
);
133 FileSeek(dh
,0,fsFromBeginning
);
135 mkfilen(filename
,fvPart
,fid
);
137 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
141 length
:=FileSeek(dh
,0,fsFromEnd
);
142 FileSeek(dh
,0,fsFromBeginning
);
143 segi
:=GetSegInfo(fid
);
144 if tSegInfo(segi
^).finalized
then begin
149 Writeln('Store1: open failed for file ',filename
,', ioresult=',IOResult
);
155 procedure tStoreObjectInfo
.EnableWrite(const fid
:tFID
);
157 writeln('Store1: enaling write');
158 assert((dh
=-1)or(not final
));
160 {file was close, create}
161 dh
:=FileCreate(filename
);
163 Writeln('Store1: create failed for file ',filename
,', ioresult=',IOResult
);
165 {init length and segments}
167 segi
:=GetSegInfo(fid
);
170 {file was open, close and reopen rw}
172 dh
:=FileOpen(filename
,fmOpenReadWrite
or fmShareDenyWrite
);
174 if dh
=-1 then rc
:=2 else rc
:=0;
176 procedure tStoreObjectInfo
.SetFLength(len
:LongWord
);
179 //writeln('Store1: SetFLength ',len);
182 FileSeek(dh
,len
,fsFromBeginning
);
183 FileSeek(dh
,0,fsFromBeginning
);
185 procedure tSegInfo
.SetSeg(ofs
,len
:LongWord
; state
:boolean);
190 procedure Dump(c
:char);
193 writeln('Store1: dumpCache ',c
,' ',LongWord(@self
));
194 while assigned(cp
) do begin
195 writeln(cp
^.first
,'-',cp
^.after
);
205 //writeln('Store1: Add: ',ofs,'-',after);
206 while assigned(cp
) do begin
208 if (ofs
<=cp
^.first
)and(after
>=cp
^.after
) then begin
209 {merge complete-encase}
215 if cp
^.after
=ofs
then begin
216 {merge left-matching}
223 if cp
^.first
=after
then begin
224 {merge right-matching}
231 if (after
>cp
^.first
)and(ofs
<=cp
^.first
)and(after
<=cp
^.after
) then begin writeln('k'); after
:=cp
^.first
; end;
232 if (ofs
<cp
^.after
)and(after
>=cp
^.after
)and(ofs
>=cp
^.first
) then begin writeln('l'); ofs
:=cp
^.after
;end;
233 if not op
then pcp
:=@cp
^.next
;
238 if ofs
<>after
then begin
247 procedure tStoreObjectInfo
.WriteSeg(ofs
:LongWord
;len
:word;data
:pointer);
250 FileSeek(dh
,ofs
,fsFromBeginning
);
251 FileWrite(dh
,data
^,len
);
252 tSegInfo(segi
^).SetSeg(ofs
,len
,true);
254 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
; var state
:pointer);
255 var cp
,cp1
,cp2
:^tSeg
;
256 begin with tSegInfo(segi
^) do begin
257 {find seg with lowest base, return 0..base-1}
260 ofs
:=LongWord(state
);
261 cp
:=cache
; while assigned(cp
) do begin
262 if ((cp1
=nil)or(cp
^.first
<cp1
^.first
))and(cp
^.first
>=ofs
) then cp1
:=cp
;
264 if assigned(cp1
) then begin
265 cp
:=cache
; while assigned(cp
) do begin
266 if ((cp2
=nil)or(cp
^.first
<cp2
^.first
))and(cp
^.first
>cp1
^.first
)and(cp
^.first
>=ofs
) then cp2
:=cp
;
268 if assigned(cp2
) then begin
273 len
:=self
.length
-ofs
;
275 end else len
:=self
.length
-ofs
;
276 state
:=pointer(ofs
+len
);
278 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
);
282 GetMiss(ofs
,len
,state
);
286 procedure tStoreObjectInfo
.ReadSeg(into
:pointer; ofs
:LongWord
; len
:word);
291 red
:=FileRead(dh
,into
^,len
);
294 if red
=len
then rc
:=0 else begin
296 writeln('Store1: read ',red
,' out of ',len
,' requested bytes');
300 procedure tSegInfo
.Free
;
301 var fh
:file of tSegStatic
;
305 Dec(refc
); if refc
>0 then begin writeln('Not saving, ',refc
); exit
;end;
306 {save segs, free segs, free}
307 mkfilen(on
,fvInfo
,name
);
309 writeln('Store1: Saving segment info');
311 while assigned(cache
) do begin
313 if not finalized
then write(fh
,cp
^);
318 if finalized
then begin
319 writeln('Store1: segi finalized, renaming datafile, erasing infofile');
320 mkfilen(on
,fvPart
,name
);
321 mkfilen(nn
,fvFinal
,name
);
325 FreeMem(@self
,sizeof(self
));
327 procedure tStoreObjectInfo
.Close
;
329 if assigned(segi
) then tSegInfo(segi
^).Free
;
334 procedure tStoreObjectInfo
.VerifyAndReset
;
335 var ctx
:tSHA1Context
;
336 var digest
:tSHA1Digest
;
337 var buf
: array [1..2048] of byte;
341 if seglen
<length
then begin writeln('Not complete! ',length
-seglen
); exit
;end;
342 {if check segi... then exit};
344 while seglen
>0 do begin
346 if red
>seglen
then red
:=seglen
;
347 red
:=FileRead(dh
,buf
,red
);
349 if red
<0 then exit
; {todo}
350 SHA1Update( ctx
, buf
, red
);
352 SHA1Final( ctx
, digest
);
353 assert(sizeof(digest
)=sizeof(tfid
));
354 if CompareWord(name
,digest
,10)=0 then begin
355 writeln('Store1: hash match');
356 {todo: mark final-verified in segi, rename on segi done}
358 assert(assigned(segi
));
359 with tSegInfo(segi
^) do begin
360 assert( (cache
^.first
=0) and (cache
^.after
=length
) and (cache
^.next
=nil) );
364 {set some invalid values to prevent doing anything}
365 length
:=0; {the object MUST be closed now} seglen
:=0;
366 end else writeln('Hash not matching ',sha1print(digest
),' ',sha1print(name
));
369 function tSegInfo
.GetSegLen(ofs
:LongWord
):LongWord
;
373 while assigned(cp
) do begin
374 if (cp
^.first
<=ofs
)and(cp
^.after
>ofs
) then begin
375 GetSegLen
:=cp
^.after
-ofs
;
381 procedure tStoreObjectInfo
.GetSegAfter(ofs
:LongWord
; out base
:LongWord
; out limit
:LongWord
);
385 cp
:=tSegInfo(segi
^).cache
; {FIXME}
386 while assigned(cp
) do begin
387 if (cp
^.first
>ofs
) then begin
389 limit
:=cp
^.after
-base
-1;
394 procedure tStoreObjectInfo
.SegSeek(ofs
:longword
);
397 if ofs
<=length
then begin
399 if FileSeek(dh
,ofs
,fsFromBeginning
)=ofs
then begin
404 end else if assigned(segi
) then begin
405 seglen
:=tSegInfo(segi
^).GetSegLen(ofs
);
406 if seglen
=0 then rc
:=4 else if FileSeek(dh
,ofs
,fsFromBeginning
)<>ofs
then rc
:=3 else rc
:=0;
410 function tStoreObjectInfo
.SegmentLength(ofs
:LongWord
): LongWord
;
412 if ofs
>self
.length
then begin result
:=0;exit
end;
413 if Final
then result
:=self
.Length
-ofs
else begin
414 result
:=tSegInfo(segi
^).GetSegLen(ofs
);
418 operator
:=(a
:string) r
:tFID
;
420 function unhex(c
:char):byte;
423 if (c
<='F')and(c
>='A') then unhex
:=(ord(c
)-ord('A'))+10
424 else unhex
:=ord(c
)-ord('0');
427 for i
:=0 to 19 do r
[i
]:=(unhex(a
[i
*2+1])shl 4)or(unhex(a
[i
*2+2]));