ну если вдруг не только в качестве задачки... оптимизированно на скорость, никаких проверок нет.
{
Author: <EMIT \<a href="mailto:bachin@webceo.com"\>Oleg A. Bachin\</a\>>
Version: $Revision: 3 $ $Modtime: 14.11.02 10:44 $
}
unit HexUtils;
{$IFOPT O+}
{$DEFINE OPTIMIZATION_ON}
{$ENDIF}
interface
// Конвертация в бинарную строку из шестнадцатиричного фармата
function HexToBin(Source: Pointer; Len: Integer): string; overload;
function HexToBin(Source: string): string; overload;
// Конвертация бинарной строки в шестнадцатиричный фармат
function BinToHex(Source: Pointer; Len: Integer): string; overload;
function BinToHex(Source: string): string; overload;
implementation
function HexToBin(Source: Pointer; Len: Integer): string;
const
_Hex: array [0..255] of Byte =
(
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 01,
02, 03, 04, 05, 06, 07, 08, 09, 00, 00,
00, 00, 00, 00, 00, 10, 11, 12, 13, 14,
15, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 10, 11, 12, 13,
14, 15, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00
);
{$IFNDEF OPTIMIZATION_ON}
procedure _HexToBin(Source, Destination: Pointer; Count: Integer);
var
Src, Dest: ^Byte;
begin
Src := Source;
Dest := Destination;
while (Count > 0) do
begin
Dest^ := _Hex[Src^] shl 4;
Inc(Src);
Dest^ := Dest^ or _Hex[Src^];
Inc(Src);
Inc(Dest);
Dec(Count);
end;
end;
{$ELSE}
procedure _HexToBin(Source, Destination: Pointer; Count: Integer); assembler; register;
asm
PUSH ESI // Store registers
PUSH EDI
PUSH EBP
MOV ESI, EAX // Source address
MOV EDI, EDX // Destination address
MOV EBP, OFFSET _HEX // Hex table address
XOR EAX, EAX
@@Loop:
LODSB // High
MOV DL, [EBP + EAX]
SHL DL, 4
LODSB // Low
MOV AL, [EBP + EAX]
OR AL, DL
STOSB // Result
DEC ECX
JNZ @@Loop
POP EBP // Restore registers
POP EDI
POP ESI
end;
{$ENDIF}
begin
SetLength(Result, Len);
_HexToBin(Source, @Result[1], Len);
end;
function HexToBin(Source: string): string;
var
Len: integer;
begin
Len := Length(Source) shr 1;
Result := HexToBin(@Source[1], Len);
end;
function BinToHex(Source: Pointer; Len: Integer): string;
const
Hex_: array [0..15] of Byte =
(
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
65, 66, 67, 68, 69, 70
);
{$IFNDEF OPTIMIZATION_ON}
procedure _BinToHex(Source, Destination: Pointer; Count: Integer); assembler; register;
var
Src, Dest: ^Byte;
C: Byte;
begin
Src := Source;
Dest := Destination;
while (Count > 0) do
begin
C := Src^;
Dest^ := Hex_[C shr 4];
Inc(Dest);
Dest^ := Hex_[Src^ and 15];
Inc(Dest);
Inc(Src);
Dec(Count);
end;
end;
{$ELSE}
procedure _BinToHex(Source, Destination: Pointer; Count: Integer); assembler;
(*
EAX = Source
EDX = Destination
ECX = Count
*)
asm
PUSH ESI // Store registers
PUSH EDI
PUSH EBP
MOV ESI, EAX // Source address
MOV EDI, EDX // Destination address
MOV EBP, OFFSET HEX_ // Hex table address
XOR EAX, EAX
XOR EDX, EDX
@@Loop:
LODSB
MOV DL, AL
SHR AL, 4
MOV AL, [EBP + EAX]
STOSB // Result
AND DL, 15
MOV AL, [EBP + EDX]
STOSB // Result
DEC ECX
JNZ @@Loop
POP EBP // Restore registers
POP EDI
POP ESI
end;
{$ENDIF}
begin
SetLength(Result, Len shl 1);
_BinToHex(Source, @Result[1], Len);
end;
function BinToHex(Source: string): string;
var
Len: integer;
begin
Len := Length(Source);
Result := BinToHex(@Source[1], Len);
end;
end.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Oleg A. Bachin, Вы писали:
OAB>ну если вдруг не только в качестве задачки... оптимизированно на скорость, никаких проверок нет.
Я тоже в свое время развлекался, но недооптимизировал, да и
инлайн-асм наложил свои диррективы....
Если будет интересно
#pragma once
//! Конвертация заданного байта в его строково-шестнадцатиричное представление.
/*!
* \date 2005
*
* \param[in,out] al обрабатываемый байт
*
* \retval ah строково-шестнадцатиричное представление старшей тетрады;
* \retval al строково-шестнадцатиричное представление младшей тетрады.
*
* \warning
* ЭТО НЕТРИВИАЛЬНАЯ ФУНКЦИЯ.
*
* Пролог и эпилог реализованы вручную с целью уменьшения накладных
* расходов на вызов (часть стандартного пролога и эпилога
* закомментирована с демонстрационной целью ).
*
* Накладные расходы: ближний call.
*
* На самом деле это не С++ функция, а скорее подпрограмма ассемблера.
*
* \note
* Обработка двух тетрад за раз ( дублирующийся код ) производится исключительно
* с целью уменьшения накладных расходов (можно было вынести отдельно).
*/
void __declspec( naked ) cvtByte2strhex( void )
{
__asm
{
// ---------- prolog
//push ebp ; Save ebp
//mov ebp, esp ; Set stack frame pointer
//sub esp, __LOCAL_SIZE
mov ah, al
// ---------- обработка первого символа
shr ah, 4
cmp ah, 10
jb less_then_10_1
add ah, 'A' - 10
jmp next_1
less_then_10_1:
or ah, '0'
next_1:
// ---------- обработка второго символа
and al, 0x0F // обнуление старшей тетрады
cmp al, 10
jb less_then_10_2
add al, 'A' - 10
jmp next_2
less_then_10_2:
or al, '0'
next_2:
// ---------- epilog
//mov esp, ebp ; Restore stack pointer
//pop ebp ; Restore ebp
ret ; Return from function
}
}
//! Конвертация заданного слова содержащего строково-шестнадцатиричное значение в двоичное значение.
/*!
* \date 2005
*
* \param[in,out] ax обрабатываемое слово
*
* \retval al двоичное значение.
*
* \warning
* ЭТО НЕТРИВИАЛЬНАЯ ФУНКЦИЯ.
*
* Пролог и эпилог реализованы вручную с целью уменьшения накладных
* расходов на вызов (часть стандартного пролога и эпилога
* закомментирована с демонстрационной целью )
*
* Накладные расходы: ближний call
*
* На самом деле это не С++ функция, а скорее подпрограмма ассемблера.
*
* \note
* Обработка слова ( дублирующийся код ) производится исключительно
* с целью уменьшения накладных расходов (можно было вынести отдельно).
*/
void __declspec( naked ) cvtstrhexword2byte( void )
{
__asm
{
// ---------- prolog
//push ebp ; Save ebp
//mov ebp, esp ; Set stack frame pointer
//sub esp, __LOCAL_SIZE
// ---------- обработка первого символа
cmp al, '9'
jbe only_digits1
and al, 0xDF // Принудительное преобразование в UpperCase
sub al, 7
only_digits1:
sub al, '0'
shl al, 4
// ---------- обработка второго символа
cmp ah, '9'
jbe only_digits2
and ah, 0xDF // Принудительное преобразование в UpperCase
sub ah, 7
only_digits2:
sub ah, '0'
// ---------- получить в AL - сконвертированное значение байта
or al, ah
// ---------- epilog
//mov esp, ebp ; Restore stack pointer
//pop ebp ; Restore ebp
ret ; Return from function
}
}
//! Преобразование числового значения в строково-шестнадцатиричное.
/*!
* \date 2005
*
* \param[in] sourceVal конвертируемое значение;
* \param[in,ou] pDest буфер принимающий сконвертированное значение;
* \param[in] sizeOfSourceVal размер конвертируемого значения.
*
* \return pDest
*
* Данная реализация поддерживает преобразование числовых значений следующих типов:
* - char;
* - short;
* - long.
*
* \note
* Значение третьего аргумента (\e sizeOfSourceVal) выводится автоматически.
* Это крайне необходимо, поскольку я не нашел другого способа получения размера \e T.
*
* \sa
* cvtFromStrHex2Value()
*/
template< typename T >
char* cvtValue2StrHex( const T& sourceVal, char* pDest, const size_t sizeOfSourceVal = sizeof( T ) )
{
__asm
{
push ecx
push edx
push esi
// EDX - исходное значение
mov ecx, DWORD PTR sourceVal
mov edx, [ecx]
// ECX - переменная цикла, равна кол-ву байт источника
mov ecx, sizeOfSourceVal
// ESI - адрес результата
mov esi, pDest
main_loop:
mov al, dl
call cvtByte2strhex
// ----------
shr edx, 8
// HACK: инструкция mov - не изменяет флаг ZF
dec cx
// ---------- сохранение результатов
mov [esi + ecx * 2 + 1], al
mov [esi + ecx * 2], ah
jnz main_loop
pop esi
pop edx
pop ecx
mov eax, pDest
}
}
//! Преобразование строково-шестнадцатиричного значения в двоичное.
/*!
* \date 2005
*
* \param[in] pSourse буфер содержащий конвертируемое значение;
* \param[in,out] destVal двоичное(сконвертированное) значение;
* \param[in] sizeOfDestVal размер двоичного(сконвертированного) значения.
*
* \return destVal
*
* Данная реализация поддерживает преобразование в значения следующих типов:
* - char;
* - short;
* - long.
*
* \note
* Регистр символов - значения не имеет.
*
* \warning
* Функция не производит проверки на правильность значений:
* в случае наличия в строке символа не принадлежащего
* диапазону '0'-'9' && 'A'-'F' - последствия не определены.
*
* \note
* Значение третьего аргумента (\e sizeOfDestVal) выводится автоматически.
* Это крайне необходимо, поскольку я не нашел другого способа получения размера \e T.
*
* \sa
* cvtValue2StrHex()
*/
template< typename T >
T& cvtFromStrHex2Value( const char* pSourse, T& destVal, const size_t sizeOfDestVal = sizeof( T ) )
{
__asm
{
// Сохраняем регистры
push ecx
push edx
push esi
// ECX - переменная цикла, равна кол-ву байт результата
mov ecx, sizeOfDestVal
// EDX - адрес результата
mov edx, DWORD PTR destVal
// ESI - адрес обрабатываемого символа
mov esi, pSourse
main_loop:
// ---------- сдвиг результата на 8 бит, в зависимости от типа результата
cmp sizeOfDestVal, 1
jne dest_not_char
shl BYTE PTR [edx], 8 // результат типа 'char'
jmp after_shift_dest
dest_not_char:
cmp sizeOfDestVal, 2
jne dest_not_short
shl WORD PTR [edx], 8 // результат типа 'short'
jmp after_shift_dest
dest_not_short:
shl DWORD PTR [edx], 8 // результат типа 'long'
after_shift_dest:
// ---------- загрузить в AX обрабатываемое слово
mov ax, [ esi ]
add si, 2
call cvtstrhexword2byte
// ---------- накопление результата
or BYTE PTR [edx],al
loop main_loop
// Поместить в EAX destVal
mov eax, edx
// Восстанавливаем регистры
pop esi
pop edx
pop ecx
}
}
... << RSDN@Home 1.1.4 stable rev. 510>>