Jebtrix
asked on
Typecasting dynamic length raw data
Using Delphi 7:
Simple situation, I want to typecast raw data using a buffer pointer and buffer length. I'm mainly looking for chars though it will be intermingled with other types. Since #0 termination byte may be in raw data, typecasting to PChar is unreliable, is there any other way to do this without iterating byte by byte. The buffer ranges from bytes to megabytes so I'm looking for something fast.
procedure GimmeData(pBuf: pointer; iBuffLen: word);
Type
TBufData = array of byte;
PTBufData = ^TBufData;
Var
BufData: PTBufData;
...
Begin
BufData := pBuf;
output(PChar(BufData^));// if only it were that easy
End
Simple situation, I want to typecast raw data using a buffer pointer and buffer length. I'm mainly looking for chars though it will be intermingled with other types. Since #0 termination byte may be in raw data, typecasting to PChar is unreliable, is there any other way to do this without iterating byte by byte. The buffer ranges from bytes to megabytes so I'm looking for something fast.
procedure GimmeData(pBuf: pointer; iBuffLen: word);
Type
TBufData = array of byte;
PTBufData = ^TBufData;
Var
BufData: PTBufData;
...
Begin
BufData := pBuf;
output(PChar(BufData^));//
End
ASKER
Thanks rllibby, another case of RTFM on my part. Good this is only a hobby. But since I'm on this subject I did at one point try to copy the contents of the buffer into an array and it kept giving me this error I can't figure out. Encore please :)
Type
TData = array of byte;
PTData = ^TData;
Var
DataDest: PTData;
DataSrc : Pointer;
Begin
DataSrc := allocmem(1000);
new(DataDest);
SetLength(TData(DataDest), 100);
DataDest := copy(TData(DataSrc),0,100) ;//[Error] incompatible types: 'Array' and 'TBufData'
...
Dispose(BufData);
FreeMem(temp);
Type
TData = array of byte;
PTData = ^TData;
Var
DataDest: PTData;
DataSrc : Pointer;
Begin
DataSrc := allocmem(1000);
new(DataDest);
SetLength(TData(DataDest),
DataDest := copy(TData(DataSrc),0,100)
...
Dispose(BufData);
FreeMem(temp);
ASKER
Correction:
//[Error] incompatible types: 'Array' and 'TData'
//[Error] incompatible types: 'Array' and 'TData'
Ok... don't mix (or confuse) delphi dynamic arrays with memory that is type cast as an array of a data type, to be shown in a second. In the example below, you will see that a block of memory can be cast with my PTData declaration, but should not be cast using a type defined as Array of Byte. These arrays (dynamic arrays) are special in that delphi allocates memory before the address of element zero to store the length of the array as well as other bits of information used to handle the dynamic array.
Regards,
Russell
type
PTData = ^TData; // You can use this to cast raw pointers with
TData = Array [0..Pred(MaxInt)] of Byte; // Dont ever declare a variable of this type, it will consume 2GB of memory
// it only serves as a means to declare a pointer type from it.
TDataArray = Array of Byte; // You should not use this to cast raw pointers with
procedure Test;
var DataDest: PTData;
DataArray: TDataArray;
DataSrc: Pointer;
dwIndex: Integer;
begin
// Create source buffer
DataSrc:=AllocMem(1000);
try
// Fill in source with some values
for dwIndex:=0 to 1000 do PTData(DataSrc)^[dwIndex]:
// Allocate memory that is type cast as byte array (note, this is not the same array of byte dynamic array)
DataDest:=AllocMem(100);
try
// Move data from source to dest
Move(DataSrc^, DataDest^, 100);
// Or using a dynamic array
SetLength(DataArray, 100);
// Move data to dynamic array
Move(DataSrc^, DataArray[0], 100);
finally
FreeMem(DataDest);
end;
finally
FreeMem(DataSrc);
end;
end;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Use SysUtils and use the PByteArray/TByteArray types... Don't use your own dynamic array.
Then again, if you do want to use dynamic arrays, do it like this:
procedure GimmeData(pBuf: pointer; iBuffLen: word);
Type
TBufData = array of byte;
Var
BufData: TTBufData;
...
Begin
SetLength(BufData, iBuffLen);
Move(pBuf, Pointer(BufData)^, iBuffLen)
...
A dynamic array is like a string. It's basically just a special kind of pointer which happens to know the amount of memory that it needs.
But rlibby did great at explaining this. ;-) I'm just summing it all up.
Then again, if you do want to use dynamic arrays, do it like this:
procedure GimmeData(pBuf: pointer; iBuffLen: word);
Type
TBufData = array of byte;
Var
BufData: TTBufData;
...
Begin
SetLength(BufData, iBuffLen);
Move(pBuf, Pointer(BufData)^, iBuffLen)
...
A dynamic array is like a string. It's basically just a special kind of pointer which happens to know the amount of memory that it needs.
But rlibby did great at explaining this. ;-) I'm just summing it all up.
>> Don't use your own dynamic array
I would be almost willing to agree EXCEPT for the fact that Borland declared it as
PByteArray = ^TByteArray;
TByteArray = array[0..32767] of Byte;
A little too short for what most people use it for. And the example above is incorrect, it should have been:
Type
TBufData = array of byte;
Var
BufData: TBufData; // not TTBufData
begin
SetLength(BufData, iBuffLen);
Move(pBuf^, Pointer(BufData)^, iBuffLen); // pBuf needs to be dereferenced
----
Russell
I would be almost willing to agree EXCEPT for the fact that Borland declared it as
PByteArray = ^TByteArray;
TByteArray = array[0..32767] of Byte;
A little too short for what most people use it for. And the example above is incorrect, it should have been:
Type
TBufData = array of byte;
Var
BufData: TBufData; // not TTBufData
begin
SetLength(BufData, iBuffLen);
Move(pBuf^, Pointer(BufData)^, iBuffLen); // pBuf needs to be dereferenced
----
Russell
Russell,
While Borland defined it as 0..32767 the reality is that if you turn off the range checking compiler option then Delphi will just ignore it's fixed length. Only with range checking enabled will you get problems if you have more than 32768 elements. In most cases you will have considerably less so this structure should even be okay.
You were right about the pBuf, btw. But you could also use this:
procedure GimmeData(var pBuf; iBuffLen: word);
The last thing is what I prefer to use in most cases anyways.
While Borland defined it as 0..32767 the reality is that if you turn off the range checking compiler option then Delphi will just ignore it's fixed length. Only with range checking enabled will you get problems if you have more than 32768 elements. In most cases you will have considerably less so this structure should even be okay.
You were right about the pBuf, btw. But you could also use this:
procedure GimmeData(var pBuf; iBuffLen: word);
The last thing is what I prefer to use in most cases anyways.
I realize this fact, but I am not the one asking the question, and you should have stated your personal preference (because thats what it is) instead of a blanket statement:
>> Don't use your own dynamic array
with nothing else to go along with it.
ASKER
Awesome answers, crystal clear now.
function GimmeData(Buffer: Pointer; Size: Integer): String;
var s: String;
begin
SetString(result, PChar(Buffer), Size);
End;
procedure TForm1.Button1Click(Sender
var p: Pointer;
s: String;
begin
p:=AllocMem(1000);
s:=GimmeData(p, 1000);
if Length(s) = 1000 then
beep
end;