алгоритм crc16 от C ++ до bash - PullRequest
       45

алгоритм crc16 от C ++ до bash

0 голосов
/ 19 декабря 2011

Я пытаюсь реализовать контрольную сумму CRC16 в bash.Я портирую из существующего фрагмента кода C ++.Я почти там, но я получаю разные ответы.

Я не совсем понимаю, почему контрольные суммы между кодом C ++ и сценарием bash отличаются.

Другой набор глаз будеточень помогите.

Вот код C ++:

uint16_t Encoder::checksum(std::string thestring)
{
    uint8_t d, e, f;
    uint16_t c, r, crccalc;
    c = 0xffff;

    for (unsigned int i = 0; i < thestring.length(); i++)
    {
        d = thestring[i];
        e = c ^ d;
        f = e ^ (e << 4);
        r = (c >> 8) ^ (f << 8) ^ (f << 3) ^ (f >> 4);
        c = r;
    }
    c ^= 0xffff;
    crccalc = c;
    return crccalc;
}

А вот мой код bash:

function calc_crc16()
{
    string=$1
    while read -d "" -n 1 ; do astring+=( "$reply" ) ; done <<< "$string"

    cnt=${#astring[@]}
    c=0xffff

    for ((x=0;x<$cnt;x++)); do
        char=${astring[$x]}
        e=$(($c ^ $char))
        s=$(($e << 4))
        f=$(($e ^ $s))
        t1=$(($c >> 8))
        t2=$(($f << 8))
        t3=$(($f << 3))
        t4=$(($f >> 4))
        r1=$(($t1 ^ $t2 ^ $t3 ^ $t4))
        c=$r1
    done
    c=$c ^ 0xffff
    echo "checksum = $c"
}

Будет ли что-то делатьс размером макс?Я предполагаю, что в bash я мало что могу с этим поделать.

Я получаю реальное число, но оно не соответствует C ++, которое, как я знаю, работает правильно.кто-нибудь видит что-нибудь, где я могу все испортить?

Ответы [ 3 ]

3 голосов
/ 19 декабря 2011

Первая проблема у вершины

while read -d "" -n 1 ; do astring+=( "$reply" ) ; done <<< "$string"

$reply неверно, поскольку вы не указали имя переменной для чтения: $REPLY.

Следующая ошибка в конце

c=$c ^ 0xffff

Это должно быть

c=$(($c ^ 0xffff))

По крайней мере, так будет работать без ошибок, правильность и уместность - это нечто другое.

Проблемы с корректностью: что делать, если во входной строке есть пробел? Это ужасно сломается. Всегда заключайте в кавычки переменные

Изменение

char=${astring[$x]}

до

char="${astring[$x]}"

Странно, но это правило отличается внутри $(()) конструкций. Ваши битовые операции должны ссылаться на переменные без $, в этих случаях

e=$(( c ^ char ))
s=$(( e << 4 ))
f=$(( e ^ s ))
t1=$(( c >> 8 ))
t2=$(( f << 8 ))
t3=$(( f << 3 ))
t4=$(( f >> 4 ))
r1=$(( t1 ^ t2 ^ t3 ^ t4))

и позже

c=$(( c ^ 0xffff ))

Это приведет к тому, что переменные будут расширены и пробелы не будут взрываться.

Как правило, вы также должны передать -r в read, см. help read о том, что он делает.

Зачем делать дополнительную копию $1 перед обработкой в ​​массив? Использование

while read -d "" -n 1 ; do astring+=( "$REPLY" ) ; done <<< "$1"

достаточно.

Вероятно, нет необходимости превращать ваши входные данные в массив перед обработкой. Вместо этого вы можете вырезать символы из строки в вашем цикле, что ближе к тому, что делает версия C ++. Заменить

char="${astring[$x]}"

с

char="${1:$x:1}"

Это работает непосредственно с параметром функции; так как мы больше не делаем копию этого, нам также нужно получить $cnt другим способом

cnt=${#1}

Но у вас действительно есть еще большие проблемы, такие как тот факт, что персонаж не является целым числом в bash. Для конвертации вы должны использовать следующий синтаксис:

printf '%d' \'a

, где a - символ для преобразования. Если вставить это в контекст скрипта, это будет

char=$(printf '%d' \'"${1:$x:1}")

Сейчас мы куда-то добираемся, но я действительно должен попросить вас подумать, действительно ли все это того стоит. Даже если ты сможешь заставить это работать, что ты получишь?

2 голосов
/ 20 декабря 2011

просто для дальнейшего использования, вот скрипт awk, который я придумал.

он работает так же быстро, как и мой код C ++, который в основном мгновенный.bash занимает около 10 секунд для запуска той же строки.awk намного быстрее.

function awk_calc_crc16()
{
    output=$(echo $1 | awk 'function ord(c){return chmap[c];}
    BEGIN{c=65535; for (i=0; i < 256; i++){ chmap[sprintf("%c", i)] = i;}}
    {
        split($0, chars, "");
        for(i = 1; i <= length(chars); i++)
        {
            cval=ord(chars[i])
            e=and(xor(c, ord(chars[i])), 0x00FF);
            s=and(lshift(e, 4), 0x00FF);
            f=and(xor(e, s), 0x00FF);
            r=xor(xor(xor(rshift(c, 8), lshift(f, 8)), lshift(f, 3)), rshift(f, 4));
            c=r;
        }
    }
    END{c=xor(c, 0xFFFF); printf("%hu", c);}')
    echo $output;
}
1 голос
/ 20 декабря 2011

ки. с помощью Сорпигала у меня есть рабочая версия.

Я подозреваю, что все это можно сделать в скрипте awk, который может работать намного быстрее. я могу попробовать это дальше.

спасибо всем за помощь. я не хочу красть решение здесь, но я работал над ним, и думаю, что это стоит поставить.

В любом случае, вот рабочая версия:

function calc_crc16()
{
    while read -r -d "" -n 1 ; do astring+=( "$REPLY" ) ; done <<< "$1"

    cnt=${#1}
    c=65535

    for ((x=0;x<$cnt;x++)); do
        char=$(printf '%d' \'"${1:$x:1}")
        e=$(((c ^ char) & 0x00FF))
        s=$(((e << 4) & 0x00FF))
        f=$(((e ^ s) & 0x00FF))
        r1=$(((c >> 8) ^ (f << 8) ^ (f << 3) ^ (f >> 4)))
        c=$r1
    done
    c=$((c ^ 0xffff))
    echo "checksum = $c"
}
...