views:

2071

answers:

4

Hi,

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.

+2  A: 
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".
joe
it is TBytes and not a single byte.. and i dont want to use loops to compute each and every value. If I am not wrong, your logic works per byte..
Pavan
This looks like the best solution you're going to get. You're gonna have to use a loop somewhere. If you want to use it on a TBytes, wrap this in a function that takes a TBytes and loops through it, building the output string one byte at a time.
Mason Wheeler
This could be adapted to a "no loop" solution. If you really care about that requirement.
Nosredna
+5  A: 
// 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.

Rob Kennedy
IntToHex(IntBytes^, 8); generates the string in reverse order. If you see my question i need it as "00FF", but the above function generates it as "FF00" .. Can i specify byte order ?
Pavan
Result := FullResult[8] + FullResult[6] + FullResult[4] + FullResult[2];
Nosredna
is there any way to specify byte order instead of accessing it element by element..??
Pavan
Sorry. I forgot that x86 is little-endian, so you need to swap the byte order.
Rob Kennedy
@Pavan: couldn't you simply add a boolean byte order parameter to the function header, then use the last line from Rob's original answer and Nosredna's comment as alternatives in an if statement?
Argalatyr
+2  A: 

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);
Wouter van Nifterick
Beware. Your result string is not null-terminated. Also, your code requires the "typed @ operator" compiler directive to be disabled because you pass a byte pointer to a parameter expecting an AnsiChar pointer. Better to change the first parameter's type to PByte. And how do you figure that you need to use "and" to clear the upper four bits in Delphi but you don't need to in assembler?
Rob Kennedy
Good points.. and yeah, you're right.. of course it's still needed to mask most significant 4 bits there. fixed that
Wouter van Nifterick
A: 

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

Despatcher