home *** CD-ROM | disk | FTP | other *** search
Wrap
{ Copyright: Vlad Karpov mailto:KarpovVV@protek.ru Author: Vlad Karpov } unit VKDBFNTX; interface uses Windows, Messages, SysUtils, Classes, contnrs, db, {$IFDEF VER140} Variants, {$ENDIF} VKDBFPrx, VKDBFParser, VKDBFIndex, VKDBFUtil, VKDBFMemMgr; const NTX_MAX_KEY = 256; // Maximum of length of key NTX_PAGE = 1024; // Dimantion of NTX page MAX_LEV_BTREE = 20; // Maximum depth of BTREE CL501RUSORDER: array [0..255] of Byte = ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 190, 191, 192, 193, 194, 195, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 196, 197, 198, 199, 200, 108, 109, 110, 111, 112, 113, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 143, 144, 145, 146, 147, 148, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 114, 149, 176, 177, 141, 178, 142, 179, 249, 250, 251, 252, 253, 254, 48, 255); type TDeleteKeyStyle = (dksClipper, dksApolloHalcyon); //NTX Structute NTX_HEADER = packed record sign: WORD; //2 0 version: WORD; //2 2 root: DWORD; //4 4 next_page: DWORD; //4 8 item_size: WORD; //2 12 key_size: WORD; //2 14 key_dec: WORD; //2 16 max_item: WORD; //2 18 half_page: WORD; //2 20 key_expr: array [0..NTX_MAX_KEY-1] of Char; //256 22 unique: Char; //1 278 reserv1: Char; //1 279 desc: Char; //1 280 reserv3: Char; //1 281 for_expr: array [0..NTX_MAX_KEY-1] of Char; //256 282 order: array [0..7] of char; //8 538 Rest: array [0..477] of char; //478 546 end; //1024 // // Describer one ITEM // NTX_ITEM = packed record page: DWORD; rec_no: DWORD; key: array[0..NTX_PAGE-1] of Char; end; pNTX_ITEM = ^NTX_ITEM; // // Beginign of Index page // NTX_BUFFER = packed record count: WORD; ref: array[0..510] of WORD; end; pNTX_BUFFER = ^NTX_BUFFER; // // Block item for compact indexing // BLOCK_ITEM = packed record rec_no: DWORD; key: array[WORD] of Char; end; pBLOCK_ITEM = ^BLOCK_ITEM; // // Block for compact indexing // BLOCK_BUFFER = packed record count: WORD; ref: array[WORD] of WORD; end; pBLOCK_BUFFER = ^BLOCK_BUFFER; TBTreeLevels = array [0..MAX_LEV_BTREE] of NTX_BUFFER; pBTreeLevels = ^TBTreeLevels; // // Abstract class Iterator // TVKNTXIndexIterator = class(TObject) public item: NTX_ITEM; Eof: boolean; procedure Open; virtual; abstract; procedure Close; virtual; abstract; procedure Next; virtual; abstract; constructor Create; destructor Destroy; override; end; // // Block class Iterator // TVKNTXBlockIterator = class(TVKNTXIndexIterator) protected i: Integer; FBufSize: Integer; Fkey_size: Integer; FFileName: String; FHndl: Integer; p: pBLOCK_BUFFER; public procedure Open; override; procedure Close; override; procedure Next; override; constructor Create(FileName: String; key_size, BufSize: Integer); overload; destructor Destroy; override; end; // // NTX class Iterator // TVKNTXIterator = class(TVKNTXIndexIterator) protected FFileName: String; FHndl: Integer; SHead: NTX_HEADER; levels: pBTreeLevels; indexes: array [0..MAX_LEV_BTREE] of WORD; cur_lev: Integer; public procedure Open; override; procedure Close; override; procedure Next; override; constructor Create(FileName: String); overload; destructor Destroy; override; end; // // Compact index class for CreateCompact method TVKNTXIndex // TVKNTXCompactIndex = class(TObject) private FHndl: Integer; SHead: NTX_HEADER; levels: TBTreeLevels; cur_lev: Integer; max_lev: Integer; SubOffSet: DWORD; CryptPage: NTX_BUFFER; public FileName: String; OwnerTable: TDataSet; Crypt: boolean; Handler: TProxyStream; constructor Create; destructor Destroy; override; procedure NewPage(lev: Integer); procedure CreateEmptyIndex(var FHead: NTX_HEADER); procedure AddItem(item: pNTX_ITEM); procedure LinkRest; procedure NormalizeRest; procedure Close; end; //Forword declarations TVKNTXIndex = class; {TVKNTXBuffer} TVKNTXBuffer = class private Fpage_offset: DWORD; Fchanged: boolean; Fpage: NTX_BUFFER; public constructor Create; end; {TVKNTXBuffers} TVKNTXBuffers = class(TObjectList) private NXTObject: TVKNTXIndex; function FindIndex(page_offset: DWORD; out Ind: Integer): boolean; public function GetPage(Handle: TProxyStream; page_offset: DWORD; out page: pNTX_BUFFER; fRead: boolean = true): Integer; function GetNTXBuffer(Handle: TProxyStream; page_offset: DWORD; out page: pNTX_BUFFER; fRead: boolean = true): Pointer; procedure SetPage(Handle: TProxyStream; page_offset: DWORD; page: pNTX_BUFFER); procedure SetChanged(i: Integer); procedure Flush(Handle: TProxyStream); end; {TVKNTXRange} TVKNTXRange = class(TPersistent) private FActive: boolean; FLoKey: String; FHiKey: String; FNTX: TVKNTXIndex; function GetActive: boolean; procedure SetActive(const Value: boolean); protected public function InRange(S: String): boolean; procedure ReOpen; property NTX: TVKNTXIndex read FNTX write FNTX; published property Active: boolean read GetActive write SetActive; property HiKey: String read FHiKey write FHiKey; property LoKey: String read FLoKey write FLoKey; end; {TVKNTXOrder} TVKNTXOrder = class(TVKDBFOrder) public FHead: NTX_HEADER; constructor Create(Collection: TCollection); override; destructor Destroy; override; function CreateOrder: boolean; override; published property OnCreateIndex; property OnEvaluteKey; property OnEvaluteFor; property OnCompareKeys; end; {TVKNTXBag} TVKNTXBag = class(TVKDBFIndexBag) private //FLstOffset: DWORD; public constructor Create(Collection: TCollection); override; destructor Destroy; override; function CreateBag: boolean; override; function Open: boolean; override; function IsOpen: boolean; override; procedure Close; override; procedure FillHandler; //property FLastOffset: DWORD read FLstOffset write FLstOffset; property NTXHandler: TProxyStream read Handler write Handler; end; {TVKNTXIndex} TVKNTXIndex = class(TIndex) private FNTXBag: TVKNTXBag; FNTXOrder: TVKNTXOrder; FLastOffset: DWORD; FReindex: boolean; FCreateIndexProc: boolean; FNTXBuffers: TVKNTXBuffers; FNTXFileName: String; //NTXHandler: Integer; //FHead: NTX_HEADER; FKeyExpresion: String; FForExpresion: String; FKeyParser: TVKDBFExprParser; FForParser: TVKDBFExprParser; FForExists: boolean; FKeyTranslate: boolean; FCl501Rus: boolean; FFileLock: boolean; FSeekRecord: Integer; FSeekKey: String; FSeekOk: boolean; FTemp: boolean; FNTXRange: TVKNTXRange; FOnSubNtx: TOnSubNtx; FDestructor: boolean; FClipperVer: TCLIPPER_VERSION; FUpdated: boolean; FFLastFUpdated: boolean; FDeleteKeyStyle: TDeleteKeyStyle; FUnique: boolean; FDesc: boolean; FOrder: String; procedure SetNTXFileName(const Value: String); procedure SetKeyExpresion(Value: String); procedure SetForExpresion(Value: String); function CompareKeys(S1, S2: PChar; MaxLen: Cardinal): Integer; function GetFreePage: DWORD; procedure SetUnique(const Value: boolean); function GetUnique: boolean; procedure SetDesc(const Value: boolean); function GetDesc: boolean; function SeekFirstInternal(Key: String; SoftSeek: boolean = false): boolean; function SeekLastInternal(Key: String; SoftSeek: boolean = false): boolean; procedure ChekExpression(var Value: String); function GetOwnerTable: TDataSet; procedure ClearIfChange; function GetCreateNow: Boolean; procedure SetCreateNow(const Value: Boolean); protected FCurrentKey: String; FCurrentRec: DWORD; function GetIsRanged: boolean; override; procedure AssignIndex(oInd: TVKNTXIndex); function InternalFirst: TGetResult; override; function InternalNext: TGetResult; override; function InternalPrior: TGetResult; override; function InternalLast: TGetResult; override; function GetCurrentKey: String; override; function GetCurrentRec: DWORD; override; //function FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer; //function FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer; function AddItem(ntxItem: pNTX_ITEM): boolean; function GetOrder: String; override; procedure SetOrder(Value: String); override; procedure DefineBag; override; procedure DefineBagAndOrder; override; public constructor Create(Collection: TCollection); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; function IsEqual(Value: TIndex): Boolean; override; function CmpKeys(ItemKey, CurrKey: pChar; KSize: Integer = 0): Integer; function CmpKeys1(ItemKey, CurrKey: pChar; KSize: Integer = 0): Integer; function CmpKeys2(ItemKey, CurrKey: pChar; KSize: Integer = 0): Integer; function CmpKeys3(ItemKey, CurrKey: pChar; KSize: Integer = 0): Integer; procedure TransKey(Key: pChar; KSize: Integer = 0; ToOem: Boolean = true); overload; function TransKey(Key: String): String; overload; function Open: boolean; override; procedure Close; override; function IsOpen: boolean; override; function SetToRecord: boolean; overload; override; function SetToRecord(Rec: Longint): boolean; overload; override; function SetToRecord(Key: String; Rec: Longint): boolean; overload; override; // function Seek(Key: String; SoftSeek: boolean = false): boolean; override; function SeekFirst( Key: String; SoftSeek: boolean = false; PartialKey: boolean = false): boolean; override; function SeekFirstRecord( Key: String; SoftSeek: boolean = false; PartialKey: boolean = false): Integer; override; function SeekFields(const KeyFields: string; const KeyValues: Variant; SoftSeek: boolean = false; PartialKey: boolean = false): Integer; override; // It is a new find mashine subject to SetDeleted, Filter and Range function FindKey(Key: String; PartialKey: boolean = false; SoftSeek: boolean = false; Rec: DWORD = 0): Integer; override; function FindKeyFields( const KeyFields: string; const KeyValues: Variant; PartialKey: boolean = false): Integer; overload; override; function FindKeyFields( const KeyFields: string; const KeyValues: array of const; PartialKey: boolean = false): Integer; overload; override; function FindKeyFields( PartialKey: boolean = false): Integer; overload; override; // function SubIndex(LoKey, HiKey: String): boolean; override; function SubNtx(var SubNtxFile: String; LoKey, HiKey: String): boolean; function FillFirstBufRecords(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): longint; override; function FillLastBufRecords(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): longint; override; function EvaluteKeyExpr: String; override; function SuiteFieldList(fl: String; out m: Integer): Integer; override; function EvaluteForExpr: boolean; override; function GetRecordByIndex(GetMode: TGetMode; var cRec: Longint): TGetResult; override; function GetFirstByIndex(var cRec: Longint): TGetResult; override; function GetLastByIndex(var cRec: Longint): TGetResult; override; procedure First; override; procedure Next; override; procedure Prior; override; procedure Last; override; function LastKey(out LastKey: String; out LastRec: LongInt): boolean; override; function NextBuffer(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): Longint; override; function PriorBuffer(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): Longint; override; procedure SetRangeFields(FieldList: String; FieldValues: array of const); overload; override; procedure SetRangeFields(FieldList: String; FieldValues: variant); overload; override; function InRange(Key: String): boolean; overload; override; function InRange: boolean; overload; override; function FLock: boolean; override; function FUnLock: boolean; override; procedure StartUpdate(UnLock: boolean = true); override; procedure Flush; override; procedure DeleteKey(sKey: String; nRec: Longint); override; procedure AddKey(sKey: String; nRec: Longint); override; // All index create in memory. Fast, but need much memory procedure CreateIndex(Activate: boolean = true); override; // Save on disk sorted blocks, then merge blocks into BTrees. Slowly CreateIndex, but no need memory procedure CreateCompactIndex(BlockBufferSize: LongWord = 65536; Activate: boolean = true); override; procedure Reindex(Activate: boolean = true); override; procedure Truncate; override; procedure BeginCreateIndexProcess; override; procedure EvaluteAndAddKey(nRec: DWORD); override; procedure EndCreateIndexProcess; override; function IsUniqueIndex: boolean; override; function IsForIndex: boolean; override; property OwnerTable: TDataSet read GetOwnerTable; published property NTXFileName: String read FNTXFileName write SetNTXFileName; property KeyExpresion: String read FKeyExpresion write SetKeyExpresion stored false; property ForExpresion: String read FForExpresion write SetForExpresion stored false; property KeyTranslate: boolean read FKeyTranslate write FKeyTranslate default true; property Clipper501RusOrder: boolean read FCl501Rus write FCl501Rus; property Unique: boolean read GetUnique write SetUnique; property Desc: boolean read GetDesc write SetDesc; property Order; property Temp: boolean read FTemp write FTemp; property NTXRange: TVKNTXRange read FNTXRange write FNTXRange; property ClipperVer: TCLIPPER_VERSION read FClipperVer write FClipperVer default v500; property CreateNow: Boolean read GetCreateNow write SetCreateNow; property DeleteKeyStyle: TDeleteKeyStyle read FDeleteKeyStyle write FDeleteKeyStyle; property OnCreateIndex; property OnSubIndex; property OnEvaluteKey; property OnEvaluteFor; property OnCompareKeys; property OnSubNtx: TOnSubNtx read FOnSubNtx write FOnSubNtx; end; implementation uses DBCommon, Dialogs, VKDBFDataSet; { TVKNTXIndex } procedure TVKNTXIndex.Assign(Source: TPersistent); begin if Source is TVKNTXIndex then AssignIndex(TVKNTXIndex(Source)) else inherited Assign(Source); end; procedure TVKNTXIndex.AssignIndex(oInd: TVKNTXIndex); begin if oInd <> nil then begin Name := oInd.Name; NTXFileName := oInd.NTXFileName; end; end; procedure TVKNTXIndex.Close; begin if not IsOpen then Exit; Flush; FNTXBuffers.Clear; FNTXBag.Close; FForExists := false; if FTemp then begin DeleteFile(FNTXFileName); if not FDestructor then Collection.Delete(Index); end; end; constructor TVKNTXIndex.Create(Collection: TCollection); var FieldMap: TFieldMap; begin inherited Create(Collection); FClipperVer := v500; FDeleteKeyStyle := dksClipper; FUnique := False; FDesc := False ; FOrder := ''; (* FNTXOrder.FHead.sign := 6; FNTXOrder.FHead.version := 1; FNTXOrder.FHead.root := 0; FNTXOrder.FHead.next_page := 0; FNTXOrder.FHead.item_size := 0; FNTXOrder.FHead.key_size := 0; FNTXOrder.FHead.key_dec := 0; FNTXOrder.FHead.max_item := 0; FNTXOrder.FHead.half_page := 0; for i := 0 to NTX_MAX_KEY-1 do FNTXOrder.FHead.key_expr[i] := #0; FNTXOrder.FHead.unique := #0; FNTXOrder.FHead.reserv1 := #0; FNTXOrder.FHead.desc := #0; FNTXOrder.FHead.reserv3 := #0; for i := 0 to NTX_MAX_KEY-1 do FNTXOrder.FHead.for_expr[i] := #0; for i := 0 to 7 do FNTXOrder.FHead.order[i] := #0; for i := 0 to 477 do FNTXOrder.FHead.Rest[i] := #0; *) FKeyParser := TVKDBFExprParser.Create(TVKDBFNTX(FIndexes.Owner), '', [], [poExtSyntax], '', nil, FieldMap); FKeyParser.IndexKeyValue := true; FForParser := TVKDBFExprParser.Create(TVKDBFNTX(FIndexes.Owner), '', [], [poExtSyntax], '', nil, FieldMap); FForParser.IndexKeyValue := true; FKeyTranslate := true; FCl501Rus := false; FFileLock := false; FTemp := false; FForExists := false; FNTXRange := TVKNTXRange.Create; FNTXRange.NTX := self; FNTXBuffers := TVKNTXBuffers.Create; FNTXBuffers.NXTObject := self; FCreateIndexProc:= false; FReindex := false; FOnSubNtx := nil; FDestructor := false; end; destructor TVKNTXIndex.Destroy; begin FDestructor := true; if IsOpen then Close; FKeyParser.Free; FForParser.Free; FNTXRange.Free; FNTXBuffers.Free; if TIndexes(Collection).ActiveObject = self then TIndexes(Collection).ActiveObject := nil; inherited Destroy; end; function TVKNTXIndex.EvaluteForExpr: boolean; begin if Assigned(FOnEvaluteFor) then FOnEvaluteFor(self, Result) else Result := FForParser.Execute; end; function TVKNTXIndex.EvaluteKeyExpr: String; begin if Assigned(FOnEvaluteKey) then FOnEvaluteKey(self, Result) else Result := FKeyParser.EvaluteKey; end; function TVKNTXIndex.InternalFirst: TGetResult; var level: Integer; v: WORD; function Pass(page_off: DWORD): TGetResult; var page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; begin Inc(level); try FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[0]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result = grOK then Exit; end; if page.count <> 0 then begin // if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(FCurrentKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(FCurrentKey, item.key, FNTXOrder.FHead.key_size); FCurrentRec := item.rec_no; // Result := grOK; end else if level = 1 then Result := grEOF else Result := grError; Exit; finally Dec(level); end; end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; level := 0; Result := Pass(FNTXOrder.FHead.root); end; procedure TVKNTXIndex.First; begin if InternalFirst = grOk then TVKDBFNTX(FIndexes.Owner).RecNo := FCurrentRec; end; function TVKNTXIndex.IsEqual(Value: TIndex): Boolean; var oNTX: TVKNTXIndex; begin oNTX := Value as TVKNTXIndex; Result := ( (FName = oNTX.Name) and (FNTXFileName = oNTX.NTXFileName) ); end; function TVKNTXIndex.IsOpen: boolean; begin Result := ((FNTXBag <> nil) and (FNTXBag.IsOpen)); end; function TVKNTXIndex.InternalLast: TGetResult; var level: Integer; v: WORD; function Pass(page_off: DWORD): TGetResult; var page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; begin Inc(level); try FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result = grOK then Exit; end; if page.count <> 0 then begin // item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(FCurrentKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(FCurrentKey, item.key, FNTXOrder.FHead.key_size); FCurrentRec := item.rec_no; // Result := grOK; end else if level = 1 then Result := grBOF else Result := grError; Exit; finally Dec(level); end; end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; level := 0; Result := Pass(FNTXOrder.FHead.root); end; function TVKNTXIndex.InternalPrior: TGetResult; var Found: boolean; gr: TGetResult; v: WORD; procedure Pass(page_off: DWORD); var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; c: Integer; procedure SetCurrentKey; begin if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(FCurrentKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(FCurrentKey, item.key, FNTXOrder.FHead.key_size); FCurrentRec := item.rec_no; end; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(FCurrentKey)); if c <= 0 then begin if ( FCurrentRec = item.rec_no ) and ( c = 0 ) then begin Found := true; if ( item.page = 0 ) then begin if ( i <> 0 ) then begin gr := grOK; item := pNTX_ITEM(pChar(page) + page.ref[i - 1]); SetCurrentKey; end; end else begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, item.page, page); if page.count > 0 then begin gr := grOK; item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); SetCurrentKey; end else gr := grError; end; Exit; end; if ( item.page <> 0 ) then Pass(item.page); if Found and (gr = grBOF) then begin if ( i <> 0 ) then begin gr := grOK; item := pNTX_ITEM(pChar(page) + page.ref[i - 1]); SetCurrentKey; end; Exit; end; if gr = grError then Exit; if gr = grOK then Exit; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); if Found and (gr = grBOF ) then begin if ( page.count <> 0 ) then begin gr := grOK; item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); SetCurrentKey; end else gr := grError; end; end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; gr := grBOF; Found := false; Pass(FNTXOrder.FHead.root); Result := gr; end; function TVKNTXIndex.Open: boolean; var oW: TVKDBFNTX; begin oW := TVKDBFNTX(FIndexes.Owner); DefineBagAndOrder; FNTXBuffers.Clear; if not ((FNTXOrder.FHead.sign = 6) or (FNTXOrder.FHead.sign = 7)) then begin FNTXBag.Close; raise Exception.Create('TVKNTXIndex.Open: File "' + FNTXFileName + '" is not NTX file'); end; Result := IsOpen; if Result then begin FLastOffset := FNTXBag.NTXHandler.Seek(0, 2); if ( ( ( oW.AccessMode.FLast and fmShareExclusive ) = fmShareExclusive ) or ( ( oW.AccessMode.FLast and fmShareDenyWrite ) = fmShareDenyWrite ) ) then StartUpdate; InternalFirst; KeyExpresion := FNTXOrder.FHead.key_expr; ForExpresion := FNTXOrder.FHead.for_expr; if ForExpresion <> '' then FForExists := true; end; end; function TVKNTXIndex.InternalNext: TGetResult; var Found: Boolean; gr: TGetResult; v: WORD; procedure Pass(page_off: DWORD); var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; c: Integer; level: Integer; procedure SetCurrentKey; begin if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(FCurrentKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(FCurrentKey, item.key, FNTXOrder.FHead.key_size); FCurrentRec := item.rec_no; end; procedure GetFirstFromSubTree(page_off: DWORD); begin Inc(level); try FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[0]); if ( item.page <> 0 ) then begin GetFirstFromSubTree(item.page); if gr = grOK then Exit; end; if page.count <> 0 then begin SetCurrentKey; gr := grOK; end else if level = 1 then gr := grEOF else gr := grError; Exit; finally Dec(level); end; end; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(FCurrentKey)); if c <= 0 then begin if ( FCurrentRec = item.rec_no ) and ( c = 0 ) then begin Found := true; // SetCurrentKey; item := pNTX_ITEM(pChar(page) + page.ref[i + 1]); if item.page <> 0 then begin level := 0; GetFirstFromSubTree(item.page); end else begin if ( ( i + 1 ) = page.count ) then begin gr := grEOF; end else begin gr := grOK; SetCurrentKey; end; end; // Exit; end; if ( item.page <> 0 ) then Pass(item.page); if (gr = grOK) then Exit; if Found and (gr = grEOF) then begin gr := grOK; SetCurrentKey; Exit; end; if gr = grError then Exit; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; Found := false; gr := grEOF; Pass(FNTXOrder.FHead.root); Result := gr; end; function TVKNTXIndex.Seek(Key: String; SoftSeek: boolean = false): boolean; var R: Integer; begin R := FindKey(Key, false, SoftSeek); if R <> 0 then begin (TVKDBFNTX(FIndexes.Owner)).RecNo := R; Result := True; end else Result := False; end; function TVKNTXIndex.SeekFirstInternal( Key: String; SoftSeek: boolean = false): boolean; var lResult, SoftSeekSet: boolean; procedure Pass(page_off: DWORD); var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin item := nil; for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(Key)); if c < 0 then begin //Key < item.key if ( item.page <> 0 ) then Pass(item.page); if (SoftSeek) and (not lResult) and (not SoftSeekSet) then begin FSeekRecord := item.rec_no; SetString(FSeekKey, item.key, FNTXOrder.FHead.key_size); SoftSeekSet := true; FSeekOk := true; end; Exit; end; if c = 0 then begin //Key = item.key if ( item.page <> 0 ) then Pass(item.page); if not lResult then begin FSeekRecord := item.rec_no; SetString(FSeekKey, item.key, FNTXOrder.FHead.key_size); FSeekOk := true; lResult := true; end; Exit; end; end; FSeekRecord := item.rec_no; SetString(FSeekKey, item.key, FNTXOrder.FHead.key_size); FSeekOk := true; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); end; begin FSeekOk := false; if FLock then try ClearIfChange; SoftSeekSet := false; lResult := false; Pass(FNTXOrder.FHead.root); Result := lResult; finally FUnLock; end else Result := false; end; function TVKNTXIndex.SeekFirst( Key: String; SoftSeek: boolean = false; PartialKey: boolean = false): boolean; var R: Integer; begin R := FindKey(Key, PartialKey, SoftSeek); if R <> 0 then begin (TVKDBFNTX(FIndexes.Owner)).RecNo := R; Result := True; end else Result := False; end; procedure TVKNTXIndex.SetKeyExpresion(Value: String); begin ChekExpression(Value); FKeyExpresion := Value; FKeyParser.SetExprParams1(FKeyExpresion, [], [poExtSyntax], ''); end; procedure TVKNTXIndex.SetForExpresion(Value: String); begin ChekExpression(Value); FForExpresion := Value; FForParser.SetExprParams1(FForExpresion, [], [poExtSyntax], ''); end; procedure TVKNTXIndex.SetNTXFileName(const Value: String); var PointPos: Integer; begin FNTXFileName := Value; FName := ExtractFileName(FNTXFileName); PointPos := Pos('.', FName); if PointPos <> 0 then FName := Copy(FName, 1, PointPos - 1); end; function TVKNTXIndex.SubIndex(LoKey, HiKey: String): boolean; var l, m: Integer; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; S: String; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(LoKey), m); if c <= 0 then begin //LoKey <= item.key if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; c := CmpKeys(item.key, pChar(HiKey), l); if c < 0 then begin // HiKey < item.key Result := true; Exit; end; if Assigned(OnSubIndex) then begin SetString(S, item.key, FNTXOrder.FHead.key_size); OnSubIndex(self, S, item.rec_no); end; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); Result := false; end; begin if FLock then try ClearIfChange; m := Length(LoKey); if FNTXOrder.FHead.key_size < m then m := FNTXOrder.FHead.key_size; l := Length(HiKey); if FNTXOrder.FHead.key_size < l then l := FNTXOrder.FHead.key_size; Pass(FNTXOrder.FHead.root); Result := true; finally FUnLock; end else Result := false; end; function TVKNTXIndex.SubNtx(var SubNtxFile: String; LoKey, HiKey: String): boolean; var l, m: Integer; Accept: boolean; oSubIndex: TVKNTXCompactIndex; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; itm: NTX_ITEM; c: Integer; S: String; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(LoKey), m); if c <= 0 then begin //LoKey <= item.key if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; c := CmpKeys(item.key, pChar(HiKey), l); if c < 0 then begin // HiKey < item.key Result := true; Exit; end; Accept := true; if Assigned(OnSubNtx) then begin SetString(S, item.key, FNTXOrder.FHead.key_size); OnSubNtx(self, S, item.rec_no, Accept); end; if Accept then begin Move(item^, itm, FNTXOrder.FHead.item_size); oSubIndex.AddItem(@itm); end; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); Result := false; end; begin oSubIndex := TVKNTXCompactIndex.Create; try oSubIndex.FileName := SubNtxFile; oSubIndex.OwnerTable := OwnerTable; oSubIndex.Crypt := TVKDBFNTX(OwnerTable).Crypt.Active; oSubIndex.CreateEmptyIndex(FNTXOrder.FHead); if oSubIndex.FHndl > 0 then try if FLock then try ClearIfChange; m := Length(LoKey); if FNTXOrder.FHead.key_size < m then m := FNTXOrder.FHead.key_size; l := Length(HiKey); if FNTXOrder.FHead.key_size < l then l := FNTXOrder.FHead.key_size; Pass(FNTXOrder.FHead.root); Result := true; finally FUnLock; end else Result := false; finally oSubIndex.Close; with FIndexes.Add as TVKNTXIndex do begin Temp := True; NTXFileName := SubNtxFile; Open; Active := true; TVKDBFNTX(FIndexes.Owner).First; end; end else Result := false; finally oSubIndex.Free; end; end; procedure TVKNTXIndex.Last; begin if InternalLast = grOk then TVKDBFNTX(FIndexes.Owner).RecNo := FCurrentRec; end; procedure TVKNTXIndex.Next; begin if InternalNext = grOk then TVKDBFNTX(FIndexes.Owner).RecNo := FCurrentRec; end; procedure TVKNTXIndex.Prior; begin if InternalPrior = grOk then TVKDBFNTX(FIndexes.Owner).RecNo := FCurrentRec; end; function TVKNTXIndex.GetRecordByIndex(GetMode: TGetMode; var cRec: Integer): TGetResult; begin Result := grOk; case GetMode of gmNext: begin if cRec <> - 1 then Result := InternalNext else Result := InternalFirst; end; gmPrior: begin if cRec <> TVKDBFNTX(FIndexes.Owner).RecordCount then Result := InternalPrior else Result := InternalLast; end; end; if Result = grOk then cRec := FCurrentRec; if Result = grBOF then cRec := -1; if Result = grEOF then cRec := TVKDBFNTX(FIndexes.Owner).RecordCount; if Result = grError then cRec := TVKDBFNTX(FIndexes.Owner).RecordCount; end; function TVKNTXIndex.GetFirstByIndex(var cRec: Integer): TGetResult; begin Result := InternalFirst; cRec := FCurrentRec; end; function TVKNTXIndex.GetLastByIndex(var cRec: Integer): TGetResult; begin Result := InternalLast; cRec := FCurrentRec; end; function TVKNTXIndex.SetToRecord: boolean; var TmpKey: String; begin Result := true; FCurrentKey := EvaluteKeyExpr; FCurrentRec := TVKDBFNTX(FIndexes.Owner).RecNo; if Unique or FForExists then begin SeekFirstInternal(FCurrentKey, true); if FSeekOk then begin TmpKey := TransKey(FSeekKey); if (FCurrentKey <> TmpKey) then begin FCurrentKey := TmpKey; FCurrentRec := FSeekRecord; end; end else Result := false; end; end; function TVKNTXIndex.NextBuffer(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): Longint; var lResult: Longint; Found: boolean; v: WORD; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; l: Integer; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(FCurrentKey)); if c <= 0 then begin //FCurrentKey <= item.key if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; // if Found then begin if NTXRange.Active then begin l := Length(NTXRange.HiKey); if l > 0 then begin c := CmpKeys(item.key, pChar(NTXRange.HiKey), l); if c < 0 then begin //NTXRange.HiKey < item.key Result := true; Exit; end; end; end; pLongint(pChar(FBufInd) + lResult * SizeOf(Longint))^ := item.rec_no; DBFHandler.Seek(data_offset + (item.rec_no - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read((FBuffer + lResult * FRecordSize)^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(item.rec_no, Pointer(FBuffer + lResult * FRecordSize), FRecordSize); Inc(lResult); end; // if lResult = FRecordsPerBuf then begin Result := true; Exit; end; if ( FCurrentRec = item.rec_no ) and ( c = 0 ) then Found := true; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; Result := false; end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; lResult := 0; Found := false; Pass(FNTXOrder.FHead.root); // if not Found then // beep; Result := lResult; end; function TVKNTXIndex.PriorBuffer(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf: Integer; FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): Longint; var lResult: Longint; bResult: boolean; Found: boolean; v: WORD; procedure Pass(page_off: DWORD); var k, i: Integer; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; label a1; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); k := page.count; if not Found then begin if page.count > 0 then begin for i := 0 to page.count - 1 do begin k := i - 1; item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(FCurrentKey)); if c <= 0 then begin //FCurrentKey <= item.key if ( FCurrentRec = item.rec_no ) and ( c = 0 ) then Found := true; if ( item.page <> 0 ) then begin Pass(item.page); if bResult then Exit; end; if Found then goto a1; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Pass(item.page); if bResult then Exit; k := page.count - 1; if Found then goto a1; end; end; // a1: if Found then begin while k >= 0 do begin item := pNTX_ITEM(pChar(page) + page.ref[k]); if k < page.count then begin if NTXRange.Active then begin c := CmpKeys(item.key, pChar(NTXRange.LoKey)); if c > 0 then begin //NTXRange.LoKey > item.key bResult := true; Exit; end; end; pLongint(pChar(FBufInd) + (FRecordsPerBuf - lResult - 1) * SizeOf(Longint))^ := item.rec_no; DBFHandler.Seek(data_offset + (item.rec_no - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read((FBuffer + (FRecordsPerBuf - lResult - 1) * FRecordSize)^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(item.rec_no, Pointer(FBuffer + (FRecordsPerBuf - lResult - 1) * FRecordSize), FRecordSize); Inc(lResult); if lResult = FRecordsPerBuf then begin bResult := true; Exit; end; end; if ( item.page <> 0 ) then begin Pass(item.page); if bResult then Exit; end; Dec(k); end; end; // end; begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; lResult := 0; bResult := false; Found := false; Pass(FNTXOrder.FHead.root); // if not Found then // beep; Result := lResult; end; function TVKNTXIndex.SetToRecord(Key: String; Rec: Integer): boolean; var TmpKey: String; begin Result := true; FCurrentKey := Key; FCurrentRec := Rec; if Unique or FForExists then begin SeekFirstInternal(FCurrentKey, true); if FSeekOk then begin TmpKey := TransKey(FSeekKey); if (FCurrentKey <> TmpKey) then begin FCurrentKey := TmpKey; FCurrentRec := FSeekRecord; end; end else Result := false; end; end; function TVKNTXIndex.GetCurrentKey: String; begin Result := FCurrentKey; end; function TVKNTXIndex.GetCurrentRec: DWORD; begin Result := FCurrentRec; end; function TVKNTXIndex.FillFirstBufRecords(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf, FRecordSize: Integer; FBufInd: pLongInt; data_offset: Word): longint; var lResult: longint; c, l: Integer; v: WORD; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; // pLongint(pChar(FBufInd) + lResult * SizeOf(Longint))^ := item.rec_no; DBFHandler.Seek(data_offset + (item.rec_no - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read((FBuffer + lResult * FRecordSize)^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(item.rec_no, Pointer(FBuffer + lResult * FRecordSize), FRecordSize); Inc(lResult); // if lResult = FRecordsPerBuf then begin Result := true; Exit; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Result := Pass(item.page) else Result := false; end; begin if not NTXRange.Active then begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; lResult := 0; Pass(FNTXOrder.FHead.root); Result := lResult; end else begin SeekFirstInternal(NTXRange.LoKey, true); if FSeekOk then begin l := Length(NTXRange.LoKey); c := CmpKeys2(pChar(NTXRange.LoKey), pChar(FSeekKey), l); if c >= 0 then begin l := Length(NTXRange.HiKey); c := CmpKeys2(pChar(NTXRange.HiKey), pChar(FSeekKey), l); if (l > 0) and (c <= 0) then begin FCurrentKey := TransKey(FSeekKey); FCurrentRec := FSeekRecord; pLongint(FBufInd)^ := FSeekRecord; DBFHandler.Seek(data_offset + (DWORD(FSeekRecord) - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read(FBuffer^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(FSeekRecord, Pointer(FBuffer), FRecordSize); Result := 1 + NextBuffer(DBFHandler, FBuffer + FRecordSize, FRecordsPerBuf - 1, FRecordSize, pLongint(pChar(FBufInd) + SizeOf(Longint)), data_offset); end else Result := 0; end else Result := 0; end else Result := 0; end; end; function TVKNTXIndex.FillLastBufRecords(DBFHandler: TProxyStream; FBuffer: pChar; FRecordsPerBuf, FRecordSize: Integer; FBufInd: pLongint; data_offset: Word): longint; var lResult: longint; c, l: Integer; v: WORD; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; if page.count > 0 then begin for i := page.count - 1 downto 0 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); // pLongint(pChar(FBufInd) + (FRecordsPerBuf - lResult - 1) * SizeOf(Longint))^ := item.rec_no; DBFHandler.Seek(data_offset + (item.rec_no - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read((FBuffer + (FRecordsPerBuf - lResult - 1) * FRecordSize)^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(item.rec_no, Pointer(FBuffer + (FRecordsPerBuf - lResult - 1) * FRecordSize), FRecordSize); Inc(lResult); // if lResult = FRecordsPerBuf then begin Result := true; Exit; end; if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; end; end; Result := false; end; begin if (not NTXRange.Active) or (NTXRange.LoKey = '') then begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; lResult := 0; Pass(FNTXOrder.FHead.root); Result := lResult; end else begin SeekLastInternal(NTXRange.HiKey, true); if FSeekOk then begin l := Length(NTXRange.LoKey); c := CmpKeys2(pChar(NTXRange.LoKey), pChar(FSeekKey), l); if c >= 0 then begin l := Length(NTXRange.HiKey); c := CmpKeys2(pChar(NTXRange.HiKey), pChar(FSeekKey), l); if (l > 0) and (c <= 0) then begin FCurrentKey := TransKey(FSeekKey); FCurrentRec := FSeekRecord; pLongint(pChar(FBufInd) + (FRecordsPerBuf - 1) * SizeOf(Longint))^ := FCurrentRec; DBFHandler.Seek(data_offset + (FCurrentRec - 1) * DWORD(FRecordSize), soFromBeginning); DBFHandler.Read((FBuffer + (FRecordsPerBuf - 1) * FRecordSize)^, FRecordSize); if TVKDBFNTX(OwnerTable).Crypt.Active then TVKDBFNTX(OwnerTable).Crypt.Decrypt(FCurrentRec, Pointer(FBuffer + (FRecordsPerBuf - 1) * FRecordSize), FRecordSize); Result := 1 + PriorBuffer(DBFHandler, FBuffer, FRecordsPerBuf - 1, FRecordSize, FBufInd, data_offset); end else Result := 0; end else Result := 0; end else Result := 0; end; end; function TVKNTXIndex.SetToRecord(Rec: Integer): boolean; var TmpKey: String; begin Result := true; FCurrentKey := EvaluteKeyExpr; FCurrentRec := Rec; if Unique or FForExists then begin SeekFirstInternal(FCurrentKey, true); if FSeekOk then begin TmpKey := TransKey(FSeekKey); if (FCurrentKey <> FSeekKey) then begin FCurrentKey := TmpKey; FCurrentRec := FSeekRecord; end; end else Result := false; end; end; (* function TVKNTXIndex.FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer; var i: Integer; l: boolean; Ok: boolean; oW: TVKDBFNTX; begin i := 0; oW := TVKDBFNTX(FIndexes.Owner); l := (( (oW.AccessMode.FLast and fmShareExclusive) = fmShareExclusive ) or ( (oW.AccessMode.FLast and fmShareDenyWrite) = fmShareDenyWrite )); repeat Result := SysUtils.FileWrite(Handle, Buffer, Count); Ok := (Result <> -1); if not Ok then begin if l then Ok := true else begin Wait(0.001, false); Inc(i); if i = oW.WaitBusyRes then Ok := true; end; end; until Ok; end; *) (* function TVKNTXIndex.FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer; var i: Integer; l: boolean; Ok: boolean; oW: TVKDBFNTX; begin i := 0; oW := TVKDBFNTX(FIndexes.Owner); l := (( (oW.AccessMode.FLast and fmShareExclusive) = fmShareExclusive ) or ( (oW.AccessMode.FLast and fmShareDenyWrite) = fmShareDenyWrite )); repeat Result := SysUtils.FileRead(Handle, Buffer, Count); Ok := (Result <> -1); if not Ok then begin if l then Ok := true else begin Wait(0.001, false); Inc(i); if i = oW.WaitBusyRes then Ok := true; end; end; until Ok; end; *) function TVKNTXIndex.CompareKeys(S1, S2: PChar; MaxLen: Cardinal): Integer; var i: Integer; T1: array [0..NTX_PAGE] of Char; T2: array [0..NTX_PAGE] of Char; begin //S1 - CurrentKey //S2 - Item Key if Assigned(OnCompareKeys) then begin OnCompareKeys(self, S1, S2, MaxLen, Result); end else begin if FCl501Rus then begin Result := 0; CharToOem(pChar(S1), T1); CharToOem(pChar(S2), T2); for i := 0 to MaxLen - 1 do begin Result := CL501RUSORDER[Ord(T1[i])] - CL501RUSORDER[Ord(T2[i])]; if Result <> 0 then Exit; end; end else begin //Result := AnsiStrLComp(S1, S2, MaxLen); - in Win95-98 not currect Result := StrLComp(S1, S2, MaxLen); end; end; end; function TVKNTXIndex.GetFreePage: DWORD; var page: pNTX_BUFFER; i: Integer; Ind: TVKNTXBuffer; item_off: WORD; begin if FNTXOrder.FHead.next_page <> 0 then begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, FNTXOrder.FHead.next_page, page); page.count := 0; Result := FNTXOrder.FHead.next_page; FNTXOrder.FHead.next_page := pNTX_ITEM(pChar(page) + page.ref[page.count]).page; pNTX_ITEM(pChar(page) + page.ref[page.count]).page := 0; Ind.Fchanged := true; end else begin Result := FLastOffset; Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, Result, page, false); page.count := 0; item_off := ( FNTXOrder.FHead.max_item * 2 ) + 4; for i := 0 to FNTXOrder.FHead.max_item do begin page.ref[i] := item_off; item_off := item_off + FNTXOrder.FHead.item_size; end; pNTX_ITEM(pChar(page) + page.ref[0]).page := 0; Inc(FLastOffset, SizeOf(NTX_BUFFER)); Ind.Fchanged := true; end; end; function TVKNTXIndex.AddItem(ntxItem: pNTX_ITEM): boolean; var NewPage: pNTX_BUFFER; _NewPageOff, NewPageOff: DWORD; ItemHasBeenAdded: boolean; rf: WORD; Ind: TVKNTXBuffer; procedure AddItemInternal(page_off: DWORD); var i, j, beg, Mid: Integer; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; Ind, Ind1: TVKNTXBuffer; procedure InsItem(page: pNTX_BUFFER); begin j := page.count; while j >= i do begin rf := page.ref[j + 1]; page.ref[j + 1] := page.ref[j]; page.ref[j] := rf; Dec(j); end; page.count := page.count + 1; Move(ntxItem.key, pNTX_ITEM(pChar(page) + page.ref[i]).key, FNTXOrder.FHead.key_size); pNTX_ITEM(pChar(page) + page.ref[i]).rec_no := ntxItem.rec_no; pNTX_ITEM(pChar(page) + page.ref[i]).page := ntxItem.page; if ( ntxItem.page <> 0 ) then begin pNTX_ITEM(pChar(page) + page.ref[i + 1]).page := NewPageOff; NewPageOff := 0; end; end; procedure CmpRec; begin if c = 0 then begin if item.rec_no < ntxItem.rec_no then c := 1 else c := -1; end; end; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); i := page.count; if ( i > 0 ) then begin beg := 0; item := pNTX_ITEM(pChar(page) + page.ref[beg]); c := CmpKeys1(item.key, pChar(@ntxItem^.key[0])); CmpRec; if ( c > 0 ) then begin repeat Mid := (i+beg) div 2; item := pNTX_ITEM(pChar(page) + page.ref[Mid]); c := CmpKeys1(item.key, pChar(@ntxItem^.key[0])); CmpRec; if ( c > 0 ) then beg := Mid else i := Mid; until ( ((i-beg) div 2) = 0 ); end else i := beg; end; item := pNTX_ITEM(pChar(page) + page.ref[i]); if ( item.page <> 0 ) then AddItemInternal(item.page); if not ItemHasBeenAdded then begin if (page.count = FNTXOrder.FHead.max_item) then begin _NewPageOff := GetFreePage; Ind1 := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, _NewPageOff, NewPage); Move(page^, NewPage^, SizeOf(NTX_BUFFER)); page.count := FNTXOrder.FHead.half_page; for j := FNTXOrder.FHead.half_page to NewPage.count do begin rf := NewPage.ref[j - FNTXOrder.FHead.half_page]; NewPage.ref[j - FNTXOrder.FHead.half_page] := NewPage.ref[j]; NewPage.ref[j] := rf; end; NewPage.count := NewPage.count - FNTXOrder.FHead.half_page; if i < FNTXOrder.FHead.half_page then begin InsItem(page); end else begin i := i - FNTXOrder.FHead.half_page; InsItem(NewPage); end; NewPageOff := _NewPageOff; if page.count >= NewPage.count then begin page.count := page.count - 1; Move(pNTX_ITEM(pChar(page) + page.ref[page.count]).key, ntxItem.key, FNTXOrder.FHead.key_size); ntxItem.rec_no := pNTX_ITEM(pChar(page) + page.ref[page.count]).rec_no; end else begin Move(pNTX_ITEM(pChar(NewPage) + NewPage.ref[0]).key, ntxItem.key, FNTXOrder.FHead.key_size); ntxItem.rec_no := pNTX_ITEM(pChar(NewPage) + NewPage.ref[0]).rec_no; for j := 0 to NewPage.count do begin rf := NewPage.ref[j]; NewPage.ref[j] := NewPage.ref[j + 1]; NewPage.ref[j + 1] := rf; end; NewPage.count := NewPage.count - 1; end; ntxItem.page := page_off; Ind.Fchanged := true; Ind1.Fchanged := true; ItemHasBeenAdded := false; end else begin InsItem(page); Ind.Fchanged := true; ItemHasBeenAdded := true; end; end; end; begin NewPageOff := 0; ItemHasBeenAdded := false; AddItemInternal(FNTXOrder.FHead.root); if not ItemHasBeenAdded then begin _NewPageOff := GetFreePage; Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, _NewPageOff, NewPage); NewPage.count := 1; Move(ntxItem.key, pNTX_ITEM(pChar(NewPage) + NewPage.ref[0]).key, FNTXOrder.FHead.key_size); pNTX_ITEM(pChar(NewPage) + NewPage.ref[0]).rec_no := ntxItem.rec_no; pNTX_ITEM(pChar(NewPage) + NewPage.ref[0]).page := ntxItem.page; pNTX_ITEM(pChar(NewPage) + NewPage.ref[1]).page := NewPageOff; FNTXOrder.FHead.root := _NewPageOff; Ind.Fchanged := true; ItemHasBeenAdded := true; end; Result := ItemHasBeenAdded; end; procedure TVKNTXIndex.AddKey(sKey: String; nRec: Integer); var item: NTX_ITEM; AddOk: boolean; begin AddOk := true; if Unique then AddOk := AddOk and (not SeekFirstInternal(sKey)); if FForExists then AddOk := AddOk and (FForParser.Execute); if AddOk then begin item.page := 0; item.rec_no := nRec; Move(pChar(sKey)^, item.key, FNTXOrder.FHead.key_size); TransKey(item.key); AddItem(@item); end; end; procedure TVKNTXIndex.DeleteKey(sKey: String; nRec: Integer); var TempItem: NTX_ITEM; LastItem: NTX_ITEM; FLastKey: String; FLastRec: DWORD; rf: WORD; procedure AddInEndItem(page_off: DWORD; itemKey: pChar; itemRec: DWORD); var page: pNTX_BUFFER; NewPage: pNTX_BUFFER; item: pNTX_ITEM; NewPageOff: DWORD; i: DWORD; Ind, Ind1: TVKNTXBuffer; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then AddInEndItem(item.page, itemKey, itemRec) else begin if page.count < FNTXOrder.FHead.max_item then begin Move(itemKey^, pNTX_ITEM(pChar(page) + page.ref[page.count]).key, FNTXOrder.FHead.key_size); pNTX_ITEM(pChar(page) + page.ref[page.count]).rec_no := itemRec; pNTX_ITEM(pChar(page) + page.ref[page.count]).page := 0; page.count := page.count + 1; pNTX_ITEM(pChar(page) + page.ref[page.count]).page := 0; Ind.Fchanged := true; end else begin NewPageOff := GetFreePage; Ind1 := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, NewPageOff, NewPage); Move(page^, NewPage^, SizeOf(NTX_BUFFER)); page.count := FNTXOrder.FHead.half_page; pNTX_ITEM(pChar(page) + page.ref[FNTXOrder.FHead.half_page]).page := NewPageOff; Ind.Fchanged := true; for i := FNTXOrder.FHead.half_page to NewPage.count do begin rf := NewPage.ref[i - FNTXOrder.FHead.half_page]; NewPage.ref[i - FNTXOrder.FHead.half_page] := NewPage.ref[i]; NewPage.ref[i] := rf; end; NewPage.count := NewPage.count - FNTXOrder.FHead.half_page; Move(itemKey^, pNTX_ITEM(pChar(NewPage) + NewPage.ref[NewPage.count]).key, FNTXOrder.FHead.key_size); pNTX_ITEM(pChar(NewPage) + NewPage.ref[NewPage.count]).rec_no := itemRec; pNTX_ITEM(pChar(NewPage) + NewPage.ref[NewPage.count]).page := 0; NewPage.count := NewPage.count + 1; Ind1.Fchanged := true; end; end; end; procedure DeletePage(page_off: DWORD); var page: pNTX_BUFFER; Ind: TVKNTXBuffer; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); page.count := 0; pNTX_ITEM(pChar(page) + page.ref[0]).page := FNTXOrder.FHead.next_page; Ind.Fchanged := true; FNTXOrder.FHead.next_page := page_off; end; procedure GetLastItemOld(page_off: DWORD; PrePage: pNTX_BUFFER; PrePageOffset: DWORD; PreItemRef: WORD); var page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; Ind: TVKNTXBuffer; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then GetLastItemOld(item.page, page, page_off, page.count) else begin // item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(FLastKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(FLastKey, item.key, FNTXOrder.FHead.key_size); FLastRec := item.rec_no; // page.count := page.count - 1; Ind.Fchanged := true; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( page.count = 0 ) and ( item.page = 0 ) then begin DeletePage(page_off); pNTX_ITEM(pChar(PrePage) + NTX_BUFFER(PrePage^).ref[PreItemRef])^.page := 0; FNTXBuffers.SetPage(FNTXBag.NTXHandler, PrePageOffset, PrePage); end; end; function Pass(page_off: DWORD; LastItemRef: WORD; LastPage: pNTX_BUFFER; LastPageOffset: DWORD): boolean; var i, j: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; item1: pNTX_ITEM; c: Integer; Ind: TVKNTXBuffer; function DelPage: boolean; begin Result := false; if page.count = 0 then begin item1 := pNTX_ITEM(pChar(page) + page.ref[0]); if ( item1.page = 0 ) then begin if LastPage <> nil then begin pNTX_ITEM(pChar(LastPage) + NTX_BUFFER(LastPage^).ref[LastItemRef])^.page := 0; DeletePage(page_off); FNTXBuffers.SetPage(FNTXBag.NTXHandler, LastPageOffset, LastPage); Result := true; end; end; end; end; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(sKey)); if c <= 0 then begin //sKey <= item.key if ( item.page <> 0 ) then begin Result := Pass(item.page, i, page, page_off); DelPage; if Result then Exit; end; if ( DWORD(nRec) = item.rec_no ) and ( c = 0 ) then begin if ( item.page = 0 ) then begin j := i; while j < page.count do begin rf := page.ref[j]; page.ref[j] := page.ref[j + 1]; page.ref[j + 1] := rf; Inc(j); end; if page.count > 0 then begin page.count := page.count - 1; Ind.Fchanged := true; end; DelPage; Result := true; end else begin GetLastItemOld(item.page, page, page_off, i); Move(pChar(FLastKey)^, pNTX_ITEM(pChar(page) + page.ref[i]).key, FNTXOrder.FHead.key_size); pNTX_ITEM(pChar(page) + page.ref[i]).rec_no := FLastRec; Ind.Fchanged := true; Result := true; end; Exit; end; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := Pass(item.page, page.count, page, page_off); DelPage; if Result then Exit; end; Result := false; end; procedure GetLastItem(page_off: DWORD); var page: pNTX_BUFFER; item: pNTX_ITEM; begin FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then GetLastItem(item.page) else begin item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); Move(item^, LastItem, FNTXOrder.FHead.item_size); end; end; function PassForDel(page_off: DWORD; ItemForDelete: pNTX_ITEM; Parent: TVKNTXBuffer; ParentItemRef: WORD): boolean; var i, j: DWORD; item: pNTX_ITEM; page: pNTX_BUFFER; Ind: TVKNTXBuffer; c: Integer; procedure DelItemi; var rf: WORD; begin j := i; while j < page.count do begin rf := page.ref[j]; page.ref[j] := page.ref[j + 1]; page.ref[j + 1] := rf; Inc(j); end; page.count := page.count - 1; end; procedure NormalizePage(CurrPage, Parent: TVKNTXBuffer; ParentItemRef: WORD); var LeftSibling, RightSibling: TVKNTXBuffer; LeftPage, RightPage: pNTX_BUFFER; TryRight: boolean; SLItem, LItem, CItem, RItem, Item, SRItem: pNTX_ITEM; Shift, j: Integer; rf: WORD; LstPage: DWORD; begin if Parent <> nil then begin if CurrPage.Fpage.count < FNTXOrder.FHead.half_page then begin TryRight := false; if ParentItemRef > 0 then begin LItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[ParentItemRef - 1]); if LItem.page <> 0 then begin LeftSibling := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, LItem.page, LeftPage); if LeftPage.count > FNTXOrder.FHead.half_page then begin SLItem := pNTX_ITEM( pChar(LeftPage) + LeftPage.ref[LeftPage.count]); rf := LeftPage.count - FNTXOrder.FHead.half_page; Shift := (rf div 2) + (rf mod 2); LeftPage.count := LeftPage.count - Shift; j := CurrPage.Fpage.count; while j >= 0 do begin rf := CurrPage.Fpage.ref[j + Shift]; CurrPage.Fpage.ref[j + Shift] := CurrPage.Fpage.ref[j]; CurrPage.Fpage.ref[j] := rf; Dec(j); end; Inc(CurrPage.Fpage.count, Shift); CItem := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[Shift - 1]); Move(LItem.key, CItem.key, FNTXOrder.FHead.key_size); CItem.rec_no := LItem.rec_no; CItem.page := SLItem.page; Dec(Shift); while Shift > 0 do begin SLItem := pNTX_ITEM( pChar(LeftPage) + LeftPage.ref[LeftPage.count + Shift]); CItem := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[Shift - 1]); Move(SLItem.key, CItem.key, FNTXOrder.FHead.key_size); CItem.rec_no := SLItem.rec_no; CItem.page := SLItem.page; Dec(Shift); end; SLItem := pNTX_ITEM( pChar(LeftPage) + LeftPage.ref[LeftPage.count]); Move(SLItem.key, LItem.key, FNTXOrder.FHead.key_size); LItem.rec_no := SLItem.rec_no; end else begin CItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[ParentItemRef]); Item := pNTX_ITEM( pChar(LeftPage) + LeftPage.ref[LeftPage.count]); Move(LItem.key, Item.key, FNTXOrder.FHead.key_size); Item.rec_no := LItem.rec_no; Inc(LeftPage.count); CItem.page := LItem.page; for j := ParentItemRef - 1 to Parent.Fpage.count - 1 do begin rf := Parent.Fpage.ref[j]; Parent.Fpage.ref[j] := Parent.Fpage.ref[j + 1]; Parent.Fpage.ref[j + 1] := rf; end; Dec(Parent.Fpage.count); for j := 0 to CurrPage.Fpage.count do begin CItem := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[j]); Item := pNTX_ITEM( pChar(LeftPage) + LeftPage.ref[LeftPage.count]); Move(CItem^, Item^, FNTXOrder.FHead.item_size); Inc(LeftPage.count); end; Dec(LeftPage.count); //Delete page CurrPage.Fpage.count := 0; CItem := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[0]); CItem.page := FNTXOrder.FHead.next_page; FNTXOrder.FHead.next_page := CurrPage.Fpage_offset; if Parent.Fpage.count = 0 then begin //Delete Parent CItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[0]); FNTXOrder.FHead.root := CItem.page; CItem.page := FNTXOrder.FHead.next_page; FNTXOrder.FHead.next_page := Parent.Fpage_offset; end; end; LeftSibling.Fchanged := true; CurrPage.Fchanged := true; Parent.Fchanged := true; end else TryRight := true; end else TryRight := true; if TryRight then begin if ParentItemRef < Parent.Fpage.count then begin RItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[ParentItemRef + 1]); if RItem.page <> 0 then begin RightSibling := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, RItem.page, RightPage); if RightPage.count > FNTXOrder.FHead.half_page then begin rf := RightPage.count - FNTXOrder.FHead.half_page; Shift := (rf div 2) + (rf mod 2); CItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[ParentItemRef]); Item := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[CurrPage.Fpage.count]); Move(CItem.key, Item.key, FNTXOrder.FHead.key_size); Item.rec_no := CItem.rec_no; Inc(CurrPage.Fpage.count); Item := pNTX_ITEM( pChar(@RightSibling.Fpage) + RightSibling.Fpage.ref[Shift - 1]); Move(Item.key, CItem.key, FNTXOrder.FHead.key_size); CItem.rec_no := Item.rec_no; LstPage := Item.page; for j := 0 to Shift - 2 do begin SRItem := pNTX_ITEM( pChar(@RightSibling.Fpage) + RightSibling.Fpage.ref[j]); Item := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[CurrPage.Fpage.count]); Move(SRItem^, Item^, FNTXOrder.FHead.item_size); Inc(CurrPage.Fpage.count); end; Item := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[CurrPage.Fpage.count]); Item.page := LstPage; Dec(RightSibling.Fpage.count, Shift); for j := 0 to RightSibling.Fpage.count do begin rf := RightSibling.Fpage.ref[j]; RightSibling.Fpage.ref[j] := RightSibling.Fpage.ref[j + Shift]; RightSibling.Fpage.ref[j + Shift] := rf; end; end else begin CItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[ParentItemRef]); Item := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[CurrPage.Fpage.count]); Move(CItem.key, Item.key, FNTXOrder.FHead.key_size); Item.rec_no := CItem.rec_no; Inc(CurrPage.Fpage.count); RItem.page := CItem.page; for j := ParentItemRef to Parent.Fpage.count - 1 do begin rf := Parent.Fpage.ref[j]; Parent.Fpage.ref[j] := Parent.Fpage.ref[j + 1]; Parent.Fpage.ref[j + 1] := rf; end; Dec(Parent.Fpage.count); for j := 0 to RightSibling.Fpage.count do begin SRItem := pNTX_ITEM( pChar(@RightSibling.Fpage) + RightSibling.Fpage.ref[j]); Item := pNTX_ITEM( pChar(@CurrPage.Fpage) + CurrPage.Fpage.ref[CurrPage.Fpage.count]); Move(SRItem^, Item^, FNTXOrder.FHead.item_size); Inc(CurrPage.Fpage.count); end; Dec(CurrPage.Fpage.count); //Delete page RightSibling.Fpage.count := 0; CItem := pNTX_ITEM( pChar(@RightSibling.Fpage) + RightSibling.Fpage.ref[0]); CItem.page := FNTXOrder.FHead.next_page; FNTXOrder.FHead.next_page := RightSibling.Fpage_offset; if Parent.Fpage.count = 0 then begin //Delete Parent CItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[0]); FNTXOrder.FHead.root := CItem.page; CItem.page := FNTXOrder.FHead.next_page; FNTXOrder.FHead.next_page := Parent.Fpage_offset; end; end; RightSibling.Fchanged := true; CurrPage.Fchanged := true; Parent.Fchanged := true; end; end; end; end; end; end; begin Ind := FNTXBuffers.GetNTXBuffer(FNTXBag.NTXHandler, page_off, page); for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys1(item.key, ItemForDelete.key); if c <= 0 then begin //ItemForDelete.key <= item.key if ( item.page <> 0 ) then begin Result := PassForDel(item.page, ItemForDelete, Ind, i); NormalizePage(Ind, Parent, ParentItemRef); if Result then Exit; end; if ( ItemForDelete.rec_no = item.rec_no ) and ( c = 0 ) then begin if ( item.page = 0 ) then begin DelItemi; Ind.Fchanged := true; end else begin GetLastItem(item.page); Move(LastItem.key, item.key, FNTXOrder.FHead.key_size); item.rec_no := LastItem.rec_no; Ind.Fchanged := true; PassForDel(item.page, @LastItem, Ind, i); end; NormalizePage(Ind, Parent, ParentItemRef); Result := true; Exit; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := PassForDel(item.page, ItemForDelete, Ind, page.count); NormalizePage(Ind, Parent, ParentItemRef); if Result then Exit; end; Result := false; end; begin if FDeleteKeyStyle = dksClipper then begin TempItem.page := 0; TempItem.rec_no := nRec; Move(pChar(sKey)^, TempItem.key, FNTXOrder.FHead.key_size); TransKey(TempItem.key); PassForDel(FNTXOrder.FHead.root, @TempItem, nil, 0); end else Pass(FNTXOrder.FHead.root, 0, nil, 0); end; function TVKNTXIndex.LastKey(out LastKey: String; out LastRec: Integer): boolean; var level: Integer; function Pass(page_off: DWORD): TGetResult; var page: pNTX_BUFFER; item: pNTX_ITEM; Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; begin Inc(level); try FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result = grOK then Exit; end; if page.count <> 0 then begin // item := pNTX_ITEM(pChar(page) + page.ref[page.count - 1]); if FKeyTranslate then begin Move(item.key, Srckey, FNTXOrder.FHead.key_size); Srckey[FNTXOrder.FHead.key_size] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); SetString(LastKey, Destkey, FNTXOrder.FHead.key_size); end else SetString(LastKey, item.key, FNTXOrder.FHead.key_size); LastRec := item.rec_no; // Result := grOK; end else if level = 1 then Result := grBOF else Result := grError; Exit; finally Dec(level); end; end; begin if FLock then try ClearIfChange; level := 0; Result := (Pass(FNTXOrder.FHead.root) = grOK); finally FUnLock; end else Result := false; end; function TVKNTXIndex.FLock: boolean; var i: Integer; l: boolean; oW: TVKDBFNTX; begin if not FFileLock then begin i := 0; oW := TVKDBFNTX(FIndexes.Owner); l := ( ( (oW.AccessMode.FLast and fmShareExclusive) = fmShareExclusive ) or ( (oW.AccessMode.FLast and fmShareDenyWrite) = fmShareDenyWrite ) or FFileLock ); repeat if not l then begin Result := FNTXBag.NTXHandler.Lock(1000000000, 1); if not Result then begin Wait(0.001, false); Inc(i); if i = oW.WaitBusyRes then Exit; end; end else Result := true; until Result; FFileLock := Result; end else Result := true; end; function TVKNTXIndex.FUnLock: boolean; var l: boolean; oW: TVKDBFNTX; begin oW := TVKDBFNTX(FIndexes.Owner); l := ( ( (oW.AccessMode.FLast and fmShareExclusive) = fmShareExclusive ) or ( (oW.AccessMode.FLast and fmShareDenyWrite) = fmShareDenyWrite ) ); if not l then Result := FNTXBag.NTXHandler.UnLock(1000000000, 1) else Result := true; FFileLock := not Result; end; procedure TVKNTXIndex.SetUnique(const Value: boolean); begin if IsOpen then begin if Value then FNTXOrder.FHead.unique := #1 else FNTXOrder.FHead.unique := #0; end else FUnique := Value; end; function TVKNTXIndex.GetUnique: boolean; begin if IsOpen then Result := (FNTXOrder.FHead.unique <> #0) else Result := FUnique; end; procedure TVKNTXIndex.SetDesc(const Value: boolean); begin if IsOpen then begin if Value then FNTXOrder.FHead.Desc := #1 else FNTXOrder.FHead.Desc := #0; end else FDesc := Value; end; function TVKNTXIndex.GetDesc: boolean; begin if IsOpen then Result := (FNTXOrder.FHead.Desc <> #0) else Result := FDesc; end; function TVKNTXIndex.GetOrder: String; var i: Integer; p: pChar; begin if IsOpen then begin for i := 0 to 7 do if FNTXOrder.FHead.order[i] = #0 then break; p := pChar(@FNTXOrder.FHead.order[0]); SetString(Result, p, i); ChekExpression(Result); if Result = '' then Result := Name; end else Result := FOrder; end; procedure TVKNTXIndex.SetOrder(Value: String); var i, j: Integer; begin if IsOpen then begin ChekExpression(Value); j := Length(Value); if j > 8 then j := 8; for i := 0 to j - 1 do FNTXOrder.FHead.order[i] := Value[i + 1]; FNTXOrder.FHead.order[j] := #0; Name := FNTXOrder.FHead.order; end else FOrder := Value; end; procedure TVKNTXIndex.ChekExpression(var Value: String); var i, j: Integer; begin j := Length(Value); for i := 1 to j do if Value[i] < #32 then begin Value := ''; Exit; end; end; function TVKNTXIndex.CmpKeys1(ItemKey, CurrKey: pChar; KSize: Integer): Integer; var Srckey: array[0..NTX_PAGE-1] of Char; Destkey1, Destkey2: array[0..NTX_PAGE-1] of Char; begin if KSize = 0 then KSize := FNTXOrder.FHead.key_size; if FKeyTranslate then begin Move(ItemKey^, Srckey, KSize); Srckey[KSize] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey1, false); Move(CurrKey^, Srckey, KSize); Srckey[KSize] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey2, false); Result := CompareKeys(Destkey2, Destkey1, KSize); end else Result := CompareKeys(CurrKey, ItemKey, KSize); if Desc then Result := - Result; end; function TVKNTXIndex.CmpKeys2(ItemKey, CurrKey: pChar; KSize: Integer): Integer; var Srckey: array[0..NTX_PAGE-1] of Char; Destkey1, Destkey2: array[0..NTX_PAGE-1] of Char; begin if KSize = 0 then KSize := FNTXOrder.FHead.key_size; if FKeyTranslate then begin Move(ItemKey^, Destkey1, KSize); //Move(ItemKey^, Srckey, KSize); //Srckey[KSize] := #0; //TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey1, false); Move(CurrKey^, Srckey, KSize); Srckey[KSize] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey2, false); Result := CompareKeys(Destkey2, Destkey1, KSize); end else Result := CompareKeys(CurrKey, ItemKey, KSize); if Desc then Result := - Result; end; function TVKNTXIndex.CmpKeys(ItemKey, CurrKey: pChar; KSize: Integer = 0): Integer; var Srckey: array[0..NTX_PAGE-1] of Char; Destkey: array[0..NTX_PAGE-1] of Char; begin if KSize = 0 then KSize := FNTXOrder.FHead.key_size; if FKeyTranslate then begin Move(ItemKey^, Srckey, KSize); Srckey[KSize] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Destkey, false); Result := CompareKeys(CurrKey, Destkey, KSize); end else Result := CompareKeys(CurrKey, ItemKey, KSize); if Desc then Result := - Result; end; function TVKNTXIndex.CmpKeys3(ItemKey, CurrKey: pChar; KSize: Integer): Integer; begin if KSize = 0 then KSize := FNTXOrder.FHead.key_size; Result := CompareKeys(CurrKey, ItemKey, KSize); if Desc then Result := - Result; end; procedure TVKNTXIndex.TransKey(Key: pChar; KSize: Integer = 0; ToOem: Boolean = true); var Srckey: array[0..NTX_PAGE-1] of Char; begin if KSize = 0 then KSize := FNTXOrder.FHead.key_size; if FKeyTranslate then begin Move(Key^, Srckey, KSize); Srckey[KSize] := #0; TVKDBFNTX(FIndexes.Owner).Translate(Srckey, Key, ToOem); end; end; procedure TVKNTXIndex.CreateIndex(Activate: boolean = true); var oB: TVKDBFNTX; DBFBuffer: pChar; RecPareBuf: Integer; ReadSize, RealRead, BufCnt: Integer; i: Integer; Key: String; Rec: DWORD; LastFUpdated: boolean; procedure CreateEmptyIndex; var IndAttr: TIndexAttributes; begin if not FReindex then begin DefineBag; FNTXBag.NTXHandler.CreateProxyStream; if not FNTXBag.NTXHandler.IsOpen then begin raise Exception.Create('TVKNTXIndex.CreateIndex: Create error "' + Name + '"'); end else begin FNTXBuffers.Clear; if FClipperVer in [v500, v501] then FNTXOrder.FHead.sign := 6 else FNTXOrder.FHead.sign := 7; FNTXOrder.FHead.version := 1; FNTXOrder.FHead.root := NTX_PAGE; FNTXOrder.FHead.next_page := 0; if Assigned(OnCreateIndex) then begin OnCreateIndex(self, IndAttr); FNTXOrder.FHead.key_size := IndAttr.key_size; FNTXOrder.FHead.key_dec := IndAttr.key_dec; System.Move(pChar(IndAttr.key_expr)^, FNTXOrder.FHead.key_expr, Length(IndAttr.key_expr)); FNTXOrder.FHead.key_expr[Length(IndAttr.key_expr)] := #0; System.Move(pChar(IndAttr.for_expr)^, FNTXOrder.FHead.for_expr, Length(IndAttr.for_expr)); FNTXOrder.FHead.for_expr[Length(IndAttr.for_expr)] := #0; end else begin FNTXOrder.FHead.key_size := Length(FKeyParser.Key); FNTXOrder.FHead.key_dec := FKeyParser.Prec; System.Move(pChar(FKeyExpresion)^, FNTXOrder.FHead.key_expr, Length(FKeyExpresion)); FNTXOrder.FHead.key_expr[Length(FKeyExpresion)] := #0; System.Move(pChar(FForExpresion)^, FNTXOrder.FHead.for_expr, Length(FForExpresion)); FNTXOrder.FHead.for_expr[Length(FForExpresion)] := #0; end; FNTXOrder.FHead.item_size := FNTXOrder.FHead.key_size + 8; FNTXOrder.FHead.max_item := (NTX_PAGE - FNTXOrder.FHead.item_size - 4) div (FNTXOrder.FHead.item_size + 2); FNTXOrder.FHead.half_page := FNTXOrder.FHead.max_item div 2; FNTXOrder.FHead.max_item := FNTXOrder.FHead.half_page * 2; FNTXOrder.FHead.reserv1 := #0; FNTXOrder.FHead.reserv3 := #0; Order := FOrder; Desc := FDesc; Unique := FUnique; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, SizeOf(NTX_HEADER)); FLastOffset := SizeOf(NTX_HEADER); GetFreePage; end; end else begin //Truncate ntx file FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.SetEndOfFile; FNTXBuffers.Clear; if FClipperVer in [v500, v501] then FNTXOrder.FHead.sign := 6 else FNTXOrder.FHead.sign := 7; FNTXOrder.FHead.version := 1; FNTXOrder.FHead.root := NTX_PAGE; FNTXOrder.FHead.next_page := 0; FNTXOrder.FHead.reserv1 := #0; FNTXOrder.FHead.reserv3 := #0; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, SizeOf(NTX_HEADER)); FLastOffset := SizeOf(NTX_HEADER); GetFreePage; end; end; begin oB := TVKDBFNTX(FIndexes.Owner); if oB.Active then begin oB.IndState := true; FCreateIndexProc:= true; DBFBuffer := VKDBFMemMgr.oMem.GetMem(self, oB.BufferSize); LastFUpdated := FUpdated; FUpdated := true; try FillChar(DBFBuffer^, oB.BufferSize, ' '); oB.IndRecBuf := DBFBuffer; if FForExpresion <> '' then FForExists := true; EvaluteKeyExpr; CreateEmptyIndex; RecPareBuf := oB.BufferSize div oB.Header.rec_size; if RecPareBuf >= 1 then begin ReadSize := RecPareBuf * oB.Header.rec_size; oB.Handle.Seek(oB.Header.data_offset, 0); Rec := 0; repeat RealRead := oB.Handle.Read(DBFBuffer^, ReadSize); BufCnt := RealRead div oB.Header.rec_size; for i := 0 to BufCnt - 1 do begin oB.IndRecBuf := DBFBuffer + oB.Header.rec_size * i; if oB.Crypt.Active then oB.Crypt.Decrypt(Rec + 1, Pointer(oB.IndRecBuf), oB.Header.rec_size); Inc(Rec); Key := EvaluteKeyExpr; AddKey(Key, Rec); end; until ( BufCnt <= 0 ); end else Exception.Create('TVKNTXIndex.CreateIndex: Record size too lage'); finally Flush; FUpdated := LastFUpdated; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, SizeOf(NTX_HEADER)); FNTXBuffers.Clear; FCreateIndexProc:= false; oB.IndState := false; oB.IndRecBuf := nil; VKDBFMemMgr.oMem.FreeMem(DBFBuffer); end; if IsOpen then begin InternalFirst; KeyExpresion := FNTXOrder.FHead.key_expr; ForExpresion := FNTXOrder.FHead.for_expr; if ForExpresion <> '' then FForExists := true; if Activate then Active := true; end; end else raise Exception.Create('TVKNTXIndex.CreateIndex: Create index only on active DataSet'); end; procedure TVKNTXIndex.CreateCompactIndex(BlockBufferSize: LongWord = 65536; Activate: boolean = true); var oB: TVKDBFNTX; DBFBuffer: pChar; RecPareBuf: Integer; ReadSize, RealRead, BufCnt: Integer; i: Integer; Key: String; Rec: DWORD; BlockBuffer: pChar; FNtxHead: NTX_HEADER; max_item: WORD; Objects: TObjectList; Iter1, Iter2: TVKNTXIndexIterator; cIndex: TVKNTXCompactIndex; procedure LoadBlock(BlockFile: String; pBlock: pChar); var h: Integer; begin h := FileOpen(BlockFile, fmOpenRead or fmShareExclusive); if h > 0 then begin SysUtils.FileRead(h, pBlock^, BlockBufferSize); SysUtils.FileClose(h); end; end; procedure SaveBlock; var TmpFileName: String; h: Integer; begin if pBLOCK_BUFFER(BlockBuffer).count > 0 then begin TmpFileName := GetTmpFileName; h := FileOpen(TmpFileName, fmOpenWrite or fmShareExclusive); if h > 0 then begin SysUtils.FileWrite(h, BlockBuffer^, BlockBufferSize); SysUtils.FileClose(h); Objects.Add(TVKNTXBlockIterator.Create(TmpFileName, FNtxHead.key_size, BlockBufferSize)); end; end; end; procedure FillNtxHeader; var i: Integer; IndAttr: TIndexAttributes; begin DefineBag; if FClipperVer in [v500, v501] then FNtxHead.sign := 6 else FNtxHead.sign := 7; FNtxHead.version := 0; FNtxHead.root := NTX_PAGE; FNtxHead.next_page := 0; if Assigned(OnCreateIndex) then begin OnCreateIndex(self, IndAttr); FNtxHead.key_size := IndAttr.key_size; FNtxHead.key_dec := IndAttr.key_dec; System.Move(pChar(IndAttr.key_expr)^, FNtxHead.key_expr, Length(IndAttr.key_expr)); FNtxHead.key_expr[Length(IndAttr.key_expr)] := #0; System.Move(pChar(IndAttr.for_expr)^, FNtxHead.for_expr, Length(IndAttr.for_expr)); FNtxHead.for_expr[Length(IndAttr.for_expr)] := #0; end else begin FNtxHead.key_size := Length(FKeyParser.Key); FNtxHead.key_dec := FKeyParser.Prec; System.Move(pChar(FKeyExpresion)^, FNtxHead.key_expr, Length(FKeyExpresion)); FNtxHead.key_expr[Length(FKeyExpresion)] := #0; System.Move(pChar(FForExpresion)^, FNtxHead.for_expr, Length(FForExpresion)); FNtxHead.for_expr[Length(FForExpresion)] := #0; end; FNtxHead.item_size := FNtxHead.key_size + 8; FNtxHead.max_item := (NTX_PAGE - FNtxHead.item_size - 4) div (FNtxHead.item_size + 2); FNtxHead.half_page := FNtxHead.max_item div 2; FNtxHead.max_item := FNtxHead.half_page * 2; if Unique then FNtxHead.unique := #1 else FNtxHead.unique := #0; FNtxHead.reserv1 := #0; if Desc then FNtxHead.desc := #1 else FNtxHead.desc := #0; FNtxHead.reserv3 := #0; for i := 0 to 7 do FNtxHead.order[i] := FNTXOrder.FHead.Order[i]; // FNTXOrder.FHead := FNtxHead; // end; procedure InitBlock(Block: pChar); var page: pBLOCK_BUFFER; half_page, item_size, item_off: WORD; i: Integer; q: LongWord; begin item_size := FNtxHead.key_size + 4; q := (BlockBufferSize - item_size - 4) div (item_size + 2); if q > MAXWORD then raise Exception.Create('TVKNTXIndex.CreateCompactIndex: BlockBufferSize too large!'); max_item := WORD(q); half_page := max_item div 2; max_item := half_page * 2; page := pBLOCK_BUFFER(Block); page.count := 0; item_off := ( max_item * 2 ) + 4; for i := 0 to max_item do begin page.ref[i] := item_off; item_off := item_off + item_size; end; end; procedure AddKeyInBlock(Key: String; Rec: DWORD); var AddOk: boolean; i, j, beg, Mid: Integer; page: pBLOCK_BUFFER; item: pBLOCK_ITEM; c: Integer; rf: WORD; procedure InsItem; begin j := page.count; while j >= i do begin rf := page.ref[j + 1]; page.ref[j + 1] := page.ref[j]; page.ref[j] := rf; Dec(j); end; page.count := page.count + 1; Move(pChar(Key)^, pBLOCK_ITEM(pChar(page) + page.ref[i]).key, FNTXOrder.FHead.key_size); pBLOCK_ITEM(pChar(page) + page.ref[i]).rec_no := Rec; end; procedure CmpRec; begin if c = 0 then begin if item.rec_no < Rec then c := 1 else c := -1; end; end; begin AddOk := true; if FForExists then AddOk := AddOk and (FForParser.Execute); if AddOk then begin page := pBLOCK_BUFFER(BlockBuffer); if page.count = max_item then begin //Save block on disc SaveBlock; //Truncate block page.count := 0; end; TransKey(pChar(Key)); i := page.count; if ( i > 0 ) then begin beg := 0; item := pBLOCK_ITEM(pChar(page) + page.ref[beg]); c := CmpKeys1(item.key, pChar(Key)); if ( c = 0 ) and Unique then Exit; CmpRec; if ( c > 0 ) then begin repeat Mid := (i+beg) div 2; item := pBLOCK_ITEM(pChar(page) + page.ref[Mid]); c := CmpKeys1(item.key, pChar(Key)); if ( c = 0 ) and Unique then Exit; CmpRec; if ( c > 0 ) then beg := Mid else i := Mid; until ( ((i-beg) div 2) = 0 ); end else i := beg; end; if AddOk then InsItem; end; end; procedure MergeList(Iter1, Iter2: TVKNTXIndexIterator; cIndex: TVKNTXCompactIndex); var c: Integer; procedure CmpRec; begin if c = 0 then begin if Iter1.item.rec_no < Iter2.item.rec_no then c := 1 else c := -1; end; end; begin if Iter2 = nil then begin Iter1.Open; try while not Iter1.Eof do begin cIndex.AddItem(@Iter1.item); Iter1.Next; end; finally Iter1.Close; end; end else begin Iter1.Open; Iter2.Open; try repeat if not ( Iter1.Eof or Iter2.Eof ) then begin c := CmpKeys1(Iter1.Item.key, Iter2.Item.key); if ( c = 0 ) and Unique then begin cIndex.AddItem(@Iter1.Item); Iter1.Next; Iter2.Next; Continue; end; CmpRec; if c > 0 then begin if not Iter1.Eof then begin cIndex.AddItem(@Iter1.Item); Iter1.Next; end; end else if not Iter2.Eof then begin cIndex.AddItem(@Iter2.Item); Iter2.Next; end; end else begin if not Iter1.Eof then begin cIndex.AddItem(@Iter1.Item); Iter1.Next; end; if not Iter2.Eof then begin cIndex.AddItem(@Iter2.Item); Iter2.Next; end; end; until ( Iter1.Eof and Iter2.Eof ); finally Iter1.Close; Iter2.Close; end; end; end; begin oB := TVKDBFNTX(FIndexes.Owner); if oB.Active then begin oB.IndState := true; Objects := TObjectList.Create; cIndex := TVKNTXCompactIndex.Create; DBFBuffer := VKDBFMemMgr.oMem.GetMem(self, oB.BufferSize); BlockBuffer := VKDBFMemMgr.oMem.GetMem(self, BlockBufferSize); try FillChar(DBFBuffer^, oB.BufferSize, ' '); oB.IndRecBuf := DBFBuffer; if FForExpresion <> '' then FForExists := true; EvaluteKeyExpr; FillNtxHeader; InitBlock(BlockBuffer); RecPareBuf := oB.BufferSize div oB.Header.rec_size; if RecPareBuf >= 1 then begin ReadSize := RecPareBuf * oB.Header.rec_size; oB.Handle.Seek(oB.Header.data_offset, 0); Rec := 0; repeat RealRead := oB.Handle.Read(DBFBuffer^, ReadSize); BufCnt := RealRead div oB.Header.rec_size; for i := 0 to BufCnt - 1 do begin oB.IndRecBuf := DBFBuffer + oB.Header.rec_size * i; if oB.Crypt.Active then oB.Crypt.Decrypt(Rec + 1, Pointer(oB.IndRecBuf), oB.Header.rec_size); Inc(Rec); Key := EvaluteKeyExpr; // AddKeyInBlock(Key, Rec); // end; until ( BufCnt <= 0 ); //Save the rest block SaveBlock; if Objects.Count > 0 then begin // Merge lists i := 0; while i < Objects.Count do begin Iter1 := TVKNTXIndexIterator(Objects[i]); if ( i + 1 ) < Objects.Count then Iter2 := TVKNTXIndexIterator(Objects[i + 1]) else Iter2 := nil; if ( Objects.Count - i ) > 2 then cIndex.FileName := '' else begin cIndex.FileName := FNTXFileName; cIndex.Crypt := oB.Crypt.Active; cIndex.OwnerTable := oB; if FNTXBag.NTXHandler.ProxyStreamType <> pstFile then cIndex.Handler := FNTXBag.NTXHandler; end; cIndex.CreateEmptyIndex(FNtxHead); try MergeList(Iter1, Iter2, cIndex); finally cIndex.Close; if ( Objects.Count - i ) > 2 then Objects.Add(TVKNTXIterator.Create(cIndex.FileName)); end; Inc(i, 2); end; end else begin cIndex.FileName := FNTXFileName; cIndex.Crypt := oB.Crypt.Active; cIndex.OwnerTable := oB; if FNTXBag.NTXHandler.ProxyStreamType <> pstFile then cIndex.Handler := FNTXBag.NTXHandler; cIndex.CreateEmptyIndex(FNtxHead); cIndex.Close; end; // end else Exception.Create('TVKNTXIndex.CreateCompactIndex: Record size too lage'); finally oB.IndState := false; oB.IndRecBuf := nil; VKDBFMemMgr.oMem.FreeMem(DBFBuffer); VKDBFMemMgr.oMem.FreeMem(BlockBuffer); Objects.Free; cIndex.Free; end; Open; if IsOpen and Activate then Active := true; end else raise Exception.Create('TVKNTXIndex.CreateCompactIndex: Create index only on active DataSet'); end; function TVKNTXIndex.SuiteFieldList(fl: String; out m: Integer): Integer; begin if Temp then begin m := 0; Result := 0 end else Result := FKeyParser.SuiteFieldList(fl, m); end; function TVKNTXIndex.SeekFields(const KeyFields: string; const KeyValues: Variant; SoftSeek: boolean = false; PartialKey: boolean = false): Integer; var m, n: Integer; Key: String; begin Result := 0; m := FKeyParser.SuiteFieldList(KeyFields, n); if m > 0 then begin Key := FKeyParser.EvaluteKey(KeyFields, KeyValues); if PartialKey then Key := TrimRight(Key); Result := SeekFirstRecord(Key, SoftSeek, PartialKey); end; end; function TVKNTXIndex.GetOwnerTable: TDataSet; begin Result := TDataSet(FIndexes.Owner); end; function TVKNTXIndex.SeekLastInternal(Key: String; SoftSeek: boolean): boolean; var lResult, SoftSeekSet: boolean; procedure Pass(page_off: DWORD); var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(Key), Length(Key)); if c < 0 then begin //Key < item.key if ( item.page <> 0 ) then Pass(item.page); if (SoftSeek) and (not lResult) and ( not SoftSeekSet ) then begin FSeekRecord := item.rec_no; SoftSeekSet := true; SetString(FSeekKey, item.key, FNTXOrder.FHead.key_size); FSeekOk := true; end; Exit; end; if c = 0 then begin //Key = item.key FSeekRecord := item.rec_no; SetString(FSeekKey, item.key, FNTXOrder.FHead.key_size); FSeekOk := true; lResult := true; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); end; begin FSeekOk := false; SoftSeekSet := false; if FLock then try ClearIfChange; lResult := false; Pass(FNTXOrder.FHead.root); Result := lResult; finally FUnLock; end else Result := false; end; procedure TVKNTXIndex.SetRangeFields(FieldList: String; FieldValues: array of const); var FieldVal: Variant; begin ArrayOfConstant2Variant(FieldValues, FieldVal); SetRangeFields(FieldList, FieldVal); end; procedure TVKNTXIndex.SetRangeFields(FieldList: String; FieldValues: Variant); var Key: String; begin Key := TrimRight(FKeyParser.EvaluteKey(FieldList, FieldValues)); NTXRange.HiKey := Key; NTXRange.LoKey := Key; NTXRange.ReOpen; end; function TVKNTXIndex.GetIsRanged: boolean; begin Result := NTXRange.Active; end; function TVKNTXIndex.InRange(Key: String): boolean; begin Result := NTXRange.InRange(Key); end; procedure TVKNTXIndex.ClearIfChange; var v: WORD; begin if not FCreateIndexProc then begin if not FUpdated then begin v := FNTXOrder.FHead.version; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Read(FNTXOrder.FHead, 12); if v <> FNTXOrder.FHead.version then FNTXBuffers.Clear; end; end; end; procedure TVKNTXIndex.StartUpdate(UnLock: boolean = true); begin if not FUpdated then if FLock then try FLastOffset := FNTXBag.NTXHandler.Seek(0, 2); ClearIfChange; FUpdated := true; finally if UnLock then FUnLock; end; end; procedure TVKNTXIndex.Flush; begin if FUpdated then begin FNTXBuffers.Flush(FNTXBag.NTXHandler); if not FCreateIndexProc then begin if FNTXOrder.FHead.version > 65530 then FNTXOrder.FHead.version := 0 else FNTXOrder.FHead.version := FNTXOrder.FHead.version + 1; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, 12); end; FUpdated := false; end; end; procedure TVKNTXIndex.Reindex(Activate: boolean = true); begin FReindex := true; try CreateIndex(Activate); finally FReindex := false; end; end; function TVKNTXIndex.GetCreateNow: Boolean; begin Result := false; end; procedure TVKNTXIndex.SetCreateNow(const Value: Boolean); begin if Value then begin CreateIndex; if csDesigning in OwnerTable.ComponentState then ShowMessage(Format('Index %s create successfully!', [NTXFileName])); end; end; function TVKNTXIndex.SeekFirstRecord(Key: String; SoftSeek: boolean = false; PartialKey: boolean = false): Integer; begin Result := FindKey(Key, PartialKey, SoftSeek); end; procedure TVKNTXIndex.Truncate; begin //Truncate ntx file FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.SetEndOfFile; //Create new header FNTXBuffers.Clear; FNTXOrder.FHead.version := 1; FNTXOrder.FHead.root := NTX_PAGE; FNTXOrder.FHead.next_page := 0; FNTXOrder.FHead.reserv1 := #0; FNTXOrder.FHead.reserv3 := #0; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, SizeOf(NTX_HEADER)); FLastOffset := SizeOf(NTX_HEADER); GetFreePage; end; procedure TVKNTXIndex.BeginCreateIndexProcess; begin Truncate; FCreateIndexProc:= true; FFLastFUpdated := FUpdated; FUpdated := true; end; procedure TVKNTXIndex.EndCreateIndexProcess; begin Flush; FUpdated := FFLastFUpdated; FNTXBag.NTXHandler.Seek(0, 0); FNTXBag.NTXHandler.Write(FNTXOrder.FHead, SizeOf(NTX_HEADER)); FNTXBuffers.Clear; FCreateIndexProc:= false; end; procedure TVKNTXIndex.EvaluteAndAddKey(nRec: DWORD); var Key: String; begin Key := EvaluteKeyExpr; AddKey(Key, nRec); end; function TVKNTXIndex.InRange: boolean; var Key: String; begin Key := EvaluteKeyExpr; Result := NTXRange.InRange(Key); end; function TVKNTXIndex.FindKey( Key: String; PartialKey: boolean = false; SoftSeek: boolean = false; Rec: DWORD = 0): Integer; var oB: TVKDBFNTX; m: Integer; iResult: Integer; function Pass(page_off: DWORD): boolean; var i: DWORD; page: pNTX_BUFFER; item: pNTX_ITEM; c: Integer; begin FNTXBuffers.GetPage(FNTXBag.NTXHandler, page_off, page); if page.count > 0 then begin for i := 0 to page.count - 1 do begin item := pNTX_ITEM(pChar(page) + page.ref[i]); c := CmpKeys(item.key, pChar(Key), m); if Rec > 0 then if c = 0 then begin if item.rec_no < Rec then c := 1 else if item.rec_no = Rec then c := 0 else c := -1; end; if c <= 0 then begin //LoKey <= item.key if ( item.page <> 0 ) then begin Result := Pass(item.page); if Result then Exit; end; if not SoftSeek then begin c := CmpKeys(item.key, pChar(Key), m); if c < 0 then begin // HiKey < item.key Result := true; Exit; end; end; if oB.AcceptTmpRecord(item.rec_no) then begin iResult := item.rec_no; Result := true; Exit; end; end; end; end; item := pNTX_ITEM(pChar(page) + page.ref[page.count]); if ( item.page <> 0 ) then Pass(item.page); Result := false; end; begin Result := 0; oB := TVKDBFNTX(FIndexes.Owner); if oB.Active then if FLock then try ClearIfChange; m := Length(Key); if m > FNTXOrder.FHead.key_size then m := FNTXOrder.FHead.key_size; if ( not ( PartialKey or SoftSeek ) ) and (m < FNTXOrder.FHead.key_size) then Exit; iResult := 0; Pass(FNTXOrder.FHead.root); Result := iResult; finally FUnLock; end; end; function TVKNTXIndex.FindKeyFields(const KeyFields: string; const KeyValues: Variant; PartialKey: boolean = false): Integer; var m, l: Integer; Key: String; KeyFields_: string; PartialKeyInternal: boolean; begin Result := 0; KeyFields_ := KeyFields; if KeyFields_ = '' then KeyFields_ := FKeyParser.GetFieldList; m := FKeyParser.SuiteFieldList(KeyFields_, l); if m > 0 then begin Key := FKeyParser.EvaluteKey(KeyFields_, KeyValues); PartialKeyInternal := PartialKey; if not PartialKeyInternal then begin if m > VarArrayHighBound(KeyValues, 1) then PartialKeyInternal := True; end; if PartialKeyInternal then Key := TrimRight(Key); Result:= FindKey(Key, PartialKeyInternal); end; end; function TVKNTXIndex.FindKeyFields(const KeyFields: string; const KeyValues: array of const; PartialKey: boolean = false): Integer; var FieldVal: Variant; begin ArrayOfConstant2Variant(KeyValues, FieldVal); Result := FindKeyFields(KeyFields, FieldVal, PartialKey); end; function TVKNTXIndex.FindKeyFields(PartialKey: boolean = false): Integer; var Key: String; begin Key := FKeyParser.EvaluteKey; if PartialKey then Key := TrimRight(Key); Result := FindKey(Key, PartialKey); end; function TVKNTXIndex.TransKey(Key: String): String; begin Result := Key; TransKey(pChar(Result), Length(Result), false); end; function TVKNTXIndex.IsForIndex: boolean; begin Result := FForExists; end; function TVKNTXIndex.IsUniqueIndex: boolean; begin Result := Unique; end; procedure TVKNTXIndex.DefineBagAndOrder; var oO: TVKNTXOrder; i: Integer; IndexName: String; begin IndexName := ChangeFileExt(ExtractFileName(NTXFileName), ''); if IndexName = '' then IndexName := Order; if IndexName = '' then IndexName := Name; DefineBag; if not FNTXBag.IsOpen then FNTXBag.Open; for i := 0 to FNTXBag.Orders.Count - 1 do begin oO := TVKNTXOrder(FNTXBag.Orders.Items[i]); if AnsiUpperCase(oO.Name) = AnsiUpperCase(IndexName) then FNTXOrder := oO; end; if FNTXOrder = nil then raise Exception.Create('TVKNTXIndex.DefineBagAndOrder: FNTXOrder not defined!'); end; procedure TVKNTXIndex.DefineBag; var oW: TVKDBFNTX; oB: TVKNTXBag; oO: TVKNTXOrder; i: Integer; BgNm, IndexName: String; begin oW := TVKDBFNTX(FIndexes.Owner); IndexName := ChangeFileExt(ExtractFileName(NTXFileName), ''); if IndexName = '' then IndexName := Order; if IndexName = '' then IndexName := Name; FNTXOrder := nil; FNTXBag := nil; for i := 0 to oW.DBFIndexDefs.Count - 1 do begin oB := TVKNTXBag(oW.DBFIndexDefs.Items[i]); BgNm := oB.Name; if BgNm = '' then BgNm := ChangeFileExt(ExtractFileName(oB.IndexFileName), ''); if BagName <> '' then begin if AnsiUpperCase(BgNm) = AnsiUpperCase(BagName) then begin FNTXBag := oB; break; end; end else begin if AnsiUpperCase(BgNm) = AnsiUpperCase(IndexName) then begin FNTXBag := oB; break; end; end; end; if FNTXBag = nil then begin oB := TVKNTXBag(oW.DBFIndexDefs.Add); oB.Name := ChangeFileExt(ExtractFileName(NTXFileName), ''); oB.IndexFileName := NTXFileName; oB.StorageType := oW.StorageType; FNTXBag := oB; end; FNTXBag.FillHandler; if FNTXBag.Orders.Count = 0 then FNTXBag.Orders.Add; oO := TVKNTXOrder(FNTXBag.Orders.Items[0]); FillChar(oO.FHead, SizeOf(NTX_HEADER), #0); oO.Name := ChangeFileExt(ExtractFileName(FNTXBag.IndexFileName), ''); if oO.Name = '' then oO.Name := FNTXBag.Name; FNTXOrder := oO; end; { TVKNTXRange } function TVKNTXRange.GetActive: boolean; begin Result := FActive; end; function TVKNTXRange.InRange(S: String): boolean; var l, c: Integer; begin c := NTX.CompareKeys(pChar(HiKey), pChar(S), NTX.FNTXOrder.FHead.key_size); Result := (c >= 0); //HiKey >= S if Result then begin l := Length(LoKey); if l > 0 then begin c := NTX.CompareKeys(pChar(LoKey), pChar(S), l); Result := (c <= 0); //LoKey <= S end; end; end; procedure TVKNTXRange.ReOpen; var oDB: TVKDBFNTX; begin if not Active then begin Active := true; end else begin NTX.Active := true; oDB := TVKDBFNTX(NTX.OwnerTable); if oDB.Active then oDB.First; end; end; procedure TVKNTXRange.SetActive(const Value: boolean); var oDB: TVKDBFNTX; l: boolean; begin l := FActive; FActive := Value; oDB := TVKDBFNTX(NTX.OwnerTable); NTX.Active := true; if (l <> Value) and oDB.Active then begin oDB.First; end; end; { TVKNTXBuffer } constructor TVKNTXBuffer.Create; begin inherited Create; Fchanged := false; end; { TVKNTXBuffers } function TVKNTXBuffers.FindIndex(page_offset: DWORD; out Ind: Integer): boolean; var B: TVKNTXBuffer; beg, Mid: Integer; begin Ind := Count; if ( Ind > 0 ) then begin beg := 0; B := TVKNTXBuffer(Items[beg]); if ( page_offset > B.Fpage_offset ) then begin repeat Mid := (Ind + beg) div 2; B := TVKNTXBuffer(Items[Mid]); if ( page_offset > B.Fpage_offset ) then beg := Mid else Ind := Mid; until ( ((Ind - beg) div 2) = 0 ); end else Ind := beg; if Ind < Count then begin B := TVKNTXBuffer(Items[Ind]); Result := (page_offset = B.Fpage_offset); end else Result := false; end else Result := false; end; procedure TVKNTXBuffers.Flush(Handle: TProxyStream); var i: Integer; CryptPage: NTX_BUFFER; begin for i := 0 to Count - 1 do begin with TVKNTXBuffer(Items[i]) do begin if Fchanged then begin Handle.Seek(Fpage_offset, 0); if TVKDBFNTX(NXTObject.OwnerTable).Crypt.Active then begin CryptPage := Fpage; TVKDBFNTX(NXTObject.OwnerTable).Crypt.Encrypt(Fpage_offset, @CryptPage, SizeOf(NTX_BUFFER)); Handle.Write(CryptPage, SizeOf(NTX_BUFFER)); end else Handle.Write(Fpage, SizeOf(NTX_BUFFER)); Fchanged := false; end; end; end; end; function TVKNTXBuffers.GetPage(Handle: TProxyStream; page_offset: DWORD; out page: pNTX_BUFFER; fRead: boolean = true): Integer; var i: Integer; b: TVKNTXBuffer; begin if FindIndex(page_offset, i) then begin b := TVKNTXBuffer(Items[i]); page := @b.Fpage; Result := i; end else begin Insert(i, TVKNTXBuffer.Create); Result := i; if fRead then begin Handle.Seek(page_offset, 0); with TVKNTXBuffer(Items[i]) do begin Handle.Read(Fpage, SizeOf(NTX_BUFFER)); if TVKDBFNTX(NXTObject.OwnerTable).Crypt.Active then TVKDBFNTX(NXTObject.OwnerTable).Crypt.Decrypt(page_offset, Pointer(@FPage), SizeOf(NTX_BUFFER)); Fpage_offset := page_offset; page := @Fpage; end; end else with TVKNTXBuffer(Items[i]) do begin Fpage_offset := page_offset; page := @Fpage; end; end; end; function TVKNTXBuffers.GetNTXBuffer(Handle: TProxyStream; page_offset: DWORD; out page: pNTX_BUFFER; fRead: boolean): Pointer; var i: Integer; begin if FindIndex(page_offset, i) then begin Result := Items[i]; page := @TVKNTXBuffer(Result).Fpage; end else begin Insert(i, TVKNTXBuffer.Create); Result := Items[i]; if fRead then begin Handle.Seek(page_offset, 0); with TVKNTXBuffer(Items[i]) do begin Handle.Read(Fpage, SizeOf(NTX_BUFFER)); if TVKDBFNTX(NXTObject.OwnerTable).Crypt.Active then TVKDBFNTX(NXTObject.OwnerTable).Crypt.Decrypt(page_offset, Pointer(@FPage), SizeOf(NTX_BUFFER)); Fpage_offset := page_offset; page := @Fpage; end; end else with TVKNTXBuffer(Items[i]) do begin Fpage_offset := page_offset; page := @Fpage; end; end; end; procedure TVKNTXBuffers.SetChanged(i: Integer); begin TVKNTXBuffer(Items[i]).Fchanged := true; end; procedure TVKNTXBuffers.SetPage(Handle: TProxyStream; page_offset: DWORD; page: pNTX_BUFFER); var i: Integer; begin if FindIndex(page_offset, i) then with TVKNTXBuffer(Items[i]) do Fchanged := true; end; { TVKNTXCompactIndex } procedure TVKNTXCompactIndex.Close; begin SubOffSet := 0; LinkRest; SHead.root := SubOffSet; SHead.next_page := 0; if Handler = nil then begin SysUtils.FileSeek(FHndl, 0, 0); SysUtils.FileWrite(FHndl, SHead, SizeOf(NTX_HEADER)); end else begin Handler.Seek(0, 0); Handler.Write(SHead, SizeOf(NTX_HEADER)); end; NormalizeRest; if Handler = nil then FileClose(FHndl) else Handler.Close; end; procedure TVKNTXCompactIndex.CreateEmptyIndex(var FHead: NTX_HEADER); begin if Handler = nil then begin if FileName = '' then FileName := GetTmpFileName; FHndl := FileCreate(FileName); if FHndl <= 0 then raise Exception.Create('TVKNTXCompactIndex.CreateEmptyIndex: Index create error'); end else Handler.CreateProxyStream; SHead := FHead; SHead.version := 0; SHead.root := NTX_PAGE; SHead.next_page := 0; if Handler = nil then begin SysUtils.FileSeek(FHndl, 0, 0); SysUtils.FileWrite(FHndl, SHead, SizeOf(NTX_HEADER)); end else begin Handler.Seek(0, 0); Handler.Write(SHead, SizeOf(NTX_HEADER)); end; NewPage(0); cur_lev := -1; SubOffSet := NTX_PAGE; end; procedure TVKNTXCompactIndex.NewPage(lev: Integer); var item_off: WORD; i: Integer; begin levels[lev].count := 0; item_off := ( SHead.max_item * 2 ) + 4; for i := 0 to SHead.max_item do begin levels[lev].ref[i] := item_off; item_off := item_off + SHead.item_size; end; pNTX_ITEM(pChar(@levels[lev]) + levels[lev].ref[0]).page := 0; max_lev := lev; end; procedure TVKNTXCompactIndex.NormalizeRest; var LeftPage: TVKNTXBuffer; procedure SavePage(page: TVKNTXBuffer); begin if Handler = nil then FileSeek(FHndl, page.Fpage_offset, 0) else Handler.Seek(page.Fpage_offset, 0); if Crypt then begin CryptPage := page.Fpage; TVKDBFNTX(OwnerTable).Crypt.Encrypt(SubOffSet, @CryptPage, SizeOf(NTX_BUFFER)); if Handler = nil then SysUtils.FileWrite(FHndl, CryptPage, SizeOf(NTX_BUFFER)) else Handler.Write(CryptPage, SizeOf(NTX_BUFFER)); end else begin if Handler = nil then SysUtils.FileWrite(FHndl, page.Fpage, SizeOf(NTX_BUFFER)) else Handler.Write(page.Fpage, SizeOf(NTX_BUFFER)); end; end; procedure GetPage(root: DWORD; page: TVKNTXBuffer); begin if Handler = nil then begin SysUtils.FileSeek(FHndl, root, 0); SysUtils.FileRead(FHndl, page.Fpage, SizeOf(NTX_BUFFER)); end else begin Handler.Seek(root, 0); Handler.Read(page.Fpage, SizeOf(NTX_BUFFER)); end; if Crypt then TVKDBFNTX(OwnerTable).Crypt.Decrypt(root, @page.Fpage, SizeOf(NTX_BUFFER)); page.Fpage_offset := root; end; procedure Normalize(root: DWORD; Parent: TVKNTXBuffer); var item, LItem, SLItem, CItem: pNTX_ITEM; rf: DWORD; Shift, j: Integer; CurrentPage: TVKNTXBuffer; begin CurrentPage := TVKNTXBuffer.Create; GetPage(root, CurrentPage); if Parent <> nil then begin if CurrentPage.Fpage.count < SHead.half_page then begin LItem := pNTX_ITEM( pChar(@Parent.Fpage) + Parent.Fpage.ref[Parent.Fpage.count - 1]); GetPage(LItem.page, LeftPage); SLItem := pNTX_ITEM( pChar(@LeftPage.FPage) + LeftPage.FPage.ref[LeftPage.FPage.count]); Shift := SHead.half_page; LeftPage.FPage.count := LeftPage.FPage.count - Shift; j := CurrentPage.Fpage.count; while j >= 0 do begin rf := CurrentPage.Fpage.ref[j + Shift]; CurrentPage.Fpage.ref[j + Shift] := CurrentPage.Fpage.ref[j]; CurrentPage.Fpage.ref[j] := rf; Dec(j); end; Inc(CurrentPage.Fpage.count, Shift); CItem := pNTX_ITEM( pChar(@CurrentPage.Fpage) + CurrentPage.Fpage.ref[Shift - 1]); Move(LItem.key, CItem.key, SHead.key_size); CItem.rec_no := LItem.rec_no; CItem.page := SLItem.page; Dec(Shift); while Shift > 0 do begin SLItem := pNTX_ITEM( pChar(@LeftPage.FPage) + LeftPage.FPage.ref[LeftPage.FPage.count + Shift]); CItem := pNTX_ITEM( pChar(@CurrentPage.Fpage) + CurrentPage.Fpage.ref[Shift - 1]); Move(SLItem.key, CItem.key, SHead.key_size); CItem.rec_no := SLItem.rec_no; CItem.page := SLItem.page; Dec(Shift); end; SLItem := pNTX_ITEM( pChar(@LeftPage.FPage) + LeftPage.FPage.ref[LeftPage.FPage.count]); Move(SLItem.key, LItem.key, SHead.key_size); LItem.rec_no := SLItem.rec_no; SavePage(Parent); SavePage(CurrentPage); SavePage(LeftPage); end; end; Item := pNTX_ITEM( pChar(@CurrentPage.Fpage) + CurrentPage.Fpage.ref[CurrentPage.Fpage.count]); if Item.page <> 0 then Normalize(Item.page, CurrentPage); CurrentPage.Free; end; begin LeftPage := TVKNTXBuffer.Create; try Normalize(SHead.root, nil); finally LeftPage.Free; end; end; procedure TVKNTXCompactIndex.LinkRest; var page: pNTX_BUFFER; i: pNTX_ITEM; begin Inc(cur_lev); if (cur_lev <= max_lev) then begin page := pNTX_BUFFER(@levels[cur_lev]); i := pNTX_ITEM(pChar(page) + page.ref[page.count]); i.page := SubOffSet; if Handler = nil then SubOffSet := FileSeek(FHndl, 0, 2) else SubOffSet := Handler.Seek(0, 2); if Crypt then begin CryptPage := page^; TVKDBFNTX(OwnerTable).Crypt.Encrypt(SubOffSet, @CryptPage, SizeOf(NTX_BUFFER)); if Handler = nil then SysUtils.FileWrite(FHndl, CryptPage, SizeOf(NTX_BUFFER)) else Handler.Write(CryptPage, SizeOf(NTX_BUFFER)); end else begin if Handler = nil then SysUtils.FileWrite(FHndl, page^, SizeOf(NTX_BUFFER)) else Handler.Write(page^, SizeOf(NTX_BUFFER)); end; LinkRest; end; Dec(cur_lev); end; procedure TVKNTXCompactIndex.AddItem(item: pNTX_ITEM); var page: pNTX_BUFFER; i: pNTX_ITEM; begin Inc(cur_lev); if (cur_lev > max_lev) then NewPage(cur_lev); page := pNTX_BUFFER(@levels[cur_lev]); if page.count = SHead.max_item then begin i := pNTX_ITEM(pChar(page) + page.ref[page.count]); if cur_lev <> 0 then i.page := item.page else i.page := 0; if Crypt then begin CryptPage := page^; TVKDBFNTX(OwnerTable).Crypt.Encrypt(SubOffSet, @CryptPage, SizeOf(NTX_BUFFER)); if Handler = nil then SysUtils.FileWrite(FHndl, CryptPage, SizeOf(NTX_BUFFER)) else Handler.Write(CryptPage, SizeOf(NTX_BUFFER)); end else begin if Handler = nil then SysUtils.FileWrite(FHndl, page^, SizeOf(NTX_BUFFER)) else Handler.Write(page^, SizeOf(NTX_BUFFER)); end; item.page := SubOffSet; Inc(SubOffSet, NTX_PAGE); AddItem(item); page.count := 0; end else begin if ( cur_lev = 0 ) then item.page := 0; Move(item^, (pChar(page) + page.ref[page.count])^, SHead.item_size); page.count := page.count + 1; end; Dec(cur_lev); end; constructor TVKNTXCompactIndex.Create; begin Handler := nil; FHndl := -1; cur_lev := -1; max_lev := -1; SubOffSet := 0; FileName := ''; OwnerTable := nil; Crypt := false; end; destructor TVKNTXCompactIndex.Destroy; begin inherited Destroy; end; { TVKNTXIndexIterator } constructor TVKNTXIndexIterator.Create; begin Eof := false; end; destructor TVKNTXIndexIterator.Destroy; begin inherited Destroy; end; { TVKNTXBlockIterator } procedure TVKNTXBlockIterator.Close; begin VKDBFMemMgr.oMem.FreeMem(p); DeleteFile(FFileName); end; constructor TVKNTXBlockIterator.Create(FileName: String; key_size, BufSize: Integer); begin inherited Create; FFileName := FileName; Fkey_size := key_size; FBufSize := BufSize; end; destructor TVKNTXBlockIterator.Destroy; begin DeleteFile(FFileName); inherited Destroy; end; procedure TVKNTXBlockIterator.Next; var BlockItem: pBLOCK_ITEM; begin Inc(i); if i >= p.count then Eof := true else begin item.page := 0; BlockItem := pBLOCK_ITEM(pChar(p) + p.ref[i]); item.rec_no := BlockItem.rec_no; Move(BlockItem.key, item.key, Fkey_size); end; end; procedure TVKNTXBlockIterator.Open; var BlockItem: pBLOCK_ITEM; begin p := VKDBFMemMgr.oMem.GetMem(self, FBufSize); FHndl := FileOpen(FFileName, fmOpenRead or fmShareExclusive); if FHndl > 0 then begin SysUtils.FileRead(FHndl, p^, FBufSize); SysUtils.FileClose(FHndl); i := 0; if p.count = 0 then Eof := true; item.page := 0; BlockItem := pBLOCK_ITEM(pChar(p) + p.ref[i]); item.rec_no := BlockItem.rec_no; Move(BlockItem.key, item.key, Fkey_size); end else raise Exception.Create('TVKNTXBlockIterator.Open: Open Error "' + FFileName + '"'); end; { TVKNTXIterator } procedure TVKNTXIterator.Close; begin FileClose(FHndl); VKDBFMemMgr.oMem.FreeMem(levels); DeleteFile(FFileName); end; constructor TVKNTXIterator.Create(FileName: String); begin inherited Create; FFileName := FileName; end; destructor TVKNTXIterator.Destroy; begin DeleteFile(FFileName); inherited Destroy; end; procedure TVKNTXIterator.Next; var page: pNTX_BUFFER; i: pNTX_ITEM; begin Inc(indexes[cur_lev]); repeat page := pNTX_BUFFER(@levels^[cur_lev]); i := pNTX_ITEM(pChar(page) + page.ref[indexes[cur_lev]]); if i.page <> 0 then begin Inc(cur_lev); indexes[cur_lev] := 0; SysUtils.FileSeek(FHndl, i.page, 0); SysUtils.FileRead(FHndl, levels^[cur_lev], SizeOf(NTX_BUFFER)); end; until i.page = 0; repeat if indexes[cur_lev] = page.count then begin Dec(cur_lev); if cur_lev = -1 then begin Eof := true; Break; end else begin page := pNTX_BUFFER(@levels^[cur_lev]); i := pNTX_ITEM(pChar(page) + page.ref[indexes[cur_lev]]); item.page := 0; item.rec_no := i.rec_no; Move(i.key, item.key, SHead.key_size); end; end else begin item.page := 0; item.rec_no := i.rec_no; Move(i.key, item.key, SHead.key_size); end; until indexes[cur_lev] < page.count; end; procedure TVKNTXIterator.Open; var page: pNTX_BUFFER; i: pNTX_ITEM; begin levels := VKDBFMemMgr.oMem.GetMem(self, MAX_LEV_BTREE * SizeOf(NTX_BUFFER)); FHndl := FileOpen(FFileName, fmOpenRead or fmShareExclusive); if FHndl > 0 then begin SysUtils.FileRead(FHndl, SHead, SizeOf(NTX_HEADER)); cur_lev := 0; SysUtils.FileSeek(FHndl, SHead.root, 0); SysUtils.FileRead(FHndl, levels^[cur_lev], SizeOf(NTX_BUFFER)); Eof := false; indexes[cur_lev] := 0; if levels^[cur_lev].count = 0 then Eof := true; if not Eof then begin repeat page := pNTX_BUFFER(@levels^[cur_lev]); i := pNTX_ITEM(pChar(page) + page.ref[indexes[cur_lev]]); if i.page <> 0 then begin Inc(cur_lev); indexes[cur_lev] := 0; SysUtils.FileSeek(FHndl, i.page, 0); SysUtils.FileRead(FHndl, levels^[cur_lev], SizeOf(NTX_BUFFER)); end; until i.page = 0; item.page := 0; item.rec_no := i.rec_no; Move(i.key, item.key, SHead.key_size); end; end else raise Exception.Create('TVKNTXIterator.Open: Open Error "' + FFileName + '"'); end; { TVKNTXBag } procedure TVKNTXBag.Close; begin Handler.Close; end; constructor TVKNTXBag.Create(Collection: TCollection); begin inherited Create(Collection); end; function TVKNTXBag.CreateBag: boolean; begin if ( StorageType = pstOuterStream ) and ( OuterStream = nil ) then raise Exception.Create('TVKNTXBag.CreateBag: StorageType = pstOuterStream but OuterStream = nil!'); Handler.FileName := IndexFileName; Handler.AccessMode.AccessMode := TVKDBFNTX(OwnerTable).AccessMode.AccessMode; Handler.ProxyStreamType := StorageType; Handler.OuterStream := OuterStream; Handler.OnLockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamLock; Handler.OnUnlockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamUnlock; Handler.CreateProxyStream; Result := Handler.IsOpen; end; destructor TVKNTXBag.Destroy; begin inherited Destroy; end; procedure TVKNTXBag.FillHandler; begin Handler.FileName := IndexFileName; Handler.AccessMode.AccessMode := TVKDBFNTX(OwnerTable).AccessMode.AccessMode; Handler.ProxyStreamType := StorageType; Handler.OuterStream := OuterStream; Handler.OnLockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamLock; Handler.OnUnlockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamUnlock; end; function TVKNTXBag.IsOpen: boolean; begin Result := Handler.IsOpen; end; function TVKNTXBag.Open: boolean; begin if ( StorageType = pstOuterStream ) and ( OuterStream = nil ) then raise Exception.Create('TVKNTXBag.Open: StorageType = pstOuterStream but OuterStream = nil!'); Handler.FileName := IndexFileName; Handler.AccessMode.AccessMode := TVKDBFNTX(OwnerTable).AccessMode.AccessMode; Handler.ProxyStreamType := StorageType; Handler.OuterStream := OuterStream; Handler.OnLockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamLock; Handler.OnUnlockEvent := TVKDBFNTX(OwnerTable).OnOuterStreamUnlock; Handler.Open; if not Handler.IsOpen then raise Exception.Create('TVKNTXBag.Open: Open error "' + IndexFileName + '"') else begin if Orders.Count = 0 then Orders.Add; with Orders.Items[0] as TVKNTXOrder do begin Handler.Seek(0, 0); Handler.Read(FHead, SizeOf(NTX_HEADER)); //FLastOffset := Handler.Seek(0, 2); if FHead.order <> '' then TVKNTXOrder(Orders.Items[0]).Name := FHead.Order else TVKNTXOrder(Orders.Items[0]).Name := ChangeFileExt(ExtractFileName(IndexFileName), ''); KeyExpresion := FHead.key_expr; ForExpresion := FHead.for_expr; Unique := (FHead.unique <> #0); Desc := (FHead.Desc <> #0); end; end; Result := Handler.IsOpen; end; { TVKNTXOrder } constructor TVKNTXOrder.Create(Collection: TCollection); begin inherited Create(Collection); if Index > 0 then raise Exception.Create('TVKNTXOrder.Create: NTX bag can not content more then one order!'); end; function TVKNTXOrder.CreateOrder: boolean; var oBag: TVKNTXBag; FKeyParser: TVKDBFExprParser; FieldMap: TFieldMap; IndAttr: TIndexAttributes; page: NTX_BUFFER; item_off: WORD; i: Integer; function EvaluteKeyExpr: String; begin if Assigned(OnEvaluteKey) then OnEvaluteKey(self, Result) else Result := FKeyParser.EvaluteKey; end; begin oBag := TVKNTXBag(TVKDBFOrders(Collection).Owner); FKeyParser := TVKDBFExprParser.Create(TVKDBFNTX(oBag.OwnerTable), '', [], [poExtSyntax], '', nil, FieldMap); FKeyParser.IndexKeyValue := true; if ClipperVer in [v500, v501] then FHead.sign := 6 else FHead.sign := 7; FHead.version := 1; FHead.root := NTX_PAGE; FHead.next_page := 0; if Assigned(OnCreateIndex) then begin OnCreateIndex(self, IndAttr); FHead.key_size := IndAttr.key_size; FHead.key_dec := IndAttr.key_dec; System.Move(pChar(IndAttr.key_expr)^, FHead.key_expr, Length(IndAttr.key_expr)); FHead.key_expr[Length(IndAttr.key_expr)] := #0; System.Move(pChar(IndAttr.for_expr)^, FHead.for_expr, Length(IndAttr.for_expr)); FHead.for_expr[Length(IndAttr.for_expr)] := #0; end else begin FKeyParser.SetExprParams1(KeyExpresion, [], [poExtSyntax], ''); EvaluteKeyExpr; FHead.key_size := Length(FKeyParser.Key); FHead.key_dec := FKeyParser.Prec; System.Move(pChar(KeyExpresion)^, FHead.key_expr, Length(KeyExpresion)); FHead.key_expr[Length(KeyExpresion)] := #0; System.Move(pChar(ForExpresion)^, FHead.for_expr, Length(ForExpresion)); FHead.for_expr[Length(ForExpresion)] := #0; end; FHead.item_size := FHead.key_size + 8; FHead.max_item := (NTX_PAGE - FHead.item_size - 4) div (FHead.item_size + 2); FHead.half_page := FHead.max_item div 2; FHead.max_item := FHead.half_page * 2; FHead.reserv1 := #0; FHead.reserv3 := #0; System.Move(pChar(Name)^, FHead.order, Length(Name)); if Desc then FHead.Desc := #1; if Unique then FHead.Unique := #1; oBag.NTXHandler.Seek(0, 0); oBag.NTXHandler.Write(FHead, SizeOf(NTX_HEADER)); page.count := 0; item_off := ( FHead.max_item * 2 ) + 4; for i := 0 to FHead.max_item do begin page.ref[i] := item_off; item_off := item_off + FHead.item_size; end; pNTX_ITEM(pChar(@page) + page.ref[0]).page := 0; oBag.NTXHandler.Write(page, SizeOf(NTX_BUFFER)); oBag.NTXHandler.SetEndOfFile; FKeyParser.Free; Result := True; end; destructor TVKNTXOrder.Destroy; begin inherited Destroy; end; end.