unit GSOB_Ntx;
{-----------------------------------------------------------------------------
Clipper Index Handler
GS_DBNtx Copyright (c) Richard F. Griffin
08 February 1993
102 Molded Stone Pl
Warner Robins, GA 31088
-------------------------------------------------------------
This unit handles the objects for all Clipper index (.NTX)
operations. This unit may be implemented by adding a conditional
define to the complier options. In the IDE this is done in the
Options|Compile menu, selecting Conditional Define, and adding
CLIPPER as a Define item. You must then recompile using the
Compile|Build option to force recompilation of units that will use
the index.
In the command line compiler use the /D option, for example:
TPC MyProg /DCLIPPER
That's the only change necessary to replace .NDX indexes with
Clipper .NTX indexes.
Changes:
17 Apr 93 - KeySort routine corrected to handle key string lengths
properly in GSOB_NDX. This correction is included for
consistency in the calling structure. This unit will
continue to use Key_Lgth as the string length.
------------------------------------------------------------------------------}
{$O+}
interface
uses
GSOB_Var,
GSOB_Dte,
GSOB_Str, {String handler routines}
GSOB_Inx,
GSOB_Dsk, {File handler routines}
GSOB_DBF,
{$IFDEF WINDOWS}
Objects;
{$ELSE}
GSOB_Obj;
{$ENDIF}
const
NdxBlokSize = 1024;
type
GSP_InxHeader = ^GSR_InxHeader;
GSR_InxHeader = Record
Vers1,
Vers2 : Integer;
Root : Longint;
Unknwn1 : Longint;
Entry_Sz : Integer;
Key_Lgth : Integer;
Key_Dcml : Integer;
Max_Keys : Integer;
Min_Keys : Integer;
Key_Form : array [0..1001] of char;
end;
GSP_InxDataBlk = ^GSR_InxDataBlk;
GSR_InxDataBlk = Record
case integer of
0 : (Data_Ary : array [0..NdxBlokSize] of byte);
1 : (Indx_Ary : array [0..NdxBlokSize div 2] of word);
2 : (Entry_Ct : Integer);
end;
GSP_InxElement = ^GSR_InxElement;
GSR_InxElement = Record
Block_Ax : Longint;
Recrd_Ax : Longint;
Char_Fld : array [1..255] of char;
end;
GSP_IndexFile = ^GSO_IndexFile;
GSP_InxNode = ^GSO_InxNode;
GSP_InxTable = ^GSO_InxTable;
GSO_InxTable = Object(TCollection)
ixLink : GSP_IndexFile;
ActivePage : GSP_InxNode;
Elements : array[0..NdxBlokSize div 12] of GSP_InxElement;
OkToClear : boolean;
constructor Init(ILink : GSP_IndexFile);
destructor Done; virtual;
function AccessPage(pn : longint) : pointer;
procedure AdjustNodePntrs(pn : longint);
function FetchBttm : pointer;
function FetchCurr : pointer;
function FetchNext : pointer;
function FetchPrev : pointer;
function FetchTop : pointer;
procedure NodeEntryDelete(en : integer);
procedure NodeEntryInsert(en : integer; wkey: string;
wb, wr: longint);
function NodeGet(pn : longint) : pointer;
procedure NodePntrReplace(en : integer; wkey: string; wb,wr: longint);
function NodePut(pn : longint) : pointer;
procedure ResetBuffers;
end;
GSO_InxNode = Object(TObject)
tbLink : GSP_InxTable; {Link to collection owner}
IndxBufr : GSP_InxDataBlk;
NodeLink : Longint;
Page_No : Longint; {Disk block holding node info}
Etry_No : Integer; {Last entry used in node}
Count : Integer; {Number of keys in this node }
NonLeaf : Boolean; {True for non-leaf nodes}
Changed : boolean;
constructor Init(CLink : GSP_InxTable; pn : longint);
destructor Done; virtual;
procedure Deliver;
procedure Retrieve;
end;
GSO_IndexFile = object(GSO_DiskFile)
ixColl : GSP_IndxColl;
ixKey_St : ixKeyString; {Holds last key value found}
ixKey_Num : longint; {Holds last physical record number}
IxKey_Form : string[255]; {Holds the key formula in type string}
ixKey_Siz : integer;
ixKey_Typ : char;
ixBOF : boolean;
ixEOF : boolean;
ixFollowKey : boolean; {Flag to follow key for next read when}
{the key is modified. If false, the }
{next record from the old key position }
{is read. If true, the next record from}
{the new key position is read. Default}
{is false}
tbLink : GSP_InxTable;
Ndx_Hdr : GSR_InxHeader;
Key_Lgth : Integer;
Max_Keys : Integer;
Entry_Sz : Integer;
CurrNode : GSP_InxNode;
CurrElmt : GSP_InxElement; {Pointer to key entry information}
CacheBuf : PByteArray;
CacheBlok : word;
Constructor Init(IName : string);
Constructor NewInit(filname,formla: string; lth,dcl: integer; typ: char);
Destructor Done; virtual;
Procedure IndxClear; virtual;
Procedure IndxStore(p : GSP_IndxColl; recnode : boolean); virtual;
Function KeyFind(st : String) : longint; virtual;
Procedure KeyList(st : string); virtual;
Function KeyLocRec(rec : longint) : boolean; virtual;
Function KeyRead(a : LongInt) : longint; virtual;
Procedure KeySort(kl : integer; sa : SortStatus); virtual;
Procedure KeyUpdate(rec: longint; st: string; Apnd: boolean); virtual;
Function Ndx_AdjVal(st : string): string;
Procedure Ndx_Close;
Procedure Ndx_Flush;
Procedure Ndx_GetHdr;
Function Ndx_NextBlock : longint;
Procedure Ndx_PutHdr;
Function Ndx_Root : Longint;
Procedure WriteStatus(RNum : longint); virtual;
end;
implementation
const
Same_Record = -5; {Token value passed to read the same record}
var
Ndx_Data : GSR_InxDataBlk;
Work_Key : string; {Holds key passed in Find and KeyUpdate}
RPag : Longint; {Work variable to hold current index block}
RNum : Longint; {Work variable for record number}
IsAscend : Boolean; {Flag for ascending/descending status.}
{Set based on Next/Previous Record read}
{------------------------------------------------------------------------------
GSO_InxTable
------------------------------------------------------------------------------}
constructor GSO_InxTable.Init(ILink : GSP_IndexFile);
var
p : pointer;
i : integer;
begin
TCollection.Init(32,16);
for i := 0 to ILink^.Max_Keys do
Elements[i] := Addr(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[i+1]]);
ixLink := ILink;
OkToClear := true;
end;
destructor GSO_InxTable.Done;
var
i : integer;
begin
TCollection.Done;
end;
function GSO_InxTable.AccessPage(pn : longint) : pointer;
var
p : GSP_InxNode;
px : longint;
i : integer;
ok : boolean;
begin
ok := false;
i := 0;
while (i < Count) and not ok do
begin
if GSP_InxNode(Items^[i])^.Page_No = pn then
begin
ok := true;
p := Items^[i];
AtDelete(i);
Insert(p);
end;
inc(i);
end;
if not ok then
begin
if Count > 7 then Free(Items^[0]);
p := New(GSP_InxNode, Init(@Self, pn));
Insert(p);
end;
ActivePage := p;
AccessPage := p;
end;
procedure GSO_InxTable.AdjustNodePntrs(pn : longint);
var
p : GSP_InxNode;
q : GSP_InxNode;
e : GSP_InxElement;
i : integer;
v : integer;
x : longint;
begin
p := AccessPage(pn);
if not p^.NonLeaf then exit;
for i := 0 to p^.Count-1 do
begin
e := Elements[i];
x := e^.Block_Ax;
for v := 0 to Count -1 do
begin
q := Items^[v];
if q^.Page_No = x then q^.NodeLink := pn;
end;
end;
end;
function GSO_InxTable.FetchBttm : pointer;
var
n : longint;
p : GSP_InxNode;
e : GSP_InxElement;
begin
p := NodeGet(ixLink^.Ndx_Root);
if p^.Count > 0 then e := Elements[p^.Count-1] else e := nil;
while p^.NonLeaf and (p^.Count > 0) do
begin
n := p^.Page_No;
p^.Etry_No := p^.Count;
if p^.Count > 0 then dec(p^.Etry_No);
p := NodeGet(e^.Block_Ax);
p^.NodeLink := n;
if p^.Count > 0 then e := Elements[p^.Count-1] else e := nil;
end;
p^.Etry_No := p^.Count;
if p^.Count > 0 then dec(p^.Etry_No);
FetchBttm := e;
end;
function GSO_InxTable.FetchCurr : pointer;
begin
FetchCurr := Elements[ActivePage^.Etry_No];
end;
function GSO_InxTable.FetchNext : pointer;
var
p : GSP_InxNode;
h : GSP_InxNode;
e : GSP_InxElement;
n : longint;
t : boolean;
begin
p := ActivePage;
p^.Retrieve;
h := p;
t := p^.NonLeaf;
inc(p^.Etry_No);
while (p^.Etry_No >= p^.Count) and (p^.NodeLink <> -1) do
begin
Delete(p);
AtInsert(0,p);
p := NodeGet(p^.NodeLink);
if t or (p^.Etry_No = p^.Count-1) then inc(p^.Etry_No);
end;
if (p^.Etry_No >= p^.Count) and (p^.Page_No = ixLink^.Ndx_Root) then
begin
FetchNext := nil;
dec(p^.Etry_No);
end
else if not t then
FetchNext := Elements[p^.Etry_No]
else
begin
e := Elements[p^.Etry_No];
while p^.NonLeaf do
begin
n := p^.Page_No;
p := NodeGet(e^.Block_Ax);
p^.NodeLink := n;
p^.Etry_No := 0;
if p^. Count > 0 then e := Elements[0] else e := nil;
end;
FetchNext := e;
end;
end;
function GSO_InxTable.FetchPrev : pointer;
var
p : GSP_InxNode;
h : GSP_InxNode;
e : GSP_InxElement;
n : longint;
t : boolean;
begin
p := ActivePage;
p^.Retrieve;
h := p;
t := p^.NonLeaf;
if not t then dec(p^.Etry_No);
while (p^.Etry_No < 0) and (p^.NodeLink <> -1) do
begin
Delete(p);
AtInsert(0,p);
p := NodeGet(p^.NodeLink);
dec(p^.Etry_No);
end;
if (p^.Etry_No < 0) and (p^.Page_No = ixLink^.Ndx_Root) then
begin
FetchPrev := nil;
inc(p^.Etry_No);
while p^.NonLeaf do
begin
e := Elements[p^.Etry_No];
p := NodeGet(e^.Block_Ax);
inc(p^.Etry_No);
end;
end
else if not t then FetchPrev := Elements[p^.Etry_No]
else
begin
e := Elements[p^.Etry_No];
while p^.NonLeaf and (p^.Count > 0) do
begin
n := p^.Page_No;
p := NodeGet(e^.Block_Ax);
p^.NodeLink := n;
p^.Etry_No := p^.Count-1;
if p^. Count > 0 then e := Elements[p^.Count-1] else e := nil;
end;
FetchPrev := e;
end;
end;
function GSO_InxTable.FetchTop : pointer;
var
p : GSP_InxNode;
e : GSP_InxElement;
n : longint;
begin
p := NodeGet(ixLink^.Ndx_Root);
if p^.Count > 0 then e := Elements[0] else e := nil;
while p^.NonLeaf and (p^.Count > 0) do
begin
n := p^.Page_No;
p^.Etry_No := 0;
p := NodeGet(e^.Block_Ax);
p^.NodeLink := n;
if p^.Count <= 0 then e := nil;
end;
p^.Etry_No := 0;
FetchTop := e;
end;
procedure GSO_InxTable.NodeEntryDelete(en : integer);
var
p : GSP_InxNode;
begin
p := ActivePage;
Move(Elements[en+1]^,Elements[en]^,ixLink^.Entry_Sz*(p^.Count-en));
dec(Ndx_Data.Entry_Ct);
p^.Deliver;
end;
procedure GSO_InxTable.NodeEntryInsert
(en : integer; wkey: string; wb,wr: longint);
var
p : GSP_InxNode;
e : GSP_InxElement;
begin
p := ActivePage;
e := Elements[en];
Move(Elements[en]^,Elements[en+1]^,ixLink^.Entry_Sz*(p^.Count-en));
move(wkey[1],e^.Char_Fld,ixLink^.Key_Lgth);
e^.Block_Ax := wb;
e^.Recrd_Ax := wr;
inc(Ndx_Data.Entry_Ct);
p^.Deliver;
end;
function GSO_InxTable.NodeGet(pn : longint) : pointer;
var
p : GSP_InxNode;
begin
p := AccessPage(pn);
p^.Retrieve;
NodeGet := p;
end;
procedure GSO_InxTable.NodePntrReplace
(en : integer; wkey: string; wb,wr: longint);
var
p : GSP_InxNode;
q : GSP_InxNode;
e : GSP_InxElement;
begin
p := ActivePage;
q := p;
p := NodeGet(p^.NodeLink);
while (p^.Etry_No >= p^.Count-1) and (p^.NodeLink <> -1) do
p := NodeGet(p^.NodeLink);
if p^.NodeLink <> -1 then
begin
e := Elements[p^.Etry_No];
FillChar(e^.Char_Fld, ixLink^.Key_Lgth, ' ');
move(wkey[1],e^.Char_Fld,length(wkey));
e^.Block_Ax := wb;
e^.Recrd_Ax := wr;
p^.Deliver;
end;
ActivePage := q;
ActivePage^.Retrieve;
end;
function GSO_InxTable.NodePut(pn : longint) : pointer;
var
p : GSP_InxNode;
begin
p := AccessPage(pn);
p^.Deliver;
NodePut := p;
end;
procedure GSO_InxTable.ResetBuffers;
begin
if OkToClear then FreeAll;
end;
{------------------------------------------------------------------------------
GSO_InxNode
------------------------------------------------------------------------------}
constructor GSO_InxNode.Init(CLink : GSP_InxTable; pn : longint);
var
i : integer;
r : word;
begin
IndxBufr := nil;
Page_No := pn;
Etry_No := -1;
Count := 0;
NonLeaf := true;
tbLink := CLink;
NodeLink := -1;
Changed := false;
end;
destructor GSO_InxNode.Done;
var
r : word;
begin
if IndxBufr <> nil then dispose(IndxBufr);
TObject.Done;
end;
procedure GSO_InxNode.Deliver;
var
v : longint;
begin
Count := Ndx_Data.Entry_Ct;
move(Ndx_Data.Data_Ary[(Count * tbLink^.ixLink^.Entry_Sz)],v,4);
NonLeaf := v <> 0;
if NonLeaf then Count := Count + 1;
if IndxBufr = nil then New(IndxBufr);
move(Ndx_Data,IndxBufr^,NdxBlokSize);
tbLink^.ixLink^.Write(Page_No,IndxBufr^,NdxBlokSize);
end;
procedure GSO_InxNode.Retrieve;
var
v : longint;
begin
if IndxBufr = nil then
begin
New(IndxBufr);
tbLink^.ixLink^.Read(Page_No,IndxBufr^,NdxBlokSize);
end;
move(IndxBufr^,Ndx_Data,NdxBlokSize);
Count := Ndx_Data.Entry_Ct;
move(Ndx_Data.Data_Ary[Ndx_Data.Indx_Ary[1]],v,4);
NonLeaf := v <> 0;
if nonLeaf then Count := Count + 1;
end;
{-----------------------------------------------------------------------------
GSO_IndexFile
------------------------------------------------------------------------------}
constructor GSO_IndexFile.Init(IName : string);
var
i : integer;
begin
GSO_DiskFile.Init(IName+'.NTX',dfReadWrite+dfSharedDenyNone);
dfFileFlsh := WriteFlush;
if dfFileExst then Reset(1)
else
begin
Error(dosFileNotFound,ndxInitError);
exit;
end;
Read(0,Ndx_Hdr,NdxBlokSize);
Key_Lgth := Ndx_Hdr.Key_Lgth;
Max_Keys := Ndx_Hdr.Max_Keys;
Entry_Sz := Ndx_Hdr.Entry_Sz;
move(Ndx_Hdr.Key_Form[0], ixKey_Form[1],241);
ixKey_Form[0] := #241;
ixKey_Form[0] := chr(pos(#0,ixKey_Form)-1);
ixKey_Form := TrimR(ixKey_Form);
ixKey_Form := TrimL(ixKey_Form);
ixKey_Siz := Key_Lgth;
ixBOF := false;
ixEOF := false;
ixKey_St := '';
ixKey_Num := 0;
ixFollowKey := false;
Read(Ndx_Root,Ndx_Data,NdxBlokSize);
tbLink := New(GSP_InxTable, Init(@Self));
end;
Constructor GSO_IndexFile.NewInit(filname,formla : string; lth,dcl: integer;
typ : char);
var
i : integer;
begin
GSO_DiskFile.Init(filname+'.NTX',dfReadWrite);
dfFileFlsh := WriteFlush;
Rewrite(1);
FillChar(Ndx_Hdr, SizeOf(Ndx_Hdr),#0);
Ndx_Hdr.Root := NdxBlokSize;
Ndx_Hdr.Vers1 := 6;
Ndx_Hdr.Vers2 := 1;
lth := lth+dcl;
if dcl > 0 then inc(lth); {account for decimal point}
Ndx_Hdr.Key_Lgth := lth;
Ndx_Hdr.Key_Dcml := dcl;
i := lth+8;
Ndx_Hdr.Max_Keys := ((NdxBlokSize-4) div (i+2)) - 1;
if odd(Ndx_Hdr.Max_Keys) then dec(Ndx_Hdr.Max_Keys);
Ndx_Hdr.Min_Keys := Ndx_Hdr.Max_Keys div 2;
Ndx_Hdr.Entry_Sz := i;
CnvStrToAsc(formla,Ndx_Hdr.Key_Form, length(formla)+1);
Write(0,Ndx_Hdr,NdxBlokSize);
Key_Lgth := lth;
Max_Keys := Ndx_Hdr.Max_Keys;
Entry_Sz := Ndx_Hdr.Entry_Sz;
ixKey_Form := formla;
ixKey_Form := TrimR(ixKey_Form);
ixKey_Form := TrimL(ixKey_Form);
ixKey_Siz := Key_Lgth;
ixKey_Typ := typ;
ixBOF := false;
ixEOF := false;
ixKey_St := '';
ixKey_Num := 0;
ixFollowKey := false;
FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
for i := 0 to Ndx_Hdr.Max_Keys do Ndx_Data.Indx_Ary[succ(i)] :=
((Ndx_Hdr.Max_Keys + 2) * 2) + (Ndx_Hdr.Entry_Sz * i);
Write(-1,Ndx_Data,NdxBlokSize);
tbLink := New(GSP_InxTable, Init(@Self));
end;
Destructor GSO_IndexFile.Done;
var
i : integer;
begin
Ndx_Close;
GSO_DiskFile.Done;
end;
Procedure GSO_IndexFile.IndxClear;
var
i : integer;
begin
Ndx_Flush;
Ndx_GetHdr;
Ndx_Hdr.Root := 1;
Write(0,Ndx_Hdr,NdxBlokSize);
ixBOF := false;
ixEOF := false;
ixKey_St := '';
ixKey_Num := 0;
FillChar(Ndx_Data, SizeOf(Ndx_Data),#0);
Write(-1,Ndx_Data,NdxBlokSize);
Truncate(-1);
end;
Procedure GSO_IndexFile.IndxStore(p: GSP_IndxColl; recnode: boolean);
var
i : integer;
rc : integer;
rl : word;
dt : longint;
ec : longint;
kc : integer;
mh : integer;
mk : integer;
mm : integer;
mr : integer;
mv : integer;
rf : GSP_IndxEtry;
rr : GSP_IndxEtry;
sc : integer;
sv : string[104];
dl : integer;
ixFiller : array[0..NdxBlokSize+108] of byte;
ixData : GSR_InxDataBlk absolute ixFiller;
ixPntr : GSP_InxElement;
ixBlok : longint;
NodeColl : GSP_IndxColl;
procedure CacheWrite;
begin
move(ixData,CacheBuf^[CacheBlok],NdxBlokSize);
CacheBlok := CacheBlok+NdxBlokSize;
if CacheBlok >= NdxBlokSize*16 then
begin
Write(-1,CacheBuf^,CacheBlok);
CacheBlok := 0;
end;
end;
procedure CollectNodes;
begin
ixData.Entry_Ct := rc-1;
CacheWrite;
if recnode then
begin
move(rr^.Tag,sv[Key_Lgth+1],4); {Hang on to Record number}
sv[0] := chr(Key_Lgth+4);
end;
NodeColl^.InsertKey(ixBlok, sv);
rc := 0;
inc(ixBlok,NdxBlokSize);
mk := mv;
if mm > 0 then
begin
inc(mk);
dec(mm);
end;
end;
begin
mk := Max_Keys;
if recnode then mr := 1 else mr := 0;
kc := p^.KeyCount;
if kc <= mk then
begin
mk := kc+1;
mv := mk;
mm := 0;
end
else
begin
i := kc;
mv := 0;
repeat
mh := mv;
mv := i div mk;
inc(mv);
i := (kc - mv) + mr;
until mh = mv;
mm := i mod mv;
mk := i div mv;
inc(mk); {to keep things balanced on leaf nodes}
mv := mk;
if mm > 0 then
begin
inc(mk);
dec(mm);
end;
end;
if recnode then
begin
ixBlok := NdxBlokSize;
GetMem(CacheBuf,NdxBlokSize*16);
Read(0,CacheBuf^,NdxBlokSize); {Position to initial loc}
end
else
begin
ixBlok := Ndx_NextBlock;
end;
CacheBlok := 0;
New(NodeColl, Init(Key_Lgth+4,NoSort));
rr := p^.RetrieveKey;
rc := 0;
ec := 0;
FillChar(ixData, SizeOf(ixData),#0);
dl := (Max_Keys + 2) * 2;
for i := 0 to Max_Keys do ixData.Indx_Ary[i+1] := (dl + (Entry_Sz * i));
while rr <> nil do
begin
rf := rr;
ixPntr := Addr(ixData.Data_Ary[ixData.Indx_Ary[rc+1]]);
sv := rr^.KeyStr;
if (ixKey_Typ = 'N') and recnode then
begin
sv := PadL(sv, Key_Lgth);
for sc := 1 to length(sv) do
if sv[sc] = ' ' then sv[sc] := '0';
end;
move(sv[1],IxPntr^.Char_Fld[1],Key_Lgth);
if recnode then
begin
IxPntr^.Recrd_Ax := rr^.Tag;
IxPntr^.Block_Ax := 0;
end
else
begin
move(rr^.KeyStr[Key_Lgth+1],IxPntr^.Recrd_Ax,4); {Load Record number}
IxPntr^.Block_Ax := rr^.Tag;
end;
inc(rc);
inc(ec);
WriteStatus(ec);
if rc >= mk then CollectNodes;
rr := p^.RetrieveKey;
end;
if rc > 0 then
begin
rr := rf;
if recnode then inc(rc);
CollectNodes;
end;
p^.EndRetrieve;
if CacheBlok > 0 then Write(-1,CacheBuf^,CacheBlok);
if ec > Max_Keys then IndxStore(NodeColl, false);
Dispose(NodeColl, Done);
if recnode then
begin
FreeMem(CacheBuf,NdxBlokSize*16);
Dispose(ixColl, Done);
Ndx_Hdr.Root := Ndx_NextBlock-NdxBlokSize;
Ndx_Flush;
end;
end;
Function GSO_IndexFile.KeyFind(st : string) : LongInt;
var
i : integer; {Work variable}
rl : integer; {Result code for Val procedure}
ct : integer; {Variable to hold BlockRead byte count}
IsEqual : boolean; {Flag to hunt for key match}
PNode : longint;
Match_Cnd : integer;
LeafPag : longint;
LeafEtry : integer;
procedure StoreMatchValue;
begin
move(CurrElmt^.Char_Fld,ixKey_St[1],Key_Lgth);
{Move the key field to Ndx_Key_St.}
ixKey_St[0] := Work_Key[0]; {Now insert the length into Ndx_Key_St}
end;
function DoMatchValue : integer;
begin
Match_Cnd := StrCompare(ixKey_St, Work_Key);
DoMatchValue := Match_Cnd;
end;
function SearchMatchValue(var Index: Integer): Boolean;
var
L,
H,
I,
C: Integer;
begin
SearchMatchValue := False;
L := 0;
H := CurrNode^.Count - 1;
while L <= H do
begin
I := (L + H) shr 1;
CurrElmt := tbLink^.Elements[I];
if (CurrNode^.NonLeaf) and (CurrNode^.Count-1 = I) then
C := 1
else
begin
StoreMatchValue;
C := DoMatchValue;
end;
if C < 0 then L := I + 1 else
begin
H := I - 1;
if C = 0 then SearchMatchValue := true;
end;
end;
CurrElmt := tbLink^.Elements[L];
StoreMatchValue;
Index := L;
end;
begin
{ tbLink^.ResetBuffers;}
ixKey_Num := 0; {Initialize}