All pastes #74295 Raw Edit

v

public text v1 · immutable
#74295 ·published 2006-06-28 21:39 UTC
rendered paste body
unit DbcHandler;

interface

uses
    Windows, Classes, SysUtils, Dialogs, AcedContainers, Variants, IniFiles, StrUtils;

type
  TKeyType = (ktIntArray,ktStringHT,ktIntHT);
  TOutStringArray=record
    OutString: string;
    offset: integer;
  end;
  TFieldTypesRecord=record
    name,kind: string;
    kind_int: string;
    hex: boolean;
  end;
  TDbcHandler = class
  private
    FName: string;
    FFieldCount,FRowCount,FTextLength,FRowLength,FKeyFieldNumber,FKeyFieldNumber2,
    max_ID,toReplaceCount: integer;
    KeyType: TKeyType;
    int_values_arr: array of integer;                   //value array
    str_values_arr: array of char;
    link_recordNumber2offset_arr: array of integer;     //links to values from string HT
    link_Id2offset_arr: array of integer;               //links by ID
    link2replace: array of integer;
    str_fields_numbers: array of integer;
    procedure Add2ReplaceArr(offs: integer);
    procedure GetIniSettings(DbcIniFName: string);
    procedure PutStringByOffset(value: string; int_offset,
      column_number: integer);
  public
    DbcHT: TIntegerHashtable;                           //offsets by integer key
    DbcStrHT: TStringHashtable;                         //offsets by string key
    function GetStringByOffset(offset: integer): string;
    procedure LoadDbcArray(DbcFileName: string; KeyFieldNumber,KeyFieldNumber2: integer; ktType: TKeyType);
    procedure SaveDbc;
    function ContainStrKey(value: string): boolean;
    function ContainIntKey(value: integer): boolean;
    function GetIntValueByStrKey(key: string; column_number: integer): integer;
    function GetIntValueByIntKey(key, column_number: integer): integer;
    function GetStrValueByStrKey(key: string; column_number: integer): string;
    function GetStrValueByIntKey(key, column_number: integer): string;
    function GetSingleValueByIntKey(key, column_number: integer): single;
    function GetSingleValueByStrKey(key: string; column_number: integer): single;
    function GetPRValueByStrKey(key: string; PR: pointer):integer;
    function GetPointerPRValueByStrKey(key: string):pointer;
    function GetPRValueByIntKey(key: integer; PR: pointer): integer;
    function GetPointerPRValueByIntKey(key: integer):pointer;
    function Get2arrayByIntKey(key, column_number: integer; buff: pointer;
      bytesgetlen: integer): integer;
    procedure SetIntValueByIntKey(key, column_number, value: integer);
    procedure SetIntValueByStrKey(key: string; column_number, value: integer);
    function SetPRValueByIntKey(key: integer; PR: pointer): integer;
    function SetPRValueByStrKey(key: string; PR: pointer): integer;
    procedure SetSingleValueByIntKey(key, column_number: integer; value: single);
    procedure SetSingleValueByStrKey(key: string; column_number: integer; value: single);
    procedure SetStrValueByIntKey(key, column_number: integer; value: string);
    procedure SetStrValueByStrKey(key: string; column_number: integer; value: string);
    function AddRecord(data: array of variant):integer;
    function AddArray(data: pointer; bytes_arr_len: integer): integer;
    procedure RemoveRecordIntKey(key: integer);
    procedure RemoveRecordStrKey(key: string);
    procedure Add2arrayWholeWithStrings(key, first_column: integer; var mod_arr; var offs: integer);
  published
    property MaxID: integer read max_ID;
  end;
  function PJWStringHash(const Key: String; TableHigh: Integer): integer;

var
  CharBuffer: array[0..4095]of char;
const
     maxlinkarr_size = $100000;

implementation

uses UnitMain, ByteArrayHandler;

