Recent

Author Topic: Code for big endian and little endian  (Read 373 times)

LemonParty

  • Hero Member
  • *****
  • Posts: 616
Code for big endian and little endian
« on: August 05, 2026, 09:30:31 am »
I have this code that should turn a 32 byte value into hex string:
Code: Pascal  [Select][+][-]
  1. type
  2.   TSessionID = array [0..31] of Byte;
  3.  
  4. function ToHex(const SID: TSessionID): String;
  5. begin
  6.   Result:=
  7.     HexStr(PQWord(@SID[0])^, 16) +
  8.     HexStr(PQWord(@SID[8])^, 16) +
  9.     HexStr(PQWord(@SID[16])^, 16) +
  10.     HexStr(PQWord(@SID[24])^, 16);
  11. end;
I have feeling that it will not work the same way on big endian architectures.
What is the code that gives the same output on little endian and big endian architectures?
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Tomxe

  • Full Member
  • ***
  • Posts: 146
Re: Code for big endian and little endian
« Reply #1 on: August 05, 2026, 09:45:11 am »
Slower, but same result on BE and LE.

Code: Pascal  [Select][+][-]
  1. function ToHex(const SID: TSessionID): String;
  2. var i: Integer;
  3. begin
  4. Result := '';
  5. For i:=0 to 31 do
  6.   Result := Result + IntToHex(SID[i], 2);
  7. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 19614
  • Glad to be alive.
Re: Code for big endian and little endian
« Reply #2 on: August 05, 2026, 09:51:10 am »
The code is subtly wrong for little endian, not big endian. You can fix it like this:
Code: Pascal  [Select][+][-]
  1. function ToHex(const SID: TSessionID): String;
  2. begin
  3.   Result :=
  4.     HexStr(NtoBE(PQWord(@SID[0])^), 16) +
  5.     HexStr(NtoBE(PQWord(@SID[8])^), 16) +
  6.     HexStr(NtoBE(PQWord(@SID[16])^), 16) +
  7.     HexStr(NtoBE(PQWord(@SID[24])^), 16);
  8. end;
Which will give you the same result on LE and BE.

Explanation:
SID[n] becomes the least-significant byte of the QWord, SID[n+7] the most significant. HexStr then prints MSB-->LSB, so each 8-byte group comes out byte-reversed relative to the array. SID = [$12,$34,$56,...] prints as if the bytes ran ...,$56,$34,$12.

My solution is coherent with network protocols/ wire transfers and on BE, NToBE is a no-op.

[edit] verified by Claude, code is correct. Explanation is correct. Sec.Op. CoPilot: same.
« Last Edit: August 05, 2026, 10:15:45 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

LemonParty

  • Hero Member
  • *****
  • Posts: 616
Re: Code for big endian and little endian
« Reply #3 on: August 05, 2026, 10:29:42 am »
Great! Thank you.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

LemonParty

  • Hero Member
  • *****
  • Posts: 616
Re: Code for big endian and little endian
« Reply #4 on: August 05, 2026, 11:25:54 am »
I modified code so it can be used on architectures where alignment is required:
Code: Pascal  [Select][+][-]
  1. function ToHex(constref SID: TSessionID): String;
  2. {$IfDef FPC_REQUIRES_PROPER_ALIGNMENT}
  3. type
  4.   T2Chars = array [0..1] of Char;
  5.   P2Chars = ^T2Chars;
  6. var
  7.   i: SizeUInt;
  8. {$EndIf}
  9. begin
  10.   {$IfDef FPC_REQUIRES_PROPER_ALIGNMENT}
  11.   if (PtrUInt(@SID[0]) and 7) = 0 then
  12.     Result:=
  13.       HexStr(NtoBE(PQWord(@SID[0])^), 16) +
  14.       HexStr(NtoBE(PQWord(@SID[8])^), 16) +
  15.       HexStr(NtoBE(PQWord(@SID[16])^), 16) +
  16.       HexStr(NtoBE(PQWord(@SID[24])^), 16)
  17.   else begin
  18.     SetLength(Result, Length(SID) * 2);
  19.     for i:= 0 to High(SID) do
  20.       P2Chars(@Result[i * 2 + 1])^:= HexStr(SID[i], 2);
  21.   end;
  22.   {$Else}
  23.   Result:=
  24.     HexStr(NtoBE(PQWord(@SID[0])^), 16) +
  25.     HexStr(NtoBE(PQWord(@SID[8])^), 16) +
  26.     HexStr(NtoBE(PQWord(@SID[16])^), 16) +
  27.     HexStr(NtoBE(PQWord(@SID[24])^), 16);
  28.   {$EndIf}
  29. end;
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Thaddy

  • Hero Member
  • *****
  • Posts: 19614
  • Glad to be alive.
Re: Code for big endian and little endian
« Reply #5 on: August 05, 2026, 11:57:29 am »
That is wrong: network transfer protocols have no alignment: CPU's do. Transfer protocols are byte aligned. So you introduced a possible bug. You can't gain anything here.
What you did is making it dependent on the array alignment, not the intended byte alignment.
Your intention was good, but it leads to problems where you need byte aligned data, which is what you were trying to achieve.
I can give you an example why this is wrong. Give me a few minutes...

A few checks later:
Your alignment‑dependent version fails whenever the 32‑byte array begins at an unaligned address — which is extremely common in real protocol parsing. The aligned and unaligned paths produce different hex strings.

The correct version should look like my first example, or, when you still are nor sure:
Code: Pascal  [Select][+][-]
  1. function ToHex(constref SID: TSessionID): string;
  2. var
  3.   qw: QWord;
  4.   i: Integer;
  5. begin
  6.   Result := '';
  7.   for i := 0 to 3 do
  8.   begin
  9.     Move(SID[i*8], qw, SizeOf(qw));   // safe on all architectures
  10.     Result := Result + HexStr(NtoBE(qw), 16);
  11.   end;
  12. end;
But I guess, you simply do not want that in network related code. You introduced a bug that is hard to debug. Give some more details later. Have to test that...
« Last Edit: August 05, 2026, 12:12:41 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

LemonParty

  • Hero Member
  • *****
  • Posts: 616
Re: Code for big endian and little endian
« Reply #6 on: August 05, 2026, 02:50:59 pm »
I just tested it with this program:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. {$Define FPC_REQUIRES_PROPER_ALIGNMENT}{for testing}
  4.  
  5. type
  6.   TSessionID = array [0..31] of Byte;
  7.   PSessionID = ^TSessionID;
  8.        
  9. function ToHex(constref SID: TSessionID): String;
  10. {$IfDef FPC_REQUIRES_PROPER_ALIGNMENT}
  11. type
  12.   T2Chars = array [0..1] of Char;
  13.   P2Chars = ^T2Chars;
  14. var
  15.   i: SizeUInt;
  16. {$EndIf}
  17. begin
  18.   {$IfDef FPC_REQUIRES_PROPER_ALIGNMENT}
  19.   if (PtrUInt(@SID[0]) and 7) = 0 then
  20.     Result:=
  21.       HexStr(NtoBE(PQWord(@SID[0])^), 16) +
  22.       HexStr(NtoBE(PQWord(@SID[8])^), 16) +
  23.       HexStr(NtoBE(PQWord(@SID[16])^), 16) +
  24.       HexStr(NtoBE(PQWord(@SID[24])^), 16)
  25.   else begin
  26.     SetLength(Result, Length(SID) * 2);
  27.     for i:= 0 to High(SID) do
  28.       P2Chars(@Result[i * 2 + 1])^:= HexStr(SID[i], 2);
  29.   end;
  30.   {$Else}
  31.   Result:=
  32.     HexStr(NtoBE(PQWord(@SID[0])^), 16) +
  33.     HexStr(NtoBE(PQWord(@SID[8])^), 16) +
  34.     HexStr(NtoBE(PQWord(@SID[16])^), 16) +
  35.     HexStr(NtoBE(PQWord(@SID[24])^), 16);
  36.   {$EndIf}
  37. end;
  38.  
  39. var
  40.   SID: TSessionID = (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);
  41.   p: PByte;
  42. begin
  43.   p:= GetMem(SizeOf(TSessionID) + 1);
  44.   Move(SID, PByte(p + 1)^, SizeOf(TSessionID));
  45.   Writeln(ToHex(SID), LineEnding, ToHex(PSessionID(p + 1)^));
  46. end.
The output is identical.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

Thaddy

  • Hero Member
  • *****
  • Posts: 19614
  • Glad to be alive.
Re: Code for big endian and little endian
« Reply #7 on: August 05, 2026, 04:50:23 pm »
You forgot the border case I mentioned: It is wrong. The reply is rather lengthy, (with all tests) so I will send it in private first. Then you can decide to publish it.
Any "programmer" that knows only one programming language is not a programmer

 

TinyPortal © 2005-2018