Re[3]: реализация HEX atoi
От: Oleg A. Bachin Украина  
Дата: 07.07.05 07:33
Оценка:
ну если вдруг не только в качестве задачки... оптимизированно на скорость, никаких проверок нет.

{
  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>>
Best regards,
Oleg A. Bachin
Re[3]: реализация HEX atoi
От: MaximE Великобритания  
Дата: 07.07.05 07:53
Оценка:
Chez wrote:

> ME>А чем strtol не устраивает?

> Это не то
> 1) если префикса 0x нет — рассматривает число как десятичное

Представь, что число идет тебе из командной строки. Как ты различишь без префикса decimal от hex?

> 2) возвращает другие результаты в случае ошибок, а надо:

>

> 4. Возвращаемое значение — либо 0 в случае ошибки, либо часть числа до момента встречи ошибки (например, если строка содержит значение "0xA0jFD", то возвращаемым значением будет число 160


Это похоже на то, что делает strtol. http://www.opengroup.org/onlinepubs/009695399/functions/strtol.html

> 3) но самое главное, что задание было поставлено просто как разминка для мозгов !

>

> 1. Запрещается пользоваться любыми библиотечными функциями (типа strlen и пр.), а также сторонними классами и иже с ними. В двух словах, задача должна решаться встроенными средствами языка С/С++.
> ...
> 8. Запрещается использовать операторы continue и goto в цикле.
> 9. Количество вспомогательных переменных ограничено тремя.
> 10. Использование конструкции switch, а также цикла do...while (но не while — его можно) запрещается.


Что за идиот это выдумал?

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 1.9
Re[4]: реализация HEX atoi
От: Chez Россия  
Дата: 07.07.05 09:03
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Chez wrote:


>> ME>А чем strtol не устраивает?

>> Это не то
>> 1) если префикса 0x нет — рассматривает число как десятичное
ME>Представь, что число идет тебе из командной строки. Как ты различишь без префикса decimal от hex?
Это понятно.
Но функция, которую я написал, принимает только hex, по условию. И если нет 0x — то рассматривает число как hex, в отличие от strtol.

>> 3) но самое главное, что задание было поставлено просто как разминка для мозгов !

>>

>> 1. Запрещается пользоваться любыми библиотечными функциями (типа strlen и пр.), а также сторонними классами и иже с ними. В двух словах, задача должна решаться встроенными средствами языка С/С++.
>> ...
>> 8. Запрещается использовать операторы continue и goto в цикле.
>> 9. Количество вспомогательных переменных ограничено тремя.
>> 10. Использование конструкции switch, а также цикла do...while (но не while — его можно) запрещается.

ME>Что за идиот это выдумал?
Здесь!
Автор: Flamer
Дата: 29.08.02

Я просто выполнил поставленное задание, т.к. делать было больше нечего.

Chez, ICQ#161095094

Posted via:RSDN@Home;version:1.1.3;muzikstamp:silent

Re[4]: реализация atoi вида NNNNNc
От: Кодт Россия  
Дата: 07.07.05 10:37
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Представь, что число идет тебе из командной строки. Как ты различишь без префикса decimal от hex?


О! Новая задачка. Представьте себе, что для различения используется не префикс, а суффикс (b/inary,d/ecimal,h/ex).

1. Напишите конвертер, на входе которого — строка в указанном формате (возможно, неправильная), на выходе — число.

2. Напишите конвертер, на входе которого — телетайп (выдающий символы поштучно). Попробуйте обойтись без буферизации в строку.
Перекуём баги на фичи!
Re[3]: PRB: реализация HEX atoi
От: Erop Россия  
Дата: 07.07.05 13:22
Оценка:
Здравствуйте, Vampire, Вы писали:


V>
V>int hex_atoi(char *s)
V>{
V>    int sign=1, val=0;
V>    if (s) while ((*s == ' ' || *s == '\t') && *(++s)); else return 0;
V>    sign*=(*s == '-' && s++)?-1:((*s == '+' && s++), 1);
V>    if ((*s == '0') && (*(++s) == 'x')) s++;
V>    while ((*s>='0' && *s<='9') || (*s>='A' && *s<='F') || (*s>='a' && *s<='f'))
V>    {
V>        val*=16;
V>        if (*s>='0' && *s<='9') val+=*s++ - 48; 
V>        if (*s>='a' && *s<='f') val+=*s++ - 'a' + 10; 
V>        if (*s>='A' && *s<='F') val+=*s++ - 'A' + 10; 
V>    }
V>    return val*sign;
V>}
V>




А между прочим ошибка:

строчка "1bC", попав на вход этой функции породит ответ 1 + 11 + 12 = 24, а вовсе и не то, что надо

Всё-таки условия конкурса не совсем удачные. Я бы лучше читабельности и надёжности хотел, а не ребусов на C++
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: реализация atoi вида NNNNNc
От: StDenis Россия  
Дата: 13.07.05 13:55
Оценка: 10 (1)
Здравствуйте, Кодт, Вы писали:

К>О! Новая задачка. Представьте себе, что для различения используется не префикс, а суффикс (b/inary,d/ecimal,h/ex).

а где ответы? тихо как-то...
можно я лишь алгоритм?
— конвертируем строку в число, используя максимально возможную базу (hex в данном случае) [база1]
— попутно отмечаем, цифры каких баз встретились (максимум)
— когда доходим до последнего символа [база2] — проверяем, все ли цифры, нам встретившиеся, соответствуют базе2
— теперь вместо строки разбираем число:

мне просто интересно знать: правильные ли у мня мысли
Re[6]: реализация atoi вида NNNNNc
От: DK3981 Россия  
Дата: 13.07.05 14:59
Оценка: +1
SD>- конвертируем строку в число, используя максимально возможную базу (hex в данном случае) [база1]
SD>- попутно отмечаем, цифры каких баз встретились (максимум)
SD>- когда доходим до последнего символа [база2] — проверяем, все ли цифры, нам встретившиеся, соответствуют базе2
SD>- теперь вместо строки разбираем число:
SD>
Так на каком-нибудь длинном binary можно и переполнение получить. Надо ещё смотреть на длину пропарсенной строки, если она превосходит порог — понижать базу1, попутно пересчтывая число.
... << RSDN@Work 1.1.3 stable >>
Pink Floyd — Wearing The Inside Out
Standarts are great, everyone should have one!
Re[7]: реализация atoi вида NNNNNc
От: Кодт Россия  
Дата: 13.07.05 15:15
Оценка:
Здравствуйте, DK3981, Вы писали:

SD>>- конвертируем строку в число, используя максимально возможную базу (hex в данном случае) [база1]

SD>>- попутно отмечаем, цифры каких баз встретились (максимум)
SD>>- когда доходим до последнего символа [база2] — проверяем, все ли цифры, нам встретившиеся, соответствуют базе2
SD>>- теперь вместо строки разбираем число:
SD>>
DK>Так на каком-нибудь длинном binary можно и переполнение получить. Надо ещё смотреть на длину пропарсенной строки, если она превосходит порог — понижать базу1, попутно пересчтывая число.


Можно и с другого конца начать. Изначально установить базу = 2. Каждый раз, встречая неожиданную цифру, повышать базу. Это произойдёт не более 2 раз, поскольку в условии говорилось про (2,10,16). Или не более 3, если добавить восьмеричную.
Ну а если уж на текущей маленькой базе получили переполнение, то с нерассмотренными ещё большими — тем более будет.
Перекуём баги на фичи!
Re[4]: реализация HEX atoi
От: srggal Украина  
Дата: 20.07.05 20:42
Оценка:
Здравствуйте, 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>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.