function PJWStringHash(const Key: String; TableHigh: Integer): integer;
var
   Hash, G: Longint;
   i: integer;
begin
     hash:=0;
     for i:=1 to length(Key) do
     begin
          Hash:=(Hash shl 4)+ord(Key[i]);
          G:=Hash and longint($F0000000);
          if (G<>0) then Hash:=(Hash xor (G shl 24) xor G);
     end;
     Result:=hash mod TableHigh;
end;

function TDbcHandler.GetStringByOffset(offset: integer): string;  //ready offset in strings
var
   presult: PChar;
begin
   try
     presult:=@str_values_arr[offset];
     result:=strpas(presult);
   except AddToLog('GetStringByOffset exception, offset='+IntToStr(offset));
   end;
end;

procedure TDbcHandler.GetIniSettings(DbcIniFName: string);
var
   DbcIni: TIniFile;
   i,rec_count,str_f_count: integer;
   promTString: TStringList;
   field_kind,iniFName: string;
begin
     iniFName:=AnsiReplaceStr(DbcIniFName,'.dbc','.ini');
     iniFName:=AnsiReplaceStr(iniFName,'.mbc','.ini');
     DbcIni:=TIniFile.Create(iniFName);
     promTString:=TStringList.Create;
     try
        DbcIni.ReadSectionValues('Field name',promTString);
        rec_count:=promTString.Count;
        str_f_count:=0;
        for i:=0 to pred(rec_count) do
        begin
             field_kind:=DbcIni.ReadString('Field kind','Field '+IntToStr(i),'ftInteger');
             if field_kind='ftString' then
             begin
                  inc(str_f_count);
                  setlength(str_fields_numbers,str_f_count);
                  str_fields_numbers[str_f_count-1]:=i;
             end;
        end;
     finally DbcIni.Free;
             promTString.Free;
     end;
end;

procedure TDbcHandler.Add2ReplaceArr(offs: integer);
begin
     inc(toReplaceCount);
     setlength(link2replace,toReplaceCount);
     link2replace[toReplaceCount-1]:=offs;
end;

procedure TDbcHandler.LoadDbcArray(DbcFileName: string; KeyFieldNumber,KeyFieldNumber2: integer; ktType: TKeyType); //KeyFieldNumber for HT only
var
   i: integer;
   InFile: TFileStream;
   HeaderDbc: array[0..4]of integer;
   row_count,field_count,row_length,text_length,
      HeaderSizeInBytes,curr_max_ID,offs,promint: integer;
   key_name: string;
function CheckAdd2Replace(curr_offs,curr_row: integer):boolean;
var
   j: integer;
   all_zero: boolean;
begin
     j:=1;all_zero:=true;
     while (j < field_count)and(all_zero) do
     begin
          if (int_values_arr[curr_offs+j] <> 0) then all_zero:=false;
          inc(j);
     end;
     if all_zero then Add2ReplaceArr(curr_offs);
     result:=all_zero;
