Swapping order of bytes in Delphi - delphi

I'm not very familiar with arrays of bite and big/little endians but I need to write an integer value into byte array in reverse and I don't know how to do it in Delphi code. C# has BitConverter.Reverse methong which is so much easier, is there any equivalent for it in Delphi?
This is my code so far:
x := 1500977838953;
setLength(byteArray, 8);
Move(x, byteArray[2], SizeOf(x));
showMessage(ByteToHex(byteArray));
ByteToHex is a method that returns me hex string so I can read the bytes if they are in correct order. The result that I am getting is : 0000693B40795D01 but I need it to be: 00-00-01-5D-79-40-3B-69
Any ideas how I can achieve this?
Edit:
function ByteToHex(b: array of byte): String;
const HexSymbols = '0123456789ABCDEF';
var i: integer;
begin
SetLength(Result, 2*Length(b));
for i := 0 to Length(b)-1 do begin
Result[1 + 2*i + 0] := HexSymbols[1 + b[i] shr 4];
Result[1 + 2*i + 1] := HexSymbols[1 + b[i] and $0F];
end;
end;

Here is an example how to use the ReverseBytes() procedure:
program Project20;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure ReverseBytes(Source, Dest: Pointer; Size: Integer);
begin
Dest := PByte(NativeUInt(Dest) + Size - 1);
while (Size > 0) do
begin
PByte(Dest)^ := PByte(Source)^;
Inc(PByte(Source));
Dec(PByte(Dest));
Dec(Size);
end;
end;
var x,y : Int64;
begin
x := 1500977838953;
WriteLn(x);
ReverseBytes(Addr(x),Addr(y),SizeOf(x)); // Or ReverseBytes(#x,#y,SizeOf(x));
WriteLn(IntToHex(x));
WriteLn(IntToHex(y));
ReadLn;
end.
Output:
1500977838953
0000015D79403B69
693B40795D010000
To get the address of a variable, use the Addr() function or the # operator.
The result is a 64-bit integer with all bytes in reversed order, as shown by the output.
There are other ways to swap the byte order of a variable. Search for bswap for example.

Related

How do I convert a `string` of `integers` in `hexadecimal` (and back)?

I want to convert a string of integers in hexadecimal (and the opposite).
I've seen the IntToHex functions, but it uses a small integer.
For example, I need to convert the number:
999888777666555444 in hexadecimal
and then the opposite:
hexadecimal number in 999888777666555444
If you need to convert more than 8-bytes values, you can represent your very-long-integer as array of byte, word, dword or something. In that case you should just convert any particular item and concatenate results. Opposite is the same (only thing you should remember is value should be considered as right-aligned).
converting a arbitrary length buffer to hex:
function HexDump(const _Buffer; _Len: integer): string;
type
PByte = ^Byte;
var
i: integer;
p: PByte;
begin
p := #_Buffer;
Result := '';
for i := 0 to _Len - 1 do begin
Result := Result + Long2Hex2(p^);
Inc(p);
end;
end;
And the utility functions used by this:
const
/// <summary>
/// String containing all characters that can be used as digits
/// </summary>
DIGIT_CHARS: string = '0123456789ABCDEFGHIJKlMNOPQRSTUVWXYZ';
function Long2Num(_l: ULong; _Base: Byte; _MinWidth: Integer = 1): string;
var
m: Byte;
begin
Result := '';
while _l > 0 do begin
m := _l mod _Base;
_l := _l div _Base;
Result := DIGIT_CHARS[m + 1] + Result;
end;
while Length(Result) < _MinWidth do
Result := '0' + Result;
end;
function Long2Hex(_l: ULong): string;
begin
Result := Long2Num(_l, 16);
end;
function Long2Hex2(_l: ULong): string;
begin
Result := Long2Hex(_l);
if Length(Result) < 2 then
Result := '0' + Result;
end;
These functions are part of my dzlib.
Note: This does not generate the hex numbers as you might expect them, e.g. if you pass an integer to the function like this:
var
IntValue: integer;
begin
IntValue := $12345678;
s := HexDump(IntValue, SizeOf(IntValue));
end;
You end up with s = '78563412' because Intel processors store integers in little endian format.
Unfortunately the other way round is more difficult, because there is no standard arbitrary length integer type in Delphi. There are some implementations of such a type though.

array of byte absolute String: incorrect content while compiling for 64-bit-Windows

If I compile for 64-bit-Windows my Byte-Arrays haven't got the correct Input-Values.
If I compile this procedure for x32-Windows the values are correct.
Can anyone help me?
procedure doAnything(AText: String); //for example "<xml><s name="hello"/></xml>"
var
myArray:array of Byte absolute AText;
begin
... (* myArray for x32: correct Length and Values (60, 0, 120, 0, 109, 0, ...) *)
... (* myArray for x64: Length: 2 (60, 0) *)
end
The memory layout for a string is not the same as a dynamic array.
Using the absolute keyword here is plain wrong.
In 32 bit it happens that the length is read correctly, but the value is in characters, not in bytes.
You can do something like this to access the string as bytes:
procedure doAnything(AText: String); //for example "<xml><s name="hello"/></xml>"
var
pB : PByte;
i,len : Integer;
begin
pB := Pointer(AText);
len := Length(AText)*SizeOf(Char);
for i := 1 to len do
begin
WriteLn(pB^);
Inc(pB);
end;
// Or
for i := 0 to len-1 do
begin
WriteLn(pB[i]);
end;
end;
If you want to access the character data of a String as raw bytes, you have to use a type-cast instead, DO NOT use absolute as the memory layout of a String and a dynamic array are not compatible, as others have pointed out to you:
procedure doAnything(AText: String);
var
myBytes: PByte;
myBytesLen: Integer;
begin
myBytes := PByte(PChar(AText));
myBytesLen := ByteLength(AText);
// or: myBytesLen := Length(AText) * SizeOf(Char);
// use myBytes up to myBytesLen as needed...
end;
If you really wanted to use absolute, you would have to use it more like this instead:
procedure doAnything(AText: String);
var
myChars: PChar;
myBytes: PByte absolute myChars;
myBytesLen: Integer;
begin
myChars := PChar(AText);
myBytesLen := ByteLength(AText);
// or: myBytesLen := Length(AText) * SizeOf(Char);
// use myBytes up to myBytesLen as needed...
end;
From what I understand, the problem is that you are mapping apples and pears in the 64 bit world. If you look at this:
http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Dynamic_Array_Types
And the string:
http://docwiki.embarcadero.com/RADStudio/XE5/en/Internal_Data_Formats#Long_String_Types
You will see that the lengths have a different number of bytes for these two. The offsets also don't match. Basically they are not compatible.

Delphi - Convert byte array to string

How do I convert a byte array to a string (base 256) in Delphi?
Use the built-in SetString command. It sets the string to the required length and copies the bytes. There's no need for the array to be null-terminated. In fact, if the array has zero--valued bytes in it, they'll correctly appear within the string; they won't terminate the string.
SetString(AnsiStr, PAnsiChar(#ByteArray[0]), LengthOfByteArray);
If you have a UnicodeString, then you'll need to halve the length parameter since it measures characters, not bytes:
SetString(UnicodeStr, PWideChar(#ByteArray[0]), LengthOfByteArray div 2);
See also, Converting TMemoryStream to String in Delphi 2009.
I'm not sure what do you mean by Base256. If you want to get hex representation of data, use this:
function bintostr(const bin: array of byte): string;
const HexSymbols = '0123456789ABCDEF';
var i: integer;
begin
SetLength(Result, 2*Length(bin));
for i := 0 to Length(bin)-1 do begin
Result[1 + 2*i + 0] := HexSymbols[1 + bin[i] shr 4];
Result[1 + 2*i + 1] := HexSymbols[1 + bin[i] and $0F];
end;
end;
If you want to just render the data as a string (this doesn't change the content!), where for each byte of data you'd get a single ASCII symbol with that code, do
function bintoAscii(const bin: array of byte): AnsiString;
var i: integer;
begin
SetLength(Result, Length(bin));
for i := 0 to Length(bin)-1 do
Result[1+i] := AnsiChar(bin[i]);
end;
var
LString : string;
LBytes : TArray<byte>;
begin
LBytes := TArray<byte>.Create($01, $02, $03);
LString := TEncoding.ANSI.GetString(ABytes);
end;
Being GetString() the reverse operation of GetBytes().
I think there is another nice way to convert byte arrays in strings - an Indy function called BytesToString contained in IdGlobal. It also allows you to specify StartIndex, Length and TEncoding for your string. I've used it several times and I find it very useful.
function bintostr_r(const bin: array of byte): string;
var i,j:integer;
res:string ;
begin
res:='';
for i:=0 to length(bin)-1 do
begin
for j:=1 to 8 do
res:=Inttostr( ((bin[i] shr (j - 1)) and ((1 shl 1) - 1)) ) +res ;
end;
result:=res;
end;
procedure TForm1.FormCreate(Sender: TObject);
var OrigStat: array [1..6] of byte;
res:integer;
begin
OrigStat[1]:=253; // 11111101
OrigStat[2]:=252;
OrigStat[3]:=251;
OrigStat[4]:=250;
OrigStat[5]:=249;
OrigStat[6]:=248;
Edit9.text:=bintostr_r(OrigStat);
end;
result => 111110001111100111111010111110111111110011111101

(Wide)String - storing in TFileStream, Delphi 7. What is the fastest way?

I'm using Delphi7 (non-unicode VCL), I need to store lots of WideStrings inside a TFileStream. I can't use TStringStream as the (wide)strings are mixed with binary data, the format is projected to speed up loading and writing the data ... However I believe that current way I'm loading/writing the strings might be a bottleneck of my code ...
currently I'm writing length of a string, then writing it char by char ...
while loading, first I'm loading the length, then loading char by char ...
So, what is the fastest way to save and load WideString to TFileStream?
Thanks in advance
Rather than read and write one character at a time, read and write them all at once:
procedure WriteWideString(const ws: WideString; stream: TStream);
var
nChars: LongInt;
begin
nChars := Length(ws);
stream.WriteBuffer(nChars, SizeOf(nChars);
if nChars > 0 then
stream.WriteBuffer(ws[1], nChars * SizeOf(ws[1]));
end;
function ReadWideString(stream: TStream): WideString;
var
nChars: LongInt;
begin
stream.ReadBuffer(nChars, SizeOf(nChars));
SetLength(Result, nChars);
if nChars > 0 then
stream.ReadBuffer(Result[1], nChars * SizeOf(Result[1]));
end;
Now, technically, since WideString is a Windows BSTR, it can contain an odd number of bytes. The Length function reads the number of bytes and divides by two, so it's possible (although not likely) that the code above will cut off the last byte. You could use this code instead:
procedure WriteWideString(const ws: WideString; stream: TStream);
var
nBytes: LongInt;
begin
nBytes := SysStringByteLen(Pointer(ws));
stream.WriteBuffer(nBytes, SizeOf(nBytes));
if nBytes > 0 then
stream.WriteBuffer(Pointer(ws)^, nBytes);
end;
function ReadWideString(stream: TStream): WideString;
var
nBytes: LongInt;
buffer: PAnsiChar;
begin
stream.ReadBuffer(nBytes, SizeOf(nBytes));
if nBytes > 0 then begin
GetMem(buffer, nBytes);
try
stream.ReadBuffer(buffer^, nBytes);
Result := SysAllocStringByteLen(buffer, nBytes)
finally
FreeMem(buffer);
end;
end else
Result := '';
end;
Inspired by Mghie's answer, have replaced my Read and Write calls with ReadBuffer and WriteBuffer. The latter will raise exceptions if they are unable to read or write the requested number of bytes.
There is nothing special about wide strings, to read and write them as fast as possible you need to read and write as much as possible in one go:
procedure TForm1.Button1Click(Sender: TObject);
var
Str: TStream;
W, W2: WideString;
L: integer;
begin
W := 'foo bar baz';
Str := TFileStream.Create('test.bin', fmCreate);
try
// write WideString
L := Length(W);
Str.WriteBuffer(L, SizeOf(integer));
if L > 0 then
Str.WriteBuffer(W[1], L * SizeOf(WideChar));
Str.Seek(0, soFromBeginning);
// read back WideString
Str.ReadBuffer(L, SizeOf(integer));
if L > 0 then begin
SetLength(W2, L);
Str.ReadBuffer(W2[1], L * SizeOf(WideChar));
end else
W2 := '';
Assert(W = W2);
finally
Str.Free;
end;
end;
WideStrings contain a 'string' of WideChar's, which use 2 bytes each. If you want to store the UTF-16 (which WideStrings use internally) strings in a file, and be able to use this file in other programs like notepad, you need to write a byte order mark first: #$FEFF.
If you know this, writing can look like this:
Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar)
reading can look like this:
Stream1.Read(WideChar1,2);//assert returned 2 and WideChar1=#$FEFF
SetLength(WideString1,(Stream1.Size div 2)-1);
Stream1.Read(WideString1[1],(Stream1.Size div 2)-1);
You can also use TFastFileStream for reading the data or strings, I pasted the unit at http://pastebin.com/m6ecdc8c2 and a sample below:
program Project36;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes,
FastStream in 'FastStream.pas';
const
WideNull: WideChar = #0;
procedure WriteWideStringToStream(Stream: TFileStream; var Data: WideString);
var
len: Word;
begin
len := Length(Data);
// Write WideString length
Stream.Write(len, SizeOf(len));
if (len > 0) then
begin
// Write WideString
Stream.Write(Data[1], len * SizeOf(WideChar));
end;
// Write null termination
Stream.Write(WideNull, SizeOf(WideNull));
end;
procedure CreateTestFile;
var
Stream: TFileStream;
MyString: WideString;
begin
Stream := TFileStream.Create('test.bin', fmCreate);
try
MyString := 'Hello World!';
WriteWideStringToStream(Stream, MyString);
MyString := 'Speed is Delphi!';
WriteWideStringToStream(Stream, MyString);
finally
Stream.Free;
end;
end;
function ReadWideStringFromStream(Stream: TFastFileStream): WideString;
var
len: Word;
begin
// Read length of WideString
Stream.Read(len, SizeOf(len));
// Read WideString
Result := PWideChar(Cardinal(Stream.Memory) + Stream.Position);
// Update position and skip null termination
Stream.Position := Stream.Position + (len * SizeOf(WideChar)) + SizeOf(WideNull);
end;
procedure ReadTestFile;
var
Stream: TFastFileStream;
my_wide_string: WideString;
begin
Stream := TFastFileStream.Create('test.bin');
try
Stream.Position := 0;
// Read WideString
my_wide_string := ReadWideStringFromStream(Stream);
WriteLn(my_wide_string);
// Read another WideString
my_wide_string := ReadWideStringFromStream(Stream);
WriteLn(my_wide_string);
finally
Stream.Free;
end;
end;
begin
CreateTestFile;
ReadTestFile;
ReadLn;
end.

how to convert byte array to its hex representation in Delphi

I have TBytes variable with a value [0,0,15,15]. How can I convert it to "00FF" ?
I dont want to use loops, bcoz this logic to be used in time intensive function.
(I tried using BinToHex, but I could not get it working with string variable.)
Thanks & Regards,
Pavan.
// Swapping is necessary because x86 is little-endian.
function Swap32(value: Integer): Integer;
asm
bswap eax
end;
function FourBytesToHex(const bytes: TBytes): string;
var
IntBytes: PInteger;
FullResult: string;
begin
Assert(Length(bytes) = SizeOf(IntBytes^));
IntBytes := PInteger(bytes);
FullResult := IntToHex(Swap32(IntBytes^), 8);
Result := FullResult[2] + FullResult[4] + FullResult[6] + FullResult[8];
end;
If that last line looks a little strange, it's because you requested a four-byte array be turned into a four-character string, whereas in the general case, eight hexadecimal digits are required to represent a four-byte value. I'm simply assumed that your byte values are all below 16, so only one hexadecimal digit is needed. If your example was a typo, then simply replace the last two lines with this one:
Result := IntToHex(Swap32(IntBytes^), 8);
By the way, your requirement forbidding loops will not be met. IntToHex uses a loop internally.
function ByteToHex(InByte:byte):shortstring;
const Digits:array[0..15] of char='0123456789ABCDEF';
begin
result:=digits[InByte shr 4]+digits[InByte and $0F];
end;
Example :
MyHex := ByteTohex($FF);
the result
MyHex is "FF".
MyHex := ByteTohex(255);
the result
MyHex is "FF".
MyHex := ByteTohex($55);
the result
MyHex is "55".
This one is quite fast and works with any array size.. It's like BinToHex, but instead of expecting 0..255 byte values, it only uses the low nibble.
procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);
const
Convert: array[0..15] of AnsiChar = '0123456789ABCDEF';
var
I: Integer;
begin
for I := 0 to BufSize - 1 do
begin
Text[0] := Convert[Byte(Buffer[I]) and $F];
Inc(Text);
end;
end;
Assembler that does the same:
procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);assembler;
asm
PUSH ESI
PUSH EDI
MOV ESI,EAX
MOV EDI,EDX
MOV EDX,0
JMP ##1
##0: DB '0123456789ABCDEF'
##1: LODSB
AND DL,AL
AND DL,0FH
MOV AL,##0.Byte[EDX]
STOSB
DEC ECX
JNE ##1
POP EDI
POP ESI
end;
usage:
type THexDigit=0..15;
const ArSize=16;
var Ar:array[0..Pred(ArSize)] of THexDigit=(0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3);
S:Array[0..Pred(ArSize)] of AnsiChar;
BinToSingleHex(#Ar,S,Length(Ar));
WriteLn(S);
Bit late to the party but why not a simple lookup table?
const
HexChars : Array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
Assuming TBytes values of 0..15
Function (ABytea: TBytes): string
begin
Result := HexChars[ABytea[0]];
Result := Result + HexChars[ABytea[1]];
Result := Result + HexChars[ABytea[2]];
Result := Result + HexChars[ABytea[3]];
end;
of course neater with a loop :) and needs modifying for byte values above 15:
begin
Result := HexChars[ABytea[0] shr 4];
Result := Result + HexChars[ABytea[0] and $0F];
Result := Result + HexChars[ABytea[1] shr 4];
Result := Result + HexChars[ABytea[1] and $0F];
Result := Result + HexChars[ABytea[2] shr 4];
Result := Result + HexChars[ABytea[2] and $0F];
Result := Result + HexChars[ABytea[3] shr 4];
Result := Result + HexChars[ABytea[3] and $0F];
end;
Still neater with a loop especially if TBytes gets larger
I had the same problem. My solution using System.SysUtils.TByteHelper.ToHexString (with loop)
function ToHexString(const MinDigits: Integer): string; overload; inline;
Example code:
procedure TForm1.Button2Click(Sender: TObject);
begin
var text:string;
var w:integer:=0;
var bytearray: Tarray<byte>:= [$DE, $AD, $BE, $EF];
repeat
text:= text+ pbyte(#bytearray[w])^.ToHexString(2);
inc(w);
until w >= high(bytearray);
end;
bytearray := $DE $AD $BE $EF
text := DEADBEEF

Resources