{ Demo at the end of this unit } { Program by Miguel Angel Cand˘n Last revision date 09-01-93 miguel_angel@jet } (* First, excuse me, I don't speak english. 1024 bytes per page, Max keys in page = 1014 div (4+LenKey) Min keys in page = Max keys in page div 2 Every key (if not level 0), have another key page One page reserved for swaps. if (LenKey = 10) and (PagesInMemory = 16) the index file can have 36^14 keys up to 72^14. MaxPagesInMemory : 4..63 = 16; { Levels per index file } Return: RecIndex : LongInt ==> rec number in data file ErrIndex : word ==> if <> 0 => errors ... procedure OpenIndex( var CualIndex:PtrIndex; const NomFile:PathStr ); procedure CreateIndex( var CualIndex:PtrIndex; const NomFile:PathStr; CualKeyLength:byte ); procedure FindKey( CualIndex:PtrIndex; Clave:string ); procedure FindKeyRecNo( CualIndex:PtrIndex; const Clave:string; RecNo:LongInt ); procedure NextKey( CualIndex:PtrIndex; SearchNextNotFound:boolean ); procedure PrevKey( CualIndex:PtrIndex; SearchNextNotFound:boolean ); procedure InsKey( CualIndex:PtrIndex; Clave:string; RecNo:LongInt ); procedure CloseIndex( var CualIndex:PtrIndex ); procedure DelKey( CualIndex:PtrIndex; Clave:string; RecNo:LongInt ); procedure ReplaceKey( CualIndex:PtrIndex; const OldClave,NewClave:string; RecNo:LongInt ); procedure FindFirstKey(CualIndex:PtrIndex); procedure FindLastKey(CualIndex:PtrIndex); procedure ShortIntToKey( valor:ShortInt ; var Salida:Char ); procedure ByteToKey( valor:byte ; var Salida:Char ); procedure IntegerToKey( valor:integer ; var Salida:Char2 ); procedure WordToKey( valor:word ; var Salida:Char2 ); procedure LongIntToKey( valor:LongInt ; var Salida:Char4 ); *) {$A+,B-,D-,E-,F-,G+,I+,L-,N-,O-,P+,Q-,R-,S-,T-,V-,X+,Y-} unit SwagIdx; interface uses memory,dos; const Version = $0202; MaxPagesInMemory : 4..63 = 16; { Leves per index file } MaxKeyLength = 100; IndexRetryCount : word = 10; { Shared retry } { IndexRetryDelay : word = 1; } ErrIndex : word=0; RecIndex : LongInt=0; ErrIndexFound = $0000; ErrIndexNotFound = $0001; ErrIndexBig = $0002; ErrIndexEOF = $0010; ErrIndexBOF = $0011; ErrIndexOtherKey = $0004; ErrIndexNoKeys = $00FF; ErrIndexShared = $1000; ErrIndexRead = $1100; ErrIndexWrite = $1200; ErrIndexFileNoOpen = $1300; ErrIndexNoMemory = $FFFF; ErrIndexTooManyKeys = $FEFE; type char2 = array[1..2] of char; char4 = array[1..4] of char; Char255 = array[1..255] of char; const SizeOfResto = 1024-10; type RegPageIndex = record Nivel:byte; RecActual:byte; OfsResto:word; NPage:LongInt; { Rec number in FIndex } KeysInPage:word; resto:array[0..SizeOfResto-1] of byte; { RecNo + clave | RecNo + clave ... } { if RecNo = -1 => deleted } end; RegAuxBufIndexHead = record NumKeys, NumDelKeys, FirstPage, LastPage:LongInt; end; RegBufIndex=record NumKeys:LongInt; NumDelKeys:LongInt; FirstPage, LastPage:LongInt; KeyLength:word; CualVersion:word; FIndex:file; MaxKeysInPage:word; TotKeyLength:word; PageActual:byte; { from 1 to PagesInMemory } SeBloquea:boolean; PtrClave:pointer; SizeCualIndex:word; PagesInMemory : 4..63; ModPages:array[0..63] of boolean; Pages:array[1..63] of RegPageIndex; end; PtrIndex = ^RegBufIndex; procedure OpenIndex( var CualIndex:PtrIndex; const NomFile:PathStr ); procedure CreateIndex( var CualIndex:PtrIndex; const NomFile:PathStr; CualKeyLength:byte ); procedure FindKey( CualIndex:PtrIndex; Clave:string ); procedure FindKeyRecNo( CualIndex:PtrIndex; const Clave:string; RecNo:LongInt ); procedure NextKey( CualIndex:PtrIndex; SearchNextNotFound:boolean ); procedure PrevKey( CualIndex:PtrIndex; SearchNextNotFound:boolean ); procedure InsKey( CualIndex:PtrIndex; Clave:string; RecNo:LongInt ); procedure CloseIndex( var CualIndex:PtrIndex ); procedure DelKey( CualIndex:PtrIndex; Clave:string; RecNo:LongInt ); procedure ReplaceKey( CualIndex:PtrIndex; const OldClave,NewClave:string; RecNo:LongInt ); procedure FindFirstKey(CualIndex:PtrIndex); procedure FindLastKey(CualIndex:PtrIndex); procedure ShortIntToKey( valor:ShortInt ; var Salida:Char ); procedure ByteToKey( valor:byte ; var Salida:Char ); procedure IntegerToKey( valor:integer ; var Salida:Char2 ); procedure WordToKey( valor:word ; var Salida:Char2 ); procedure LongIntToKey( valor:LongInt ; var Salida:Char4 ); procedure UnLockFileIndex( CualHandle:word ); function LockFileIndex(CualHandle:word):boolean; procedure MyGetMem(var puntero:pointer; Tamano:word; ValorRelleno:byte); procedure MyFreeMem(var puntero:pointer; Tamano:word); function Equal(var Comparando1,Comparando2; Tamano:word):boolean; implementation const IsLockFileIndex : boolean = false; type MaskPtr = record xOffset,xSegment:word end; MaskLong = record LoWord, HiWord : word end; MaskWord = record LoByte, HiByte : byte end; procedure MyGetMem(var puntero:pointer; Tamano:word; ValorRelleno:byte); begin if Lo(Tamano) and $0F<>0 then Tamano := (Tamano and $FFF0) + $10; if LongInt(Tamano) > MaxAvail then RunError(203) { Heap Overflow Error } else begin if Tamano > 65528 then RunError(203); { Heap Overflow Error } Puntero := MemAllocSeg(Tamano); { GetMem(Puntero,Tamano); } fillchar( Puntero^, Tamano, ValorRelleno ) end; end; procedure MyFreeMem(var puntero:pointer; Tamano:word); begin if puntero <> nil then begin if MaskWord(Tamano).LoByte and $0F<>0 then Tamano := (Tamano and $FFF0) + $10; FreeMem(Puntero,Tamano); Puntero:=nil; end end; procedure ShortIntToKey( valor:ShortInt ; var Salida:Char ); assembler; asm mov al,valor cmp al,80h jb @@ShortIntToKeyPositivo not ax inc al jmp @@EndShortIntToKey @@ShortIntToKeyPositivo: or al,80h @@EndShortIntToKey: les di,Salida stosb end; procedure ByteToKey( valor:byte ; var Salida:Char ); assembler; asm mov al,valor les di,Salida stosb end; procedure IntegerToKey( valor:integer ; var Salida:Char2 ); assembler; asm mov ax,valor cmp ah,80h jb @@IntegerToKeyPositivo not ax inc ax jmp @@EndIntegerToKey @@IntegerToKeyPositivo: or ah,80h @@EndIntegerToKey: xchg al,ah les di,Salida stosw end; procedure WordToKey( valor:word ; var Salida:Char2 ); assembler; asm mov ax,valor xchg al,ah les di,Salida stosw end; procedure LongIntToKey( valor:LongInt ; var Salida:Char4 ); assembler; asm mov ax,MaskLong(valor).HiWord mov dx,MaskLong(valor).LoWord cmp ah,80h jb @@LongIntToKeyPositivo not ax not dx inc dx jnc @@EndLongIntToKey inc ax jmp @@EndLongIntToKey @@LongIntToKeyPositivo: or ah,80h @@EndLongIntToKey: xchg al,ah xchg dl,dh les di,Salida cld stosw mov es:[di],dx end; function Equal(var Comparando1,Comparando2; Tamano:word):boolean; assembler; asm mov al,false push ds cld mov cx,Tamano les di,Comparando1 lds si,Comparando2 repe cmpsb jne @@NoEqual mov al,True @@NoEqual: pop ds end; procedure WritePage(CualIndex:PtrIndex; CualPage:byte); begin with CualIndex^ do if Pages[CualPage].NPage>0 then begin seek(FIndex,Pages[CualPage].NPage); BlockWrite(FIndex,Pages[CualPage],1); ModPages[CualPage]:=false end end; procedure GetEmptyPage(CualIndex:PtrIndex; DondeLeer:byte); var AuxBucle:byte; begin with CualIndex^ do begin inc(LastPage); Pages[DondeLeer].NPage:=LastPage end end; function LockFileIndex(CualHandle:word):boolean; var Intentos:word; HayError:boolean; begin Intentos:=0; repeat HayError:=false; asm mov ax,5C00h mov bx,CualHandle xor cx,cx mov dx,cx mov si,cx mov di,16 int 21h jnc @@LockFileIndexOk mov HayError,True { If AL=1 => Invalid function code, =6 Invalid handle, 33 File-Locking violation } @@LockFileIndexOk: end; if HayError then if IndexRetryCount <> 0 then inc(intentos) until (not HayError) or (intentos >= IndexRetryCount); IsLockFileIndex := not HayError; LockFileIndex := ( not HayError ) end; procedure UnLockFileIndex( CualHandle:word ); {var } { Intentos:word; } { HayError:boolean; } begin { Intentos:=0; repeat HayError:=false; } asm mov ah,68h { Commit file } mov bx,CualHandle int 21h mov ax,5C01h mov bx,CualHandle xor cx,cx mov dx,cx mov si,cx mov di,16 int 21h (* jnc @@UnLockFileIndexOk mov HayError,True { if AL=1 => Invalid function code, =6 Invalid handle, 33 File-Locking violation } @@UnLockFileIndexOk: *) end; (* if HayError then if IndexRetryCount<>0 then inc(intentos) until (not HayError) or (intentos=IndexRetryCount); *) IsLockFileIndex:=false end; procedure LoadPage(var CualIndex:PtrIndex; PageToLeer:LongInt; DondeLeer:byte ); label LeePage; var leidos:word; LowLevel:byte; PageLowLevel:byte; procedure ChangePage; var AuxPage:RegPageIndex; AuxModPage:boolean; begin with CualIndex^ do begin AuxPage:=Pages[DondeLeer]; Pages[DondeLeer]:=Pages[leidos]; Pages[leidos]:=AuxPage; AuxModPage:=ModPages[DondeLeer]; ModPages[DondeLeer]:=ModPages[leidos]; ModPages[Leidos]:=AuxModPage end end; begin { LoadPage } with CualIndex^ do begin if DondeLeer>PagesInMemory then begin ErrIndex:=ErrIndexTooManyKeys; exit end; for leidos:=2 to PagesInMemory do if PageToLeer = Pages[leidos].NPage then begin if leidos<>DondeLeer then ChangePage; exit end; for leidos:=2 to PagesInMemory do { find empty page ... } if Pages[leidos].NPage = 0 then begin ChangePage; goto LeePage end; if DondeLeer < PagesInMemory then begin LowLevel := $FF; PageLowLevel := 0; for leidos := DondeLeer+1 to PagesInMemory do begin if Pages[leidos].Nivel < LowLevel then begin LowLevel := Pages[leidos].Nivel; PageLowLevel := leidos end; if (Pages[leidos].Nivel = 0) and (not ModPages[leidos]) then begin LowLevel := Pages[leidos].Nivel; PageLowLevel := leidos end; end; if LowLevel <> $FF then begin leidos := PageLowLevel; ChangePage end; end; LeePage: if ModPages[DondeLeer] then WritePage( CualIndex, DondeLeer ); if PageToLeer >= 0 then begin seek( FIndex, PageToLeer ); BlockRead( FIndex, Pages[DondeLeer], 1 ) end else fillchar( Pages[DondeLeer], SizeOf(RegPageIndex), 0 ) end end; { LoadPage } procedure ChkModFile(CualIndex:PtrIndex); var AuxHead : RegAuxBufIndexHead; { NumKeys , NumDelKeys , FirstPage, LastPage } OfsAuxHead:word; Handle:word; HayError:byte; begin with CualIndex^ do if LockFileIndex(FileRec(FIndex).Handle) then begin HayError := 0; Handle:=FileRec(FIndex).Handle; OfsAuxHead:=ofs(AuxHead); FileRec( FIndex ).RecSize := 1; seek( FIndex, 0 ); BlockRead( FIndex, AuxHead, SizeOf(RegAuxBufIndexHead) ); FileRec( FIndex ).RecSize := SizeOf( RegPageIndex ); if (AuxHead.NumKeys<>NumKeys) or (AuxHead.NumDelKeys<>NumDelKeys) then begin move( AuxHead, CualIndex^, SizeOf(RegAuxBufIndexHead) ); fillchar(CualIndex^.Pages,SizeOf(RegPageIndex)*PagesInMemory,0); LoadPage( CualIndex, FirstPage, 1 ); PageActual:=1; IsLockFileIndex := true end end else ErrIndex:=Lo(ErrIndex) + ErrIndexShared end; procedure IniciaIndex(CualIndex:PtrIndex; var SeDesbloquea:boolean); begin ErrIndex := Lo(ErrIndex); if CualIndex^.SeBloquea then begin SeDesbloquea:=not IsLockFileIndex; if SeDesbloquea then ChkModFile(CualIndex) end else SeDesbloquea:=false end; procedure FinalizaIndex( CualIndex:PtrIndex; SeDesbloquea:boolean ); var Handle:word; AuxBucle:byte; HayError:byte; begin if CualIndex^.SeBloquea then with CualIndex^ do begin for AuxBucle:=1 to PagesInMemory do if ModPages[AuxBucle] then WritePage(CualIndex,AuxBucle); if ModPages[0] then begin HayError := 0; Handle:=FileRec(FIndex).Handle; FileRec( FIndex ).RecSize := 1; seek( FIndex, 0 ); BlockWrite( FIndex, CualIndex^, SizeOf(RegAuxBufIndexHead) ); FileRec( FIndex ).RecSize := SizeOf( RegPageIndex ); ErrIndex := Lo(ErrIndex); ModPages[0]:=false end; if SeDesbloquea then UnLockFileIndex(FileRec(FIndex).Handle) end end; procedure CreateIndex(var CualIndex:PtrIndex; const NomFile:PathStr; CualKeyLength:byte ); var AuxWord:word; begin AuxWord := SizeOf(RegBufIndex)-(63-MaxPagesInMemory)*SizeOf(RegPageIndex); MyGetMem( pointer(CualIndex), AuxWord, 0 ); if CualIndex = nil then ErrIndex:=ErrIndexNoMemory else begin with CualIndex^ do begin SizeCualIndex := AuxWord; PagesInMemory := MaxPagesInMemory; if (FileMode and 112) = 0 then SeBloquea := false else SeBloquea := ( FileMode and 112 ) <> 16 ; { if shared } CualVersion := Version; KeyLength := CualKeyLength; FirstPage := 1; LastPage := 1; System.assign(FIndex,NomFile); rewrite( FIndex, 1 ); {$I-} if IOResult<>0 then begin ErrIndex := ErrIndexFileNoOpen; MyFreeMem(pointer(CualIndex),SizeOf(RegBufIndex)); exit end; {$I+} if SeBloquea then LockFileIndex( FileRec(FIndex).Handle ); Pages[2].NPage:=1; BlockWrite( FIndex, CualIndex^.Pages[1], SizeOf(RegPageIndex)*2 ); seek( FIndex, 0 ); BlockWrite( FIndex, CualIndex^, 20 ); Pages[1].NPage := 1; Pages[2].NPage := 0; TotKeyLength := KeyLength+4; MaxKeysInPage := SizeOfResto div TotKeyLength; if SeBloquea then UnLockFileIndex( FileRec(FIndex).Handle ); close( FIndex ); reset( FIndex, SizeOf(RegPageIndex ) ) end; end end; procedure OpenIndex(var CualIndex:PtrIndex; const NomFile:PathStr ); var SaveFileMode:byte; AuxWord:word; begin AuxWord := SizeOf(RegBufIndex)-(63-MaxPagesInMemory)*SizeOf(RegPageIndex); MyGetMem( pointer(CualIndex), AuxWord , 0 ); if CualIndex=nil then ErrIndex:=ErrIndexNoMemory else with CualIndex^ do begin SizeCualIndex := AuxWord; PagesInMemory := MaxPagesInMemory; if (FileMode and 112) = 0 then SeBloquea := false else SeBloquea := ( FileMode and 112 <> 16 ); { if shared } System.assign(FIndex,NomFile); {$I-} reset( FIndex, SizeOf(RegPageIndex) ); if IOResult <> 0 then begin ErrIndex := ErrIndexFileNoOpen; MyFreeMem( pointer(CualIndex), CualIndex^.SizeCualIndex ); exit end; {$I+} if SeBloquea then LockFileIndex( FileRec(FIndex).Handle ); FileRec( FIndex ).RecSize := 1; BlockRead( FIndex, CualIndex^, 20 ); { file tail } FileRec( FIndex ).RecSize := SizeOf( RegPageIndex ); TotKeyLength := KeyLength + 4; MaxKeysInPage := SizeOfResto div TotKeyLength; seek( FIndex, FirstPage ); BlockRead( FIndex, Pages[1], 1 ); LastPage := FileSize(FIndex) - 1; if SeBloquea then UnLockFileIndex( FileRec(FIndex).Handle ) end end; procedure CloseIndex(var CualIndex:PtrIndex); var AuxBucle:byte; begin if FileRec(CualIndex^.FIndex).Mode = fmInOut then begin with CualIndex^ do begin for AuxBucle:=1 to PagesInMemory do if ModPages[AuxBucle] then WritePage(CualIndex,AuxBucle); if CualIndex^.ModPages[0] then begin FileRec( FIndex ).RecSize := 1; seek( FIndex, 0 ); BlockWrite( Findex, NumKeys, SizeOf(RegAuxBufIndexHead) ); end; close( FIndex ); end; MyFreeMem( pointer(CualIndex), CualIndex^.SizeCualIndex ); CualIndex:=nil end end; procedure InsKey(CualIndex:PtrIndex; Clave:string; RecNo:LongInt); var AuxWord,AuxBucle:word; AuxRecNo,AuxRecPage:LongInt; SeDesbloquea:boolean; procedure MeteKey(CualPagina:word; Donde:word; Replace:boolean; var Clave); var AuxWord:word; procedure ModifyPagesBefore; var BuscaPage,AuxLong:LongInt; SaveDonde:word; begin SaveDonde:=Donde; with CualIndex^ do begin while (donde = Pages[CualPagina].KeysInPage) and (CualPagina > 1) do { modificar p gina(s) anterior(es) } begin (***************************) BuscaPage := Pages[CualPagina].NPage; dec( CualPagina ); donde := 0; AuxWord := 0; repeat move( Pages[CualPagina].resto[AuxWord], AuxLong, 4 ); inc( donde ); AuxWord := AuxWord + TotKeyLength until (Donde > Pages[CualPagina].KeysInPage) or (AuxLong = BuscaPage); if AuxLong = BuscaPage then begin AuxWord := AuxWord-TotKeyLength; move( clave, Pages[CualPagina].resto[AuxWord], TotKeyLength ); move( AuxLong, Pages[CualPagina].resto[AuxWord], 4 ); ModPages[CualPagina] := true end end end; Donde:=SaveDonde end; procedure PartePage; var AuxBucle:word; AuxClave:Char255; begin with CualIndex^ do begin LoadPage( CualIndex, -1, PagesInMemory ); (* // *) (* // if ModPages[PagesInMemory] then WritePage( CualIndex, PagesInMemory ); fillchar( Pages[PagesInMemory], SizeOf(RegPageIndex), 0 ); // *) AuxWord := TotKeyLength * ( MaxKeysInPage div 2 ); move( Pages[CualPagina], Pages[PagesInMemory], 10 + AuxWord ); ModPages[PagesInMemory] := true; ModPages[CualPagina] := true; GetEmptyPage( CualIndex, PagesInMemory ); Pages[PagesInMemory].KeysInPage := MaxKeysInPage div 2; Pages[CualPagina].KeysInPage := MaxKeysInPage - Pages[PagesInMemory].KeysInPage; AuxWord := TotKeyLength * Pages[CualPagina].KeysInPage; move( Pages[CualPagina].resto[ (MaxKeysInPage div 2)*TotKeyLength ], Pages[CualPagina].resto[0], AuxWord ); fillchar( Pages[CualPagina].resto[AuxWord], SizeOfResto - AuxWord, 0 ); if donde > Pages[PagesInMemory].KeysInPage then begin PageActual := CualPagina; donde := donde - Pages[PagesInMemory].KeysInPage; end else PageActual := PagesInMemory; Pages[PageActual].OfsResto := (donde-1) * TotKeyLength; Pages[PageActual].RecActual := donde; if not Replace then begin inc( Pages[PageActual].KeysInPage ); inc( NumKeys ); ModPages[0] := true; move( Pages[PageActual].resto[Pages[PageActual].OfsResto], Pages[PageActual].resto[Pages[PageActual].OfsResto + TotKeyLength], TotKeyLength*(Pages[PageActual].KeysInPage - donde)); end; move( clave, Pages[PageActual].resto[Pages[PageActual].OfsResto], TotKeyLength ); if CualPagina = 1 then { make another first key page } begin if Pages[1].Nivel+1 = PagesInMemory then { too many keys } begin ErrIndex := ErrIndexTooManyKeys; exit end; LoadPage( CualIndex, -1, PagesInMemory-1 ); (* // *) (* // WritePage( CualIndex, PagesInMemory ); if ModPages[ PagesInMemory-1 ] then WritePage( CualIndex, PagesInMemory-1 ); // *) for AuxBucle := PagesInMemory-1 downto 2 do begin Pages[AuxBucle] := Pages[AuxBucle-1]; ModPages[AuxBucle] := ModPages[AuxBucle-1]; end; fillchar( Pages[1], SizeOf(RegPageIndex), 0 ); ModPages[1] := true; GetEmptyPage( CualIndex, 1 ); Pages[1].KeysInPage := 2; Pages[1].Nivel := Pages[2].Nivel+1; move( Pages[PagesInMemory].NPage, Pages[1].resto[0], 4 ); move( Pages[PagesInMemory].resto[(Pages[PagesInMemory].KeysInPage-1)*TotKeyLength+ 4], Pages[1].resto[4], KeyLength ); move( Pages[2].NPage, Pages[1].resto[TotKeyLength], 4 ); move( Pages[2].resto[(Pages[2].KeysInPage-1)*TotKeyLength+4], Pages[1].resto[TotKeyLength+4], KeyLength ); FirstPage := Pages[1].NPage; ModPages[0] := true end else begin move( Pages[PagesInMemory].resto[(Pages[PagesInMemory].KeysInPage-1)*TotKeyLength] , AuxClave[1], TotKeyLength ); move( Pages[PagesInMemory].NPage, AuxClave[1], 4 ); if PageActual = PagesInMemory then begin LoadPage( CualIndex, -1, CualPagina ); (* // *) (* // if ModPages[CualPagina] then WritePage( CualIndex, CualPagina ); // *) Pages[CualPagina] := Pages[PagesInMemory]; ModPages[CualPagina] := true; end else begin LoadPage( CualIndex, -1, PagesInMemory ) (* // *) (* // WritePage( CualIndex, PagesInMemory ); // *) end; fillchar( Pages[PagesInMemory], SizeOf(RegPageIndex), 0 ); ModPages[PagesInMemory] := false; PageActual := CualPagina; if ErrIndex = ErrIndexNotFound then MeteKey( CualPagina-1, Pages[CualPagina-1].RecActual-1, false, AuxClave[1] ) else MeteKey( CualPagina-1, Pages[CualPagina-1].RecActual, false, AuxClave[1] ); PageActual := CualPagina; if ErrIndex=ErrIndexTooManyKeys then exit; ModifyPagesBefore end; end end; begin { MeteKey } with CualIndex^ do begin if (donde > MaxKeysInPage) or ((not Replace) and (Pages[CualPagina].KeysInPage = MaxKeysInPage)) then if CualPagina < PagesInMemory then PartePage else ErrIndex := ErrIndexTooManyKeys else begin AuxWord := (donde-1)*TotKeyLength; if not Replace then move( Pages[CualPagina].Resto[AuxWord], Pages[CualPagina].Resto[AuxWord+TotKeyLength], (Pages[CualPagina].KeysInPage+1-donde)*TotKeyLength); move( clave, Pages[CualPagina].resto[AuxWord], TotKeyLength ); if not Replace then begin inc( Pages[CualPagina].KeysInPage ); inc( NumKeys ); ModPages[0] := true; end; Pages[CualPagina].RecActual := donde; Pages[CualPagina].OfsResto := (donde-1)*TotKeyLength; ModPages[CualPagina] := true; ModifyPagesBefore end end end; { MeteKey } begin { InsKey } IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex) <> 0 then exit; while length(Clave) < CualIndex^.KeyLength do Clave:=Clave+#0; FindKey( CualIndex, Clave ); move( Clave[1],Clave[5], CualIndex^.KeyLength ); move( RecNo, Clave[1], 4 ); with CualIndex^ do begin if ErrIndex = ErrIndexNotFound then { clave mayor que todas las claves } begin PageActual := 1; while Pages[PageActual].Nivel <> 0 do begin Pages[PageActual].RecActual := Pages[PageActual].KeysInPage+1; Pages[PageActual].OfsResto := Pages[PageActual].KeysInPage*TotKeyLength; move( Pages[PageActual].resto[TotKeyLength*(Pages[PageActual].KeysInPage-1)], AuxRecPage, 4 ); inc( PageActual ); LoadPage( CualIndex, AuxRecPage, PageActual ); if ErrIndex = ErrIndexTooManyKeys then exit; end; Pages[PageActual].RecActual := Pages[PageActual].KeysInPage+1; Pages[PageActual].OfsResto := Pages[PageActual].KeysInPage*TotKeyLength; MeteKey( PageActual,Pages[PageActual].KeysInPage+1, false, Clave[1] ) end else { if ErrIndex=ErrIndexNotFound then key more big that last key in file } begin { find key greather equal } MeteKey( PageActual,Pages[PageActual].RecActual, false, Clave[1] ) end end; FinalizaIndex( CualIndex, SeDesbloquea ); end; { InsKey } procedure NextKey(CualIndex:PtrIndex; SearchNextNotFound:boolean); label Label01; var AuxRecPage:LongInt; AuxWord,WordBusca:word; AuxClave:Char255; begin with CualIndex^ do begin move( Pages[PageActual].resto[Pages[PageActual].OfsResto], AuxClave, TotKeyLength ); if Pages[PageActual].RecActual < Pages[PageActual].KeysInPage then begin inc( Pages[PageActual].RecActual ); inc( Pages[PageActual].OfsResto,TotKeyLength ) end else begin AuxWord := PageActual; { $R-} while (Pages[PageActual].RecActual = Pages[PageActual].KeysInPage) and (PageActual > 1) do dec( PageActual ); { $R+} if Pages[PageActual].RecActual = Pages[PageActual].KeysInPage then begin PageActual := AuxWord; ErrIndex := ErrIndexEOF; exit end; inc( Pages[PageActual].OfsResto, TotKeyLength ); inc( Pages[PageActual].RecActual ); while Pages[PageActual].Nivel <> 0 do begin move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], AuxRecPage, 4 ); inc( PageActual ); LoadPage( CualIndex, AuxRecPage, PageActual ); if ErrIndex = ErrIndexTooManyKeys then exit; Pages[PageActual].OfsResto := 0; Pages[PageActual].RecActual := 1; end end; move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], RecIndex, 4 ); PtrClave := addr(Pages[PageActual].resto[Pages[PageActual].OfsResto+4]); if Equal( AuxClave[5], Pages[PageActual].Resto[Pages[PageActual].OfsResto+4], KeyLength) then ErrIndex := ErrIndexFound else if SearchNextNotFound then ErrIndex := ErrIndexFound else ErrIndex := ErrIndexOtherKey end end; procedure PrevKey(CualIndex:PtrIndex; SearchNextNotFound:boolean); label Label01; var AuxRecPage:LongInt; AuxWord,WordBusca:word; AuxClave:Char255; begin with CualIndex^ do begin move( Pages[PageActual].resto[Pages[PageActual].OfsResto], AuxClave, TotKeyLength ); if Pages[PageActual].RecActual > 1 then begin dec( Pages[PageActual].RecActual ); dec( Pages[PageActual].OfsResto, TotKeyLength ) end else begin AuxWord := PageActual; { $R-} while (Pages[PageActual].RecActual = 1) and (PageActual > 1) do dec( PageActual ); { $R+} if Pages[PageActual].RecActual = 1 then begin PageActual := AuxWord; ErrIndex := ErrIndexBOF; exit end; dec( Pages[PageActual].OfsResto, TotKeyLength ); dec( Pages[PageActual].RecActual ); while Pages[PageActual].Nivel <> 0 do begin move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], AuxRecPage, 4 ); inc( PageActual ); LoadPage( CualIndex, AuxRecPage, PageActual ); if ErrIndex = ErrIndexTooManyKeys then exit; Pages[PageActual].RecActual := Pages[PageActual].KeysInPage; Pages[PageActual].OfsResto := (Pages[PageActual].KeysInPage-1) * TotKeyLength end end; move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], RecIndex, 4 ); PtrClave := addr( Pages[PageActual].resto[Pages[PageActual].OfsResto+4] ); if Equal( AuxClave, Pages[PageActual].Resto[Pages[PageActual].OfsResto+4], KeyLength ) then ErrIndex := ErrIndexFound else if SearchNextNotFound then ErrIndex := ErrIndexFound else ErrIndex := ErrIndexOtherKey end end; procedure FindKey(CualIndex:PtrIndex; Clave:string); label H1; var AuxRecPage:LongInt; AuxCont:word; SeDesbloquea:boolean; function BuscaMayorIgual(var Clave, Resto; KeyLength, KeysInPage:word):byte; assembler; asm mov cx,KeyLength mov dx,cx add dx,4 { dx = TotKeyLength } mov ax,1 mov bx,KeysInPage cld push ds les di,Clave lds si,Resto add si,4 @@Bucle: cmp ax,bx ja @@EndXX push si push di push cx repe cmpsb pop cx pop di pop si jae @@EndXX add si,dx inc ax jmp @@Bucle @@EndXX: pop ds end; begin IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex)<>0 then exit; while length(Clave) < CualIndex^.KeyLength do Clave := Clave+#0; with CualIndex^ do begin PageActual := 1; H1: Pages[PageActual].RecActual := BuscaMayorIgual(Clave[1],Pages[PageActual].Resto,KeyLength,Pages[PageActual] .KeysInPage); Pages[PageActual].OfsResto := (Pages[PageActual].RecActual-1) * TotKeyLength; if Pages[PageActual].RecActual > Pages[PageActual].KeysInPage then ErrIndex := ErrIndexNotFound else begin if Pages[PageActual].Nivel <> 0 then begin move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], AuxRecPage, 4 ); inc( PageActual ); LoadPage( CualIndex, AuxRecPage, PageActual ); if ErrIndex = ErrIndexTooManyKeys then exit; goto H1 end else begin if Equal(clave[1],Pages[PageActual].resto[Pages[PageActual].OfsResto+4],KeyLength) and (Pages[PageActual].RecActual <= Pages[PageActual].KeysInPage) then ErrIndex := ErrIndexFound else ErrIndex := ErrIndexBig; move( Pages[PageActual].resto[Pages[PageActual].OfsResto], RecIndex, 4 ); PtrClave := addr( Pages[PageActual].resto[Pages[PageActual].OfsResto+4] ) end end; if SeBloquea and SeDesbloquea then UnLockFileIndex( FileRec(FIndex).Handle ) end; { FinalizaIndex( CualIndex, SeDesbloquea ); } end; procedure FindKeyRecNo(CualIndex:PtrIndex; const Clave:string; RecNo:LongInt); var SeDesbloquea:boolean; begin IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex) <> 0 then exit; FindKey( CualIndex, Clave ); if ErrIndex = ErrIndexFound then with CualIndex^ do begin repeat move( Pages[PageActual].Resto[Pages[PageActual].OfsResto], RecIndex, 4 ); if RecIndex <> RecNo then NextKey( CualIndex, false ); until (ErrIndex = ErrIndexOtherKey) or (ErrIndex = ErrIndexEOF) or (RecIndex = RecNo); if (RecIndex <> RecNo) or (ErrIndex = ErrIndexEOF) or (ErrIndex = ErrIndexOtherKey) then ErrIndex := ErrIndexNotFound end else ErrIndex := ErrIndexNotFound; FinalizaIndex( CualIndex, SeDesbloquea ); end; procedure DelKey(CualIndex:PtrIndex; Clave:string; RecNo:LongInt); label Label01; var AuxLong:LongInt; BuclePage:word; SavePage:word; SeDesbloquea:boolean; procedure MiraFirstPage; var AuxBuclePage:word; begin with CualIndex^ do if (Pages[1].KeysInPage = 1) and (Pages[1].Nivel > 0) then begin Pages[1].KeysInPage := 0; fillchar( Pages[1].Resto, SizeOfResto, 0 ); Pages[1].RecActual := 0; Pages[1].OfsResto := 0; Pages[1].Nivel := 0; WritePage( CualIndex, 1 ); for AuxBuclePage := 1 to PagesInMemory-1 do begin Pages[AuxBuclePage] := Pages[AuxBuclePage+1]; ModPages[AuxBuclePage] := ModPages[AuxBuclePage+1]; end; ModPages[PagesInMemory] := false; fillchar( Pages[PagesInMemory], SizeOf(RegPageIndex), 0 ); FirstPage := Pages[1].NPage; dec( SavePage ) end end; procedure JuntaPages; var sw:boolean; AuxPage:byte; begin with CualIndex^ do begin sw:=true; if Pages[BuclePage-1].RecActual > 0 then begin if ModPages[PagesInMemory] then WritePage( CualIndex, PagesInMemory ); move( Pages[BuclePage-1].resto[Pages[BuclePage-1].OfsResto-TotKeyLength],AuxLong,4 ); LoadPage( CualIndex, AuxLong, PagesInMemory ); if Pages[PagesInMemory].KeysInPage+Pages[BuclePage].KeysInPage 2) then begin AuxPage := BuclePage-2; while AuxPage >= 1 do begin with Pages[BuclePage-1] do move( Resto[OfsResto+4], Pages[AuxPage].Resto[Pages[AuxPage].OfsResto+4], KeyLength ); dec( AuxPage ) end; end end end; if not sw then begin fillchar( Pages[PagesInMemory], SizeOf(RegPageIndex), 0); Pages[PagesInMemory]. NPage := AuxLong; ModPages[PagesInMemory]:=true; ModPages[BuclePage]:=true; dec(Pages[BuclePage-1].KeysInPage); ModPages[BuclePage-1]:=true end; end end; begin { DelKey } IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex)<>0 then exit; FindKeyRecNo(CualIndex,Clave,RecNo); if ErrIndex=ErrIndexFound then with CualIndex^ do begin SavePage:=PageActual; Label01: if Pages[PageActual].KeysInPage=Pages[PageActual].RecActual then begin dec(Pages[PageActual].KeysInPage); fillchar(Pages[PageActual].Resto[Pages[PageActual].OfsResto],TotKeyLength,0) ; dec(Pages[PageActual].OfsResto,TotKeyLength); dec(Pages[PageActual].RecActual); ModPages[PageActual]:=true; { change previous pages } if (Pages[PageActual].KeysInPage=0) and (PageActual>1) then begin dec(PageActual); goto Label01 end; if Pages[PageActual].KeysInPage>0 then begin move(Pages[PageActual].resto[Pages[PageActual].OfsResto],Clave[1],TotKeyLength); while (PageActual>1) and (Pages[PageActual].KeysInPage=Pages[PageActual].RecActual) do begin move(Clave[5],Pages[PageActual-1].resto[Pages[PageActual-1].OfsResto+4],KeyLength); ModPages[PageActual-1]:=true; dec(PageActual) end end end else begin with Pages[PageActual] do move( resto[ OfsResto + TotKeyLength ], resto[ OfsResto ], ( MaxKeysInPage - RecActual ) * TotKeyLength ); dec(Pages[PageActual].KeysInPage); ModPages[PageActual]:=true end; inc(NumDelKeys); dec(NumKeys); ModPages[0]:=true; MiraFirstPage; PageActual:=SavePage; { Comprobaci˘n de que las p ginas se puedan juntar con la anterior o siguiente siempre y cuando KeysInPage <= MaxKeysInPage div 3 } for BuclePage := SavePage downto 2 do if Pages[BuclePage].KeysInPage > 0 then begin if (Pages[BuclePage].KeysInPage<=(MaxKeysInPage div 3)) then JuntaPages end else begin { P gina vacˇa } end; MiraFirstPage end else ErrIndex:=ErrIndexNotFound; FinalizaIndex( CualIndex, SeDesbloquea ); end; { DelKey } procedure ReplaceKey(CualIndex:PtrIndex; const OldClave,NewClave:string; RecNo:LongInt); var SeDesbloquea:boolean; begin IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex)<>0 then exit; DelKey(CualIndex,OldClave,RecNo); if ErrIndex=ErrIndexFound then InsKey(CualIndex,NewClave,RecNo) else ErrIndex:=ErrIndexNotFound; FinalizaIndex( CualIndex, SeDesbloquea ) end; procedure FindFirstKey(CualIndex:PtrIndex); var AuxClave:string; begin fillchar(AuxClave,SizeOf(AuxClave),0); AuxClave[0]:=char(CualIndex^.KeyLength); FindKey(CualIndex,AuxClave); if (Lo(ErrIndex)=ErrIndexBig) or (ErrIndex=0) then ErrIndex := ErrIndexFound else ErrIndex := ErrIndexNoKeys end; procedure FindLastKey(CualIndex:PtrIndex); var AuxLong:LongInt; SeDesbloquea:boolean; begin IniciaIndex( CualIndex, SeDesbloquea ); if Hi(ErrIndex)<>0 then exit; with CualIndex^ do begin if Pages[1].KeysInPage > 0 then begin PageActual := 1; while Pages[PageActual].Nivel <> 0 do begin move( Pages[PageActual].resto[(Pages[PageActual].KeysInPage-1)*TotKeyLength],AuxLong,4); inc( PageActual ); LoadPage( CualIndex, AuxLong, PageActual ); end; Pages[PageActual].RecActual := Pages[PageActual].KeysInPage; Pages[PageActual].OfsResto := (Pages[PageActual].RecActual-1)*TotKeyLength; move(Pages[PageActual].resto[Pages[PageActual].OfsResto],RecIndex,4); PtrClave:=addr(Pages[PageActual].resto[Pages[PageActual].OfsResto+4]); ErrIndex := ErrIndexFound end else ErrIndex := ErrIndexNoKeys end; FinalizaIndex( CualIndex, SeDesbloquea ) end; end. { ---------------------------- DEMO -------------------- } { CUT } { SwagIndex Demo. First, excuse me, I don't speak english. There are two index file for one file data -The first index is the field Numero (one data rec => one key) -The second index is the field Nombre1 and Nombre2 if not empty (one data rec => one or two keys). } uses crt,dos,SwagIdx; type RegData = record Numero:integer; Nombre1,Nombre2:string[25]; telefono:string[12] end; var FData:file of RegData; RData:RegData; IndexDataNumero,IndexDataNombre,AuxIndex:PtrIndex; ch,CualClave,CualOrden:char; CadBusca:string; swWriteCab:boolean; procedure WriteError; begin case ErrIndex and $00FF of ErrIndexNotFound : writeln( 'Key not found' ); ErrIndexBig : writeln( 'Key not found, big exist'); ErrIndexEOF : writeln( 'End of index file, no more keys' ); ErrIndexBOF : writeln( 'Begin of index file' ); ErrIndexOtherKey : writeln( 'It''s another key' ); ErrIndexNoKeys : writeln( 'No keys in index file' ); end; case (ErrIndex and $FF00) of ErrIndexShared : writeln( 'Sharing mode error'); ErrIndexRead : writeln( 'Read error'); ErrIndexWrite : writeln( 'Write error'); ErrIndexFileNoOpen : writeln( 'Index file not open' ); end; case ErrIndex of ErrIndexNoMemory : writeln( 'Not enaught memory' ); ErrIndexTooManyKeys : writeln( 'Too many keys in file' ) end end; procedure InsRec( Numero:integer; const Nombre1,Nombre2,Telefono:string); var AuxClave : char2; begin IntegerToKey( Numero, AuxClave ); InsKey( IndexDataNombre, Nombre1 , FileSize(FData) ); if Nombre2 <> '' then { if there is a second name ... } InsKey( IndexDataNombre, Nombre2 , FileSize(FData) ); InsKey( IndexDataNumero, AuxClave, FileSize(FData) ); RData.Numero := Numero; RData.Nombre1 := Nombre1; RData.Nombre2 := Nombre2; RData.Telefono := Telefono; seek(FData,FileSize(FData)); write(FData,RData); end; procedure ShowRec; procedure WriteCab; begin swWriteCab:=true; writeln(' Rec Num. Name 1 Name 2 Telephon'); writeln('----- ----- -------------------------- -------------------------- ------------') end; begin { ShowRec } if not swWriteCab then WriteCab; seek(FData,RecIndex); read(FData,RData); writeln( RecIndex:5, RData.Numero:6,' ',RData.Nombre1,'':27-length(RData.Nombre1),RData.Nombre2,'':27-length(RData .Nombre2), RData.Telefono); end; { ShowRec } procedure MakeFile; begin if FileRec(FData).Mode = fmInOut then begin writeln( 'File already open'); exit end; rewrite(FData); CreateIndex( IndexDataNombre, 'SWAGNDC.NOM',30 ); CreateIndex( IndexDataNumero, 'SWAGNDC.NUM', 2 ); InsRec( 1245,'PEPITO PEREZ','JOSEFA PEREZ','12354' ); InsRec( 1313,'PEPITO LOPEZ','','91-13123123' ); InsRec( 245,'OTRO PEPITO','','959-12354' ); InsRec( 145,'FULANITO DE TAL','MENGANITA','9912354' ); InsRec( -12,'TAL Y TAL','GIL','1544254' ); InsRec( 1435,'TAL Y PASCUAL','MARAGALL','07505505505' ); { y ya me he cansado } end; procedure LeeFile; begin if FileRec(FData).Mode = fmInOut then begin writeln( 'File already open'); exit end; {$I-} reset(FData); if IOResult <> 0 then {$I+} begin writeln( 'Open data file error'); exit end; OpenIndex( IndexDataNombre, 'SWAGNDC.NOM' ); OpenIndex( IndexDataNumero, 'SWAGNDC.NUM' ) end; procedure Informa; begin writeln( FileSize(FData):5,' recs. in file data' ); writeln( IndexDataNumero^.NumKeys:5,' keys in number''s index file and ', IndexDataNumero^.NumDelKeys:5,' deleted keys'); writeln( IndexDataNombre^.NumKeys:5,' keys in name''s index file and ', IndexDataNombre^.NumDelKeys:5,' deleted keys'); end; procedure MuestraRecs; begin if CualOrden = 'A' then begin FindFirstKey( AuxIndex ); if ErrIndex=ErrIndexNoKeys then WriteError else while ErrIndex <> ErrIndexEOF do begin ShowRec; NextKey( AuxIndex, true ) end end else begin FindLastKey( AuxIndex ); if ErrIndex=ErrIndexNoKeys then WriteError else while ErrIndex <> ErrIndexBOF do begin ShowRec; PrevKey( AuxIndex, true ) end; end end; procedure BuscaClave; var AuxInt:integer; AuxChar2:char2; begin write('Key to find: '); if CualClave = '2' then readln(CadBusca) else begin readln(AuxInt); IntegerToKey( AuxInt, AuxChar2 ); CadBusca := AuxChar2 end; FindKey( AuxIndex, CadBusca ); if ErrIndex = ErrIndexFound then ShowRec else WriteError end; procedure BuscaGenerica; var AuxChar2:char2; AuxInt:integer; begin write('Key to find: '); if CualClave = '2' then readln(CadBusca) else begin readln(AuxInt); IntegerToKey( AuxInt, AuxChar2 ); CadBusca := AuxChar2 end; FindKey( IndexDataNombre, CadBusca ); while (ErrIndex = ErrIndexFound) or (ErrIndex=ErrIndexBig) do begin if Equal( IndexDataNombre^.PtrClave^, CadBusca[1], length(CadBusca) ) then begin ShowRec; NextKey( IndexDataNombre, true ) end else break end end; procedure BorraClave; begin if (ErrIndex = 0) or (ErrIndex = ErrIndexBig) or (ErrIndex = ErrIndexOtherKey) then begin CadBusca[0] := char(AuxIndex^.KeyLength); move( AuxIndex^.PtrClave^, CadBusca[1], byte(CadBusca[0]) ); DelKey( AuxIndex, CadBusca, RecIndex ); if ErrIndex <> 0 then WriteError end end; procedure MeteRec; var AuxInt:integer; AuxNombre1,AuxNombre2:string[25]; AuxTelefono:string[12]; begin write('Number: '); readln(AuxInt); write('Name 1 : '); readln(AuxNombre1); write('Name 2 : '); readln(AuxNombre2); write('Telephon: '); readln(AuxTelefono); InsRec( AuxInt, AuxNombre1, AuxNombre2, AuxTelefono ) end; begin assign( FData,'SWAGNDC.D'); repeat swWriteCab:=false; ClrScr; writeln( '0.- Create file' ); writeln( '1.- Read file' ); writeln( 'A.- About files' ); writeln( 'B.- Show recs' ); writeln( 'C.- Find Key' ); writeln( 'D.- Find generic key (only names)' ); writeln( 'E.- Delete last found key' ); writeln( 'F.- Find next' ); writeln( 'G.- Find prev' ); writeln( 'H.- Find smallest key' ); writeln( 'I.- Find biggest key' ); writeln( 'J.- Add rec' ); writeln( 'Z.- End' ); writeln; write( 'Option 0,A-I,Z: ' ); ch := UpCase(ReadKey); writeln( ch ); writeln; if ch = 'Z' then break; if (ch in ['A'..'J']) and (FileRec(FData).Mode <> fmInOut) then begin writeln( 'Must read o create index file first' ); ch := #0 end; if (ch >='B') and (ch <= 'I') then begin if (ch <> 'D') and (ch <> 'E') then begin write( 'Key (1=Numbers, 2=Names) : '); CualClave := ReadKey; writeln( CualClave ) end else if ch = 'D' then CualClave := '2'; if (CualClave <> '1') and (CualClave <> '2') then ch := #0 else begin if CualClave = '1' then AuxIndex := IndexDataNumero else AuxIndex := IndexDataNombre; if ch = 'B' then begin write('Orden Ascendente o Descendente (A/D): '); CualOrden := UpCase(ReadKey); writeln(CualOrden) end end end; case ch of '0':MakeFile; '1':LeeFile; 'A':Informa; 'B':MuestraRecs; 'C':BuscaClave; 'D':BuscaGenerica; 'E':BorraClave; 'F':begin NextKey( AuxIndex, true ); if ErrIndex = 0 then ShowRec else WriteError end; 'G':begin PrevKey( AuxIndex, true ); if ErrIndex = 0 then ShowRec else WriteError end; 'H':begin FindFirstKey( AuxIndex ); if ErrIndex = ErrIndexFound then ShowRec else WriteError end; 'I':begin FindLastKey( AuxIndex ); if ErrIndex = ErrIndexFound then ShowRec else WriteError end; 'J':MeteRec end; writeln; write('Press Enter ...'); readln; until false; if FileRec(FData).Mode = fmInOut then begin close(FData); CloseIndex( IndexDataNumero ); CloseIndex( IndexDataNombre ) end end.