How do I speedup the copy of a generic array? - arrays

I have a simple array:
Myarray<T> = array[0..100] of T;
How do I copy this as fast as possible into another array.
Note that the source is always a plain generic array and the dest can be a dynamic array. (but I doubt that matters).
I tried
move(Source[0], dest[0], count * sizeof(T));
But that does not play nice with managed types.
I'm hoping to get something faster than:
for i:= 0 to count -1 do D[i]:= S[i];
What options do I have?
Background
The array is part of a BTree structure and there is a lot of copying going on.
I need the speed.

I asked the same question on Google+ - https://plus.google.com/u/0/116430453567926016001/posts/b1u1shzkmrW
The code is by Stefan Glienke and I have re-posted the code here with to minor changes to two variable types, so it is 64 bit compatible.
uses
TypInfo,
SysUtils;
type
TArray = record
class function Clone<T>(const source: T): T; static;
end;
function DynArrayLength(const A: Pointer): NativeInt;
type
PDynArrayRec = ^TDynArrayRec;
TDynArrayRec = packed record
{$IFDEF CPUX64}
_Padding: LongInt;
{$ENDIF}
RefCnt: LongInt;
Length: NativeInt;
end;
begin
Result := 0;
if A <> nil then
Result := PDynArrayRec(PByte(A) - SizeOf(TDynArrayRec))^.Length;
end;
{$POINTERMATH ON}
type
PArray = ^Pointer;
{$POINTERMATH OFF}
procedure CopyArrayDeep(Dest, Source: PArray; TypeInfo: Pointer; Count: NativeInt);
var
typeData: PTypeData;
elType: PPTypeInfo;
i: Integer;
len : NativeInt;
begin
if (Source = nil) or (Count = 0) then
Exit;
typeData := GetTypeData(TypeInfo);
elType := typeData.elType;
if (elType <> nil) and (elType^.kind = tkDynArray) then
begin
for i := 0 to Count-1 do
begin
len := DynArrayLength(Source[i]);
DynArraySetLength(Dest[i], elType^, 1,#len);
CopyArrayDeep(Dest[i], Source[i], elType^, len);
end;
end
else
if elType <> nil then
CopyArray(Dest, Source, elType^, Count)
else
Move(Source^, Dest^, Count * typeData.elSize);
end;
class function TArray.Clone<T>(const source: T): T;
var
p: PTypeInfo;
count: NativeInt;
i: Integer;
begin
p := TypeInfo(T);
if p.Kind <> tkDynArray then
raise ENotSupportedException.Create('type not supported');
count := DynArrayLength(PArray(#source)^);
DynArraySetLength(PArray(#Result)^, p, 1,#count);
CopyArrayDeep(PArray(#Result)^, PArray(#source)^, p, count);
end;

Related

C++ to Delphi dll call

I am trying to convert this code to delphi 7
LIBEXPORT int __stdcall MY_GetDLLInfo(const char **pp_version, const char **pp_release_type, const char **pp_build_date, const char **pp_load_path);
// C++ Call Example
const char *p_version = NULL;
const char *p_release_type = NULL;
const char *p_build_date = NULL;
MY_GetDLLInfo(&p_version,&p_release_type,&p_build_date,NULL);
Delphi Code
MY_GetDLLInfo: function (const pp_version:PPAnsiChar;const pp_release_type:PPAnsiChar;const pp_build_date:PPAnsiChar;const pp_load_path:PPAnsiChar): Integer; stdcall;
// Delphi Call Example
var
hHandle:THandle;
p_version,p_release_type,p_build_date,p_load_path:PPAnsiChar;
begin
hHandle := LoadLibrary(Dl_path);
#MY_GetDLLInfo:=GetProcAddress(hHandle, PChar('MY_GetDLLInfo'));
if Assigned(MY_GetDLLInfo) then begin
MY_GetDLLInfo(p_version,p_release_type,p_build_date,#null);
ShowMessage(StrPas(#p_version)); // <- I Get Strange Output
ShowMessage(StrPas(#p_release_type)); // <- I Get Strange Output
ShowMessage(StrPas(#p_build_date)); // <- I Get Strange Output
end;
end;
With my converted code i get strange output
I think i am doing wrong at using strpas ?
char* in C/C++ is PAnsiChar in Delphi, and char** is PPAnsiChar.
Your local string variables need to be declared as PAnsiChar, not as PPAnsiChar, and then you need to use the # address operator (Delphi's equivilent of C/C++'s & address operator) to pass the variables into the function.
Also, null is a Variant in Delphi. To assign a null to a pointer, you need to use nil instead.
Try this:
// Delphi Call Example
var
hHandle: THandle;
MY_GetDLLInfo: function(const pp_version, pp_release_type, pp_build_date, pp_load_path: PPAnsiChar): Integer; stdcall;
p_version, p_release_type, p_build_date: PAnsiChar;
begin
hHandle := LoadLibrary(Dl_path);
if hHandle <> 0 then
begin
#MY_GetDLLInfo := GetProcAddress(hHandle, 'MY_GetDLLInfo');
if Assigned(MY_GetDLLInfo) then
begin
p_version := nil;
p_release_type := nil;
p_build_date := nil;
MY_GetDLLInfo(#p_version, #p_release_type, #p_build_date, nil);
ShowMessage(p_version);
ShowMessage(p_release_type);
ShowMessage(p_build_date);
end;
FreeLibrary(hHandle);
end;
end;

How get correct total number of elements of a PByteArray?

On following code i'm trying convert a binary file to your correspondent bytes representation in Delphi.
I'm with difficulty to get the correct total number of elements of array (declared as PByteArray) and this returns a limited number (32767) like already discussed here. Then exists a alternative way to achieve it?
This is code:
type
TForm1 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function File2ByteArray(const FileName: string; out ByteArray: PByteArray): Boolean;
var
hFile: HWND;
dwSize, dwRead: DWORD;
begin
Result := False;
hFile := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hFile = INVALID_HANDLE_VALUE then
Exit;
dwSize := GetFileSize(hFile, nil);
ByteArray := AllocMem(dwSize);
ReadFile(hFile, ByteArray^, dwSize, dwRead, nil);
Result := True;
CloseHandle(hFile);
end;
procedure TForm1.btn1Click(Sender: TObject);
var
ByteArray: PByteArray;
MyList: TStringList;
I: Integer;
begin
if File2ByteArray(ParamStr(0), ByteArray) then
begin
try
MyList := TStringList.Create;
MyList.Add('Const');
MyList.Add(' aSize = ' + IntToStr(High(ByteArray^)) + ';');
MyList.Add('stub_: Array [' + IntToStr(Low(ByteArray^)) + ' .. ' + IntToStr(High(ByteArray^) - 1) + '] of Byte =');
MyList.Add('(');
MyList.Add('');
for I := Low(ByteArray^) to High(ByteArray^) - 1 do
MyList.Add('$' + IntToHex(ByteArray[I], 2) + ',');
MyList.Add(');');
MyList.SaveToFile('C:\File2ByteArray.txt');
finally
MyList.Free;
end;
end;
end;
end.
A pointer to an array allocated as you have done so, does not contain length information. You cannot query that pointer for the length of the array. Instead you need to modify File2ByteArray so that it returns that length, in addition to the pointer.
We can't see what type PByteArray is, but I would normally expect to see PByte used to represent a pointer to an array of bytes.
It's also worth noting that your code leaks the array. I would resolve that, and resolve the issue of length, by returning a TBytes dynamic array instead of a manually allocated array.

Setlength in Delphi 10.2.3 for array like types

Although I found some postings concerning the subject setlength I couldn't find out a solution for my problem:
With the code below - which is part of a much bigger program -
type TVektor2=array[20] of extended;
TElement2=array[20] of String;
Procedure Sort_Shell2(
element1X: TElement2; zahlX: TVektor2; var Element2X : TElement2;
var zahl2X : TVektor2);
var
bis, i, j, k, min : LongInt; l, laenge : single;
h,s,w,h1,h2, ElemX: string;
e : array[20] of String;
begin
laenge := 5; // just an example
SetLength(Element1X, 3); /// Error
//DynArraySetlength(e,l,1); /// how?
bis := High(e);
k := bis shr 1;// div 2
while (k > 0) do
begin
for i := 0 to (bis - k) do
begin
j := i;
h1 := e[j]; //I use this because before I had an Acces violation
h2 := e[j + k]; // using directly e[j] := e[j+k];
while (j >= 0) and (h1 > h2) do
begin
h := h1;
l:=zahlx[j]; //str(l:5:3,S);showmessage(h + s);
e[j] :=e[j + k];
zahlx[j] := zahlx[j+ k];
e[j + k] := h;
zahlx[j+ k]:=l;
if j > k then
Dec(j, k)
else
j := 0;
end; // {end while]
end; // { end for}
k := k shr 1; // div 2
end; // {end while}
Element2x:=e; zahl2x :=zahlx;
end;
I get the error 'incompatible types' if I try the setlength command like this.
I tried - with a for next loop -to attribute to each position of the static array (with 20 entries) or also to the correponding dynamic array and then to use setlength.
But it didn't work. Is there some casting possible to transform TElement2 to an array ? (since it is already an array!)
Why isn't it possible to use a simple static array[1..20] of strings = a, set for each position a[i] = TElement2[i] and use setlength(a,5)?
If I use DynArraySetLength(Pointer, typeInfo,dimCnt, lengthVec) what must I use for these variables?
I don't know nearly anything about Pointers and I have no idea for such problem what parameters I must use to get an array of a given length starting with the given TElement2 array. By the way, in general, is it a good idea to use dynamic arrays?
By the way there might also be an error in this sorting routine because it doesn't work well...
Can any one help me?
In order to use a Dynamic Array in Delphi you have to declare an array like this:
TElement2=array of String;
and not TElement2=array[20] of String or TElement2=array[1..20] of String;
If you declare TElement2 that way then SetLength(element1X, 3); is going to work.
moreover when you assign at the bottom of the code
Element2x:=e;
it's not going to compile unless both variable aren't declared of the same type:
e : TElement2;

Get pos and extract string in TFileStream

I am trying to find posisition of mykeyword and extract strings from a loaded file (up to 200 MB).
procedure TForm5.Button4Click(Sender: TObject);
var
Stream: TFileStream;
Buffer: array [0 .. 1023] of AnsiChar;
i: Integer;
myKeyword: string;
pullStr: AnsiString;
begin
myKeyword :='anything';
Stream := TFileStream.Create(edtTarget.Text, fmOpenRead);
while Stream.Position < Stream.Size do
begin
Stream.Read(Buffer, 1024);
m1.Lines.Add(Buffer); // no need, just display to evaluate
(* 1. Get address of given keyword *)
// i := Stream.PositionOf(myKeyword); < how to do this?
(* 2. Stream Exract *)
// pullStr := Stream.copy(i,1000); < how to do this ?
end;
end;
I have read other topics regarding file and string. I found a very good answer from here. And i think i want to expand those features.
Something like
TFileSearchReplace.GetStrPos(const KeyWord: string) : Integer;
TFileSearchReplace.ExtractStr (const KeyWord: string; Len : Integer) ;
procedure TForm5.Button4Click(Sender: TObject);
var
Stream: TFileStream;
Buffer: AnsiString;
i, BytesRead, SearchPos: Integer;
myKeyword: string;
pullStr: AnsiString;
Found: Boolean;
begin
myKeyword :='anything';
Found := False;
SetLength(Buffer, 1024);
Stream := TFileStream.Create(edtTarget.Text, fmOpenRead);
while Stream.Position < Stream.Size do
begin
// read some bytes and remember, how many bytes been read actually
BytesRead := Stream.Read(Buffer[1], 1024);
// glue new bytes to the end of the pullStr
pullStr := pullStr + copy(Buffer, 1, BytesRead);
// file is divided to two parts: before myKeyword, and after
// if myKeyword alreay found, there is nothing to do, just repeat reading to pullStr
if Found then
continue;
// if myKeyword is not found yet, pullStr acts like temporary buffer
// search for myKeyword in buffer
SearchPos := Pos(myKeyword, pullStr);
if SearchPos > 0 then
begin //keyword was found, delete from beginning up to and icluding myKeyword
// from now on, pullStr is not tmp buffer, but result
Found := True;
Delete(pullStr, 1, SearchPos + Length(myKeyWord) - 1);
continue;
end;
// myKeyword still not found. Find last line end in buffer
SearchPos := LastDelimiter(#13#10, pullStr);
// and delete everything before it
if SearchPos > 0 then
Delete(pullStr, 1, SearchPos);
// so if myKeyword spans across two reads, it still will be found in next iteration
end;
// if there is no myKeyword in file, clear buffer
if not Found then
pullStr := '';
end;

"Operator is not overloaded" when checking for a byte in an array of bytes

I have an array[0..2] of byte. I need to check if a byte is in that array or not. However, when using if ($52 in byteArray) then, I run into the error "Operator is not overloaded". I've tried setting an additional variable as a byte and then using that in the statement, but still get the error. Here's an incredibly simple program showing this:
program overloaded;
var
byteArray: array[0..2] of Byte;
begin
byteArray[0] := $00;
byteArray[1] := $69;
byteArray[2] := $52;
if ($52 in byteArray) then
writeLn('We will not get to this point');
end.
This will fail to compile with the above error in FPC 3.0.2.
You have two alternatives, overloading the operator or using for.
program ByteArrayIteration;
var
ByteArray: array[0..2] of Byte = ($00, $69, $52);
SomeByte : Byte = $52;
begin
for SomeByte in byteArray do
if SomeByte = $52 then
begin
WriteLn('We will get to this point');
Break;
end;
end.
And the overload alternative:
program OverloadInOperator;
uses SysUtils;
operator in(const A: Byte; const B: TBytes): Boolean;
var
i: Integer;
begin
Result := True;
for i := Low(B) to High(B) do if A = B[i] then Exit;
Result := False;
end;
var
Bytes : TBytes;
AByte : Byte = $52;
begin
Bytes := TBytes.Create($41, $52, $90);
if AByte in Bytes then WriteLn('Done');
end.
If your use case is limited to 255 items per array, consider using sets instead.
program SetOfByte;
var
Bytes : set of Byte = [$41, $52, $90];
AByte : Byte = $52;
begin
if AByte in Bytes then WriteLn('Done');
end.

Resources