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}
41 const prefix
='object';
44 tSegStatic
=packed object
48 tSeg
=object(tSegStatic
)
51 tSegInfo_ptr
=^tSegInfo
;
52 pSegInfo
=tSegInfo_ptr
;
58 procedure SetSeg(ofs
,len
:LongWord
; state
:boolean);
59 function GetSegLen(ofs
:LongWord
):LongWord
;
62 var SegInfoChain
:^tSegInfo
;
65 procedure mkfilen(var d
:string; flag
:char; 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);
83 function GetSegInfo(const fid
:tFID
):tSegInfo_ptr
;
85 var fh
:file of tSegStatic
;
90 while assigned(result
) do begin
91 if CompareWord(result
^.name
,fid
,10)=0 then goto nocr
;
101 SegInfoChain
:=result
;
103 {$I-}ReSet(fh
);{$I+}if ioresult
=0 then begin
104 while not eof(fh
) do begin
117 procedure tStoreObjectInfo
.Open(const fid
:tfid
);
119 mkfilen(filename
,'f',fid
);
123 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
127 length
:=FileSeek(dh
,0,fsFromEnd
);
128 FileSeek(dh
,0,fsFromBeginning
);
130 mkfilen(filename
,'p',fid
);
132 dh
:=FileOpen(filename
,fmOpenRead
or fmShareDenyWrite
);
136 length
:=FileSeek(dh
,0,fsFromEnd
);
137 FileSeek(dh
,0,fsFromBeginning
);
138 segi
:=GetSegInfo(fid
);
140 Writeln('Store1: open failed for file ',filename
,', ioresult=',IOResult
);
146 procedure tStoreObjectInfo
.EnableWrite(const fid
:tFID
);
148 writeln('Store1: enaling write');
149 assert((dh
=-1)or(not final
));
151 {file was close, create}
152 dh
:=FileCreate(filename
);
154 Writeln('Store1: create failed for file ',filename
,', ioresult=',IOResult
);
156 {init length and segments}
158 segi
:=GetSegInfo(fid
);
161 {file was open, close and reopen rw}
163 dh
:=FileOpen(filename
,fmOpenReadWrite
or fmShareDenyWrite
);
165 if dh
=-1 then rc
:=2 else rc
:=0;
167 procedure tStoreObjectInfo
.SetFLength(len
:LongWord
);
170 //writeln('Store1: SetFLength ',len);
173 FileSeek(dh
,len
,fsFromBeginning
);
174 FileSeek(dh
,0,fsFromBeginning
);
176 procedure tSegInfo
.SetSeg(ofs
,len
:LongWord
; state
:boolean);
181 procedure Dump(c
:char);
184 writeln('Store1: dumpCache ',c
,' ',LongWord(@self
));
185 while assigned(cp
) do begin
186 writeln(cp
^.first
,'-',cp
^.after
);
196 //writeln('Store1: Add: ',ofs,'-',after);
197 while assigned(cp
) do begin
199 if (ofs
<=cp
^.first
)and(after
>=cp
^.after
) then begin
200 {merge complete-encase}
206 if cp
^.after
=ofs
then begin
207 {merge left-matching}
214 if cp
^.first
=after
then begin
215 {merge right-matching}
222 if (after
>cp
^.first
)and(ofs
<=cp
^.first
)and(after
<=cp
^.after
) then begin writeln('k'); after
:=cp
^.first
; end;
223 if (ofs
<cp
^.after
)and(after
>=cp
^.after
)and(ofs
>=cp
^.first
) then begin writeln('l'); ofs
:=cp
^.after
;end;
224 if not op
then pcp
:=@cp
^.next
;
229 if ofs
<>after
then begin
238 procedure tStoreObjectInfo
.WriteSeg(ofs
:LongWord
;len
:word;data
:pointer);
241 FileSeek(dh
,ofs
,fsFromBeginning
);
242 FileWrite(dh
,data
^,len
);
243 tSegInfo(segi
^).SetSeg(ofs
,len
,true);
245 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
; var state
:pointer);
246 var cp
,cp1
,cp2
:^tSeg
;
247 begin with tSegInfo(segi
^) do begin
248 {find seg with lowest base, return 0..base-1}
251 ofs
:=LongWord(state
);
252 cp
:=cache
; while assigned(cp
) do begin
253 if ((cp1
=nil)or(cp
^.first
<cp1
^.first
))and(cp
^.first
>=ofs
) then cp1
:=cp
;
255 if assigned(cp1
) then begin
256 cp
:=cache
; while assigned(cp
) do begin
257 if ((cp2
=nil)or(cp
^.first
<cp2
^.first
))and(cp
^.first
>cp1
^.first
)and(cp
^.first
>=ofs
) then cp2
:=cp
;
259 if assigned(cp2
) then begin
264 len
:=self
.length
-ofs
;
266 end else len
:=self
.length
-ofs
;
267 state
:=pointer(ofs
+len
);
269 procedure tStoreObjectInfo
.GetMiss(out ofs
:LongWord
; out len
:LongWord
);
273 GetMiss(ofs
,len
,state
);
277 procedure tStoreObjectInfo
.ReadSeg(into
:pointer; ofs
:LongWord
; len
:word);
282 red
:=FileRead(dh
,into
^,len
);
285 if red
=len
then rc
:=0 else begin
287 writeln('Store1: read ',red
,' out of ',len
,' requested bytes');
291 procedure tSegInfo
.Free
;
293 var fh
:file of tSegStatic
;
296 Dec(refc
); if refc
>0 then begin writeln('Not saving, ',refc
); exit
;end;
297 {save segs, free segs, free}
298 writeln('Store1: Saving segment info');
299 mkfilen(fn
,'i',name
);
302 while assigned(cache
) do begin
308 FreeMem(@self
,sizeof(self
));
310 procedure tStoreObjectInfo
.Close
;
312 if assigned(segi
) then tSegInfo(segi
^).Free
;
315 procedure tStoreObjectInfo
.VerifyAndReset
;
316 var ctx
:tSHA1Context
;
317 var digest
:tSHA1Digest
;
318 var buf
: array [1..2048] of byte;
323 if seglen
<length
then begin writeln('Not complete! ',length
-seglen
); exit
;end;
324 {if check segi... then exit};
326 while seglen
>0 do begin
328 if red
>seglen
then red
:=seglen
;
329 red
:=FileRead(dh
,buf
,red
);
331 if red
<0 then exit
; {todo}
332 SHA1Update( ctx
, buf
, red
);
334 SHA1Final( ctx
, digest
);
335 assert(sizeof(digest
)=sizeof(tfid
));
336 if CompareWord(name
,digest
,10)=0 then begin
337 writeln('Store1: hash match, renaming, not deleting infofile');
341 mkfilen(on
,'p',name
);
342 mkfilen(nn
,'f',name
);
344 (*mkfilen(on,'i',name);
346 {set some invalid values to prevent doing anything}
347 length
:=0; {the object MUST be closed now} seglen
:=0;
348 end else writeln('Hash not matching ',sha1print(digest
),' ',sha1print(name
));
351 function tSegInfo
.GetSegLen(ofs
:LongWord
):LongWord
;
355 while assigned(cp
) do begin
356 if (cp
^.first
<=ofs
)and(cp
^.after
>ofs
) then begin
357 GetSegLen
:=cp
^.after
-ofs
;
363 procedure tStoreObjectInfo
.GetSegAfter(ofs
:LongWord
; out base
:LongWord
; out limit
:LongWord
);
367 cp
:=tSegInfo(segi
^).cache
; {FIXME}
368 while assigned(cp
) do begin
369 if (cp
^.first
>ofs
) then begin
371 limit
:=cp
^.after
-base
-1;
376 procedure tStoreObjectInfo
.SegSeek(ofs
:longword
);
379 if ofs
<=length
then begin
381 if FileSeek(dh
,ofs
,fsFromBeginning
)=ofs
then begin
386 end else if assigned(segi
) then begin
387 seglen
:=tSegInfo(segi
^).GetSegLen(ofs
);
388 if seglen
=0 then rc
:=4 else if FileSeek(dh
,ofs
,fsFromBeginning
)<>ofs
then rc
:=3 else rc
:=0;
392 function tStoreObjectInfo
.SegmentLength(ofs
:LongWord
): LongWord
;
394 if ofs
>self
.length
then begin result
:=0;exit
end;
395 if Final
then result
:=self
.Length
-ofs
else begin
396 result
:=tSegInfo(segi
^).GetSegLen(ofs
);
400 operator
:=(a
:string) r
:tFID
;
402 function unhex(c
:char):byte;
405 if (c
<='F')and(c
>='A') then unhex
:=(ord(c
)-ord('A'))+10
406 else unhex
:=ord(c
)-ord('0');
409 for i
:=0 to 19 do r
[i
]:=(unhex(a
[i
*2+1])shl 4)or(unhex(a
[i
*2+2]));