end;
begin
     FName:=DbcFileName;
     GetIniSettings(DbcFileName);
     InFile:=TFileStream.Create(DbcFileName,fmOpenRead);

     try
        //try
           KeyType:=ktType;
           HeaderSizeInBytes:=sizeof(HeaderDbc);
           InFile.Read(HeaderDbc,HeaderSizeInBytes);
           if HeaderDbc[0]=$43424457 then
           begin
                row_count:=HeaderDbc[1];
                field_count:=HeaderDbc[2];
                row_length:=HeaderDbc[3];
                text_length:=HeaderDbc[4];
                FRowCount:=row_count;
                FFieldCount:=field_count;
                FRowLength:=row_length;
                FTextLength:=text_length;
                FKeyFieldNumber:=KeyFieldNumber;
                FKeyFieldNumber2:=KeyFieldNumber2;

                if (KeyFieldNumber >= field_count)or(KeyFieldNumber2 >= field_count) then
                   raise Exception.Create('KeyFieldNumber not in field count');

                setlength(int_values_arr,row_count*field_count);
                InFile.Seek(20,soFromBeginning);
                InFile.Read(int_values_arr[0],row_count*row_length);

                setlength(str_values_arr,text_length);
                InFile.Seek(-text_length,soFromEnd);
                InFile.Read(str_values_arr[0],text_length);

                max_ID:=0;toReplaceCount:=0;
                if ktType=ktIntArray then
                begin
                     setlength(link_recordNumber2offset_arr,row_count);
                     setlength(link_Id2offset_arr,maxlinkarr_size);
                     for i:=0 to pred(maxlinkarr_size) do link_Id2offset_arr[i]:=-1;
                     for i:=0 to pred(row_count) do
                     begin
                          offs:=i*field_count;
                          curr_max_ID:=int_values_arr[offs]; //ID
                          if (curr_max_ID > max_ID) then max_ID:=curr_max_ID;
                          link_recordNumber2offset_arr[i]:=offs;

                          if not(CheckAdd2Replace(offs,i))then
                             link_Id2offset_arr[int_values_arr[offs]]:=offs;
                     end;
                     setlength(link_Id2offset_arr,max_ID+1);
                end
                else if ktType=ktStringHT then begin
                     setlength(link_recordNumber2offset_arr,row_count);
                     DbcStrHT:=TStringHashtable.Create(false,row_count);
                     for i:=0 to pred(row_count) do
                     begin
                          offs:=i*field_count;
                          curr_max_ID:=int_values_arr[offs]; //ID
                          if (curr_max_ID > max_ID) then max_ID:=curr_max_ID;
                          link_recordNumber2offset_arr[i]:=offs;

                          if not(CheckAdd2Replace(offs,i))then
                          try
                             key_name:=GetStringByOffset(int_values_arr[offs+KeyFieldNumber]);
                             if (key_name <> '') then DbcStrHT.Add(AnsiUpperCase(key_name),@link_recordNumber2offset_arr[i]);
                          except;
                          end;
                     end;
                end
                else begin   //integer HT
                     setlength(link_recordNumber2offset_arr,row_count);
                     DbcHT:=TIntegerHashtable.Create(row_count);
                     for i:=0 to pred(row_count) do
                     begin
                          offs:=i*field_count;
                          curr_max_ID:=int_values_arr[offs]; //ID
                          if (curr_max_ID > max_ID) then max_ID:=curr_max_ID;
                          link_recordNumber2offset_arr[i]:=offs;

                          if not(CheckAdd2Replace(offs,i))then
                          try
                             if FKeyFieldNumber2 >= 0 then
                                promint:=(int_values_arr[offs+FKeyFieldNumber] shl 16)or(int_values_arr[offs+FKeyFieldNumber2] and $FFFF)
                             else promint:=int_values_arr[offs+FKeyFieldNumber];
                             DbcHT.Add(promint,@link_recordNumber2offset_arr[i]);
                          except;
                          end;
                     end;
                end;
           end;
        //except ShowMessage('Some problems with '+DbcFileName+' or not found');
        //end;
     finally InFile.Free;
     end;
end;

procedure TDbcHandler.SaveDbc;
var
   OutFile: TFileStream;
   HeaderDbc: array[0..4]of integer;
   i,j,str_fields_numbers_last,HeaderSizeInBytes,
      max_string_number,loffs,promstrlen: integer;
   OutStringArray: array of char;
   StringHT: TStringHashtable;
   string_offsets: array of integer;
   promstr: string;
   pinteger: ^integer;
begin
   try
     //rebuild strings
     if (str_fields_numbers<>nil)and(length(str_fields_numbers)<>0)then
     begin
          setlength(string_offsets,$1000000);
          max_string_number:=1;
          string_offsets[max_string_number]:=1;
          setlength(OutStringArray,length(str_values_arr));
          str_fields_numbers_last:=length(str_fields_numbers)-1;
          StringHT:= TStringHashtable.Create(true,FRowCount);
          for i:=0 to pred(FRowCount)do
          begin
               for j:=0 to str_fields_numbers_last do
               begin
                    loffs:=link_recordNumber2offset_arr[i]+str_fields_numbers[j];
                    promstr:=trim(GetStringByOffset(int_values_arr[loffs]));
                    if promstr='' then int_values_arr[loffs]:=0
                    else if (StringHT.Contains(promstr))then
                    begin
                         pinteger:=StringHT[promstr];
                         int_values_arr[loffs]:=pinteger^;
                    end
                    else begin
                         int_values_arr[loffs]:=string_offsets[max_string_number];
                         StringHT.Add(promstr,@string_offsets[max_string_number]);
                         promstrlen:=length(promstr);
                         move((@promstr[1])^,(@OutStringArray[string_offsets[max_string_number]])^,promstrlen);
                         inc(max_string_number);
                         string_offsets[max_string_number]:=string_offsets[max_string_number-1]+promstrlen+1;
                    end;
               end;
          end;
          move(OutStringArray[0],str_values_arr[0],string_offsets[max_string_number]);
          setlength(str_values_arr,string_offsets[max_string_number]);
          StringHT.Free;
     end;

     if (int_values_arr <> nil)then
     begin
          OutFile:=TFileStream.Create(FName,fmCreate);
          try
             HeaderSizeInBytes:=sizeof(HeaderDbc);
             HeaderDbc[0]:=$43424457;
             HeaderDbc[1]:=FRowCount;
             HeaderDbc[2]:=FFieldCount;
             HeaderDbc[3]:=FRowLength;
             HeaderDbc[4]:=length(str_values_arr);
             OutFile.Write(HeaderDbc,HeaderSizeInBytes);

             OutFile.Write(int_values_arr[0],FRowCount*FRowLength);

             OutFile.Write(str_values_arr[0],length(str_values_arr));
          except;
          end;
          OutFile.Free;
     end;
     OutStringArray:=nil;
     string_offsets:=nil;
   except AddToLog('SveDbc exception, file='+FName);
   end;
end;

function TDbcHandler.ContainStrKey(value: string):boolean;
begin
     result:=false;
     if (KeyType=ktStringHT) then result:=DbcStrHT.Contains(value);
end;

function TDbcHandler.ContainIntKey(value: integer):boolean;
begin
     result:=false;
     if (KeyType=ktIntHT)then result:=DbcHT.Items[value]<>nil
     else if (KeyType=ktIntArray)and(value >= 0)and(value <= max_ID) then result:=true;
end;

function TDbcHandler.GetStrValueByStrKey(key: string; column_number: integer):string;
var
   ppos: ^integer;
begin
   try
     result:='';
     if (KeyType=ktStringHT) then
     begin
          ppos:=DbcStrHT[key];
          result:=GetStringByOffset(int_values_arr[ppos^+column_number]);
     end;
   except AddToLog('GetStrValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.GetStrValueByIntKey(key: integer; column_number: integer):string;
var
   ppos: ^integer;
   link: integer;
begin
   try
     result:='';
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          result:=GetStringByOffset(int_values_arr[ppos^+column_number]);
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then result:=GetStringByOffset(int_values_arr[link+column_number]);
     end;
   except AddToLog('GetStrValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.GetIntValueByStrKey(key: string; column_number: integer):integer;
var
   ppos: ^integer;
begin
   result:=-1;
   try
     if (KeyType=ktStringHT) then    //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
             result:=int_values_arr[ppos^+column_number];
     end;
   except AddToLog('GetIntValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.GetIntValueByIntKey(key: integer; column_number: integer):integer;
var
   ppos: ^integer;
   link: integer;
begin
   result:=-1;
   try
     if (KeyType=ktIntHT) then    //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
             result:=int_values_arr[ppos^+column_number];
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then result:=int_values_arr[link+column_number];
     end;
   except AddToLog('GetIntValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

{ pointers to Packed Records }

function TDbcHandler.GetPRValueByStrKey(key: string; PR: pointer):integer;
var
   ppos: ^integer;
begin
   result:=0;
   try
     if (KeyType=ktStringHT) then    //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
          begin
               result:=FFieldCount shl 2;
               move((@int_values_arr[ppos^])^,PR^,result);
          end;
     end;
   except AddToLog('GetPRValueByStrKey exception, key='+key);
   end;
end;

function TDbcHandler.GetPointerPRValueByStrKey(key: string):pointer;
begin
   //result:=nil;
   try
     if (KeyType=ktStringHT) then result:=DbcStrHT[key];    //and(DbcStrHT.Contains(key))
   except AddToLog('GetPointerPRValueByStrKey exception, key='+key);
   end;
end;

function TDbcHandler.GetPRValueByIntKey(key: integer; PR: pointer):integer;
var
   ppos: ^integer;
   link: integer;
begin
   result:=0;
   try
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          result:=FFieldCount shl 2;
          move((@int_values_arr[ppos^])^,PR^,result);
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then
          begin
               result:=FFieldCount shl 2;
               move((@int_values_arr[link])^,PR^,result);
          end;
     end;
   except AddToLog('GetPRValueByIntKey exception, key='+IntToStr(key));
   end;
end;

function TDbcHandler.GetPointerPRValueByIntKey(key: integer):pointer;
var
   link: integer;
   ppos: ^integer;
begin
   result:=nil;
   try
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then    //DbcHT.Contains(key)
               result:=@int_values_arr[ppos^];
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then result:=@int_values_arr[link];
     end;
   except AddToLog('GetPointerPRValueByIntKey exception, key='+IntToStr(key));
   end;
end;

function TDbcHandler.GetSingleValueByStrKey(key: string; column_number: integer):single;
var
   ppos: ^integer;
   psingle: ^single;
begin
   result:=-1;
   try
     if (KeyType=ktStringHT) then    //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
          begin
               psingle:=@int_values_arr[ppos^+column_number];
               result:=psingle^;
          end;
     end;
   except AddToLog('GetSingleValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.GetSingleValueByIntKey(key: integer; column_number: integer):single;
var
   ppos: ^integer;
   psingle: ^single;
   link: integer;
begin
   result:=-1;
   try
     if (KeyType=ktIntHT) then    //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
          begin
               psingle:=@int_values_arr[ppos^+column_number];
               result:=psingle^;
          end;
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then
          begin
               psingle:=@int_values_arr[link+column_number];
               result:=psingle^;
          end;
     end;
   except AddToLog('GetSingleValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.Get2arrayByIntKey(key: integer; column_number: integer; buff: pointer; bytesgetlen: integer):integer;
var
   ppos: ^integer;
   link: integer;
begin
   result:=-1;
   try
     if (KeyType=ktIntHT) then    //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
          begin
               move(int_values_arr[ppos^+column_number],buff^,bytesgetlen);
               result:=bytesgetlen;
          end;
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then
          begin
               move(int_values_arr[link+column_number],buff^,bytesgetlen);
               result:=bytesgetlen;
          end;
     end;
   except AddToLog('Get2arrayByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

procedure TDbcHandler.PutStringByOffset(value: string; int_offset, column_number: integer);
var
   j,new_str_offset: integer;
   promstr: string;
begin
     promstr:=trim(value);
     if (promstr = '') then int_values_arr[int_offset+column_number]:=0
     else begin
          new_str_offset:=length(str_values_arr);
          setlength(str_values_arr,new_str_offset+succ(length(promstr)));
          for j:=1 to length(promstr) do str_values_arr[new_str_offset+j-1]:=promstr[j];
          int_values_arr[int_offset+column_number]:=new_str_offset;
     end;
end;

procedure TDbcHandler.SetStrValueByStrKey(key: string; column_number: integer; value: string);
var
   ppos: ^integer;
begin
   try
     if (KeyType=ktStringHT) then
     begin
          ppos:=DbcStrHT[key];
          PutStringByOffset(value,ppos^,column_number);
     end;
   except AddToLog('SetStrValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

procedure TDbcHandler.SetStrValueByIntKey(key: integer; column_number: integer; value: string);
var
   ppos: ^integer;
   link: integer;
begin
   try
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          PutStringByOffset(value,ppos^,column_number);
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then PutStringByOffset(value,int_values_arr[link],column_number);
     end;
   except AddToLog('SetStrValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

procedure TDbcHandler.SetIntValueByStrKey(key: string; column_number, value: integer);
var
   ppos: ^integer;
begin
   try
     if (KeyType=ktStringHT) then     //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
             int_values_arr[ppos^+column_number]:=value;
     end;
   except AddToLog('SetIntValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

procedure TDbcHandler.SetIntValueByIntKey(key: integer; column_number, value: integer);
var
   ppos: ^integer;
   link: integer;
begin
   try
     if (KeyType=ktIntHT) then    //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
             int_values_arr[ppos^+column_number]:=value;
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then int_values_arr[link+column_number]:=value;
     end;
   except AddToLog('SetIntValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.SetPRValueByStrKey(key: string; PR: pointer):integer;
{var     TODO
   ppos: ^integer;}
begin
   result:=0;
   {try
     if (KeyType=ktStringHT) then    //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
          begin
               result:=FFieldCount shl 2;
               move((@int_values_arr[ppos^])^,PR^,result);
          end;
     end;
   except AddToLog('SetPRValueByStrKey exception, key='+key);
   end;}
end;

function TDbcHandler.SetPRValueByIntKey(key: integer; PR: pointer):integer;
{var     TODO
   ppos: ^integer;
   link: integer; }
begin
   result:=0;
   {try
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          result:=FFieldCount shl 2;
          move((@int_values_arr[ppos^])^,PR^,result);
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then
          begin
               result:=FFieldCount shl 2;
               move((@int_values_arr[link])^,PR^,result);
          end;
     end;
   except AddToLog('SetPRValueByIntKey exception, key='+IntToStr(key));
   end;}
end;

procedure TDbcHandler.SetSingleValueByStrKey(key: string; column_number: integer; value :single);
var
   ppos,pint: ^integer;
begin
   try
     if (KeyType=ktStringHT) then     //and(DbcStrHT.Contains(key))
     begin
          ppos:=DbcStrHT[key];
          if ppos<>nil then
          begin
               pint:=@value;
               int_values_arr[ppos^+column_number]:=pint^;
          end;
     end;
   except AddToLog('SetSingleValueByStrKey exception, key='+key+' column='+IntToStr(column_number));
   end;
end;

procedure TDbcHandler.SetSingleValueByIntKey(key: integer; column_number: integer; value: single);
var
   ppos,pint: ^integer;
   link: integer;
begin
   try
     if (KeyType=ktIntHT) then     //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
          begin
               pint:=@value;
               int_values_arr[ppos^+column_number]:=pint^;
          end;
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(column_number < FFieldCount)and(key <= max_ID) then
     begin
          link:=link_Id2offset_arr[key];
          if link>=0 then
          begin
               pint:=@value;
               int_values_arr[+column_number]:=pint^;
          end;
     end;
   except AddToLog('SetSingleValueByIntKey exception, key='+IntToStr(key)+' column='+IntToStr(column_number));
   end;
end;

function TDbcHandler.AddRecord(data: array of variant):integer;
var
   i,last_iter,new_int_offset,insert2row: integer;
   key_name: string;
   promsingle: single;
   //promdouble: double;
   pint: ^integer;
   //pint64: ^int64;
begin
   result:=0;
   try
     last_iter:=length(data);
     if (FFieldCount < last_iter) then last_iter:=FFieldCount;

     new_int_offset:=length(int_values_arr);
     insert2row:=FRowCount;
     if toReplaceCount > 0 then
     begin
          new_int_offset:=link2replace[toReplaceCount-1];
          dec(toReplaceCount);
          setlength(link2replace,toReplaceCount);
          insert2row:=new_int_offset div FFieldCount;
     end
     else begin
          setlength(int_values_arr,new_int_offset+FFieldCount);
          inc(FRowCount);
     end;

     inc(max_ID);
     result:=max_ID;
     int_values_arr[new_int_offset]:=max_ID;

     i:=1;
     while (i < last_iter) do  //0 - ID, auto
     begin
          case VarType(data[i]) of
               varString: PutStringByOffset(data[i],new_int_offset,i);
               varDouble: begin //single through double %|
                               promsingle:=data[i];
                               pint:=@promsingle;
                               int_values_arr[new_int_offset+i]:=pint^;
               end;
               varEmpty : int_values_arr[new_int_offset+i]:=0;
               else begin
                    int_values_arr[new_int_offset+i]:=data[i];
               end;
          end;
          inc(i);
     end;

     if KeyType=ktIntArray then
     begin
          setlength(link_Id2offset_arr,max_ID+1);
          link_Id2offset_arr[max_ID]:=new_int_offset;
          setlength(link_recordNumber2offset_arr,FRowCount);
          link_recordNumber2offset_arr[insert2row]:=new_int_offset;
     end
     else if KeyType=ktStringHT then begin
          setlength(link_recordNumber2offset_arr,FRowCount);
          link_recordNumber2offset_arr[insert2row]:=new_int_offset;
          try
             key_name:=GetStringByOffset(int_values_arr[new_int_offset+FKeyFieldNumber]);
             if (key_name <> '') then DbcStrHT.Add(AnsiUpperCase(key_name),@link_recordNumber2offset_arr[insert2row]);
          except;
          end;
     end
     else begin
          setlength(link_recordNumber2offset_arr,FRowCount);
          link_recordNumber2offset_arr[insert2row]:=new_int_offset;
          try
             DbcHT.Add(int_values_arr[new_int_offset+FKeyFieldNumber],@link_recordNumber2offset_arr[insert2row]);
          except;
          end;
     end;
     FTextLength:=length(str_values_arr);
   except AddToLog('AddRecord exception, variants');
   end;
end;

function TDbcHandler.AddArray(data: pointer; bytes_arr_len: integer):integer;  //starting from 0 pos, not for StringHT, result new ID
var
   new_int_offset,insert2row,bytes_count: integer;  //i,last_iter,
   //pint: ^integer;
   //pint64: ^int64;
   //found: boolean;
   //intList: array of integer;
begin
   result:=0;
   if KeyType=ktStringHT then exit;
   
   try
     {last_iter:=length(data);
     if (FFieldCount < last_iter) then last_iter:=FFieldCount;}

     new_int_offset:=length(int_values_arr);
     insert2row:=FRowCount;
     if toReplaceCount > 0 then
     begin
          new_int_offset:=link2replace[toReplaceCount-1];
          dec(toReplaceCount);
          setlength(link2replace,toReplaceCount);
          insert2row:=new_int_offset div FFieldCount;
     end
     else begin
          setlength(int_values_arr,new_int_offset+FFieldCount);
          inc(FRowCount);
     end;

     inc(max_ID);
     result:=max_ID;

     bytes_count:=FRowLength;
     if bytes_count>bytes_arr_len then bytes_count:=bytes_arr_len;
     move(data^,int_values_arr[new_int_offset],bytes_count);
     int_values_arr[new_int_offset]:=max_ID;

     if KeyType=ktIntArray then
     begin
          setlength(link_Id2offset_arr,max_ID+1);
          link_Id2offset_arr[max_ID]:=new_int_offset;
          setlength(link_recordNumber2offset_arr,FRowCount);
          link_recordNumber2offset_arr[insert2row]:=new_int_offset;
     end
     else if KeyType=ktIntHT then begin
          setlength(link_recordNumber2offset_arr,FRowCount);
          link_recordNumber2offset_arr[insert2row]:=new_int_offset;
          try
             DbcHT.Add(int_values_arr[new_int_offset+FKeyFieldNumber],@link_recordNumber2offset_arr[insert2row]);
          except;
          end;
     end;
   except AddToLog('AddArray exception');
   end;
end;

procedure TDbcHandler.RemoveRecordStrKey(key: string);
var
   i: integer;
   ppos: ^integer;
begin
   try
     if (KeyType=ktStringHT) then
     begin
          ppos:=DbcStrHT[key];
          Add2ReplaceArr(link_Id2offset_arr[int_values_arr[ppos^]]);
          for i:=1 to pred(FFieldCount)do
              int_values_arr[ppos^+i]:=0;
          DbcStrHT.Remove(key);
     end;
   except AddToLog('RemoveRecordStrKey exception, key='+key);
   end;
end;

procedure TDbcHandler.RemoveRecordIntKey(key: integer);
var
   i: integer;
   ppos: ^integer;
begin
   try
     if (KeyType=ktIntHT)then
     begin
          ppos:=DbcHT[key];
          Add2ReplaceArr(link_Id2offset_arr[int_values_arr[ppos^]]);
          for i:=1 to pred(FFieldCount)do
              int_values_arr[ppos^+i]:=0;
          DbcHT.Remove(key);
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(key <= max_ID) then
     begin
          Add2ReplaceArr(link_Id2offset_arr[key]);
          for i:=1 to pred(FFieldCount)do
              int_values_arr[link_Id2offset_arr[key]+i]:=0;
     end;
   except AddToLog('RemoveRecordIntKey exception, key='+IntToStr(key));
   end;
end;

//integer keys only
procedure TDbcHandler.Add2arrayWholeWithStrings(key, first_column: integer; var mod_arr; var offs: integer);
var
   i,str_offs,key_offs,next_string_column,curr_column,move_count: integer;
   ppos: ^integer;
begin
     key_offs:=0;
     if (KeyType=ktIntHT) then     //and(DbcHT.Contains(key))
     begin
          ppos:=DbcHT[key];
          if ppos<>nil then
             key_offs:=ppos^;
     end
     else if (KeyType=ktIntArray)and(key >= 0)and(key <= max_ID) then
          key_offs:=link_Id2offset_arr[key];

     if key_offs>=0 then
     begin
          curr_column:=first_column;
          for i:=0 to pred(length(str_fields_numbers)) do
          begin
               next_string_column:=str_fields_numbers[i];
               if next_string_column < curr_column then continue
               else if next_string_column = curr_column then
               begin
                    str_offs:=int_values_arr[key_offs+next_string_column];
                    Add2byteArr(GetStringByOffset(str_offs),mod_arr,offs);
                    inc(curr_column);
               end
               else begin //next_string_column > curr_column
                    move_count:=(next_string_column - curr_column) shl 2;
                    move(int_values_arr[key_offs+curr_column],(@TBytes(mod_arr)[offs])^,move_count);
                    inc(offs,move_count);
                    curr_column:=next_string_column;
                    //now string
                    str_offs:=int_values_arr[key_offs+next_string_column];
                    Add2byteArr(GetStringByOffset(str_offs),mod_arr,offs);
                    inc(curr_column);
               end;
          end;
          //fill rest
          move_count:=pred(FFieldCount) - curr_column;
          if move_count > 0 then
          begin
               move_count:=move_count shl 2;
               move(int_values_arr[key_offs+curr_column],(@TBytes(mod_arr)[offs])^,move_count);
               inc(offs,move_count);
          end;
     end;
end;

end.