views:

158

answers:

2

I want to know how to convert big-endian numbers to native numbers in Delphi. I am porting some C++ code in that I came across:

unsigned long blockLength  = *blockLengthPtr++ << 24;
blockLength |= *blockLengthPtr++ << 16;
blockLength |= *blockLengthPtr++ << 8;
blockLength |= *blockLengthPtr;

unsigned long dataLength  = *dataLengthPtr++ << 24;
dataLength |= *dataLengthPtr++ << 16;
dataLength |= *dataLengthPtr++ << 8;
dataLength |= *dataLengthPtr;

I am not familiar with C++, so I don't understand what those operators do.

+1  A: 

To reverse the order of the bits:

procedure SwapEndiannessOfBits(var Value: cardinal);
var
  tmp: cardinal;
  i: Integer;
begin
  tmp := 0;
  for i := 0 to 8*sizeof(Value) - 1 do
    inc(tmp, ((Value shr i) and $1) shl (8*sizeof(Value) - i - 1));
  Value := tmp;
end;

To reverse the order of the bytes:

procedure SwapEndiannessOfBytes(var Value: cardinal);
var
  tmp: cardinal;
  i: Integer;
begin
  tmp := 0;
  for i := 0 to sizeof(Value) - 1 do
    inc(tmp, ((Value shr (8*i)) and $FF) shl (8*(sizeof(Value) - i - 1)));
  Value := tmp;
end;

I think the last one is what you are looking for. Most likely there are faster and more elegant solutions, though.

Disclaimer: I might be totally wrong. I feel a bit confused at the moment. Hopefully someone else will see this question and provide a more definite answer!

Andreas Rejbrand
+13  A: 

Andreas's answer is a pretty good example of how to do it in pure pascal, but it still looks kinda awkward, just like the C++ code. This can actually be done in a single assembly instruction, though which one depends on whether you're using 32-bit or 16-bit integers:

function SwapEndian(Value: integer): integer; register; overload;
asm
  bswap eax
end;

function SwapEndian(Value: smallint): smallint; register; overload;
asm
  xchg  al, ah
end;
Mason Wheeler
You don't need to specify the calling convention, at least not for Delphi (don't know about Free Pascal). `register` is the default.
Michael Madsen
This is *the* solution, then!
Andreas Rejbrand
You don't *need* to specify the calling convention, @Michael, but it's useful to include it in assembler functions like this since they *require* a specific calling convention in order to work properly.
Rob Kennedy
Yeah, basically what Rob said. I know it's the default calling convention, and putting `register;` there is optional, but stating it explicitly makes it clear why you're operating on the (E)AX register.
Mason Wheeler
It's just a pity Delphi can't inline assembler code.
ldsandon
This is a bad example of using the overload directive that should be avoided. The Integer and SmallInt types are very close and converted implicitely by the compiler. You can keep SmallInt value in Integer variable (that is a common practice on 32-bit platform), and you are very likely to make a bug that is difficult to find with the overloaded SwapEndian. The dedicated names (ex SwapEndian16 and SwapEndian32) are more safe and better.
Serg