C на PHP, обработка символов - PullRequest
2 голосов
/ 09 января 2010

У меня есть какой-то устаревший код C (как макрос), который я не могу ни изменять, ни заменять.

Этот код (в конечном итоге) выводит строку дайджеста (C) на основе исходной строки, выполняя операцию над значением хеш-функции для каждого символа в строке.

#define DO_HASH(src, dest) { \
    unsigned long hash = 1111; // Seed. You must NOT change this. \
    char c, *srcPtr; \
    int i; \
    unsigned char hashedChar; \
    \
    srcPtr = src; \
    c = *srcPtr++; \
    while ( c) { \
            hash = ((hash << 5) + hash) + c; \
            c = *srcPtr++; \
    } \
    ... // etc.

} // 

Несколько лет назад мне пришлось реализовать его на PHP как функцию, возвращающую строку дайджеста. Функция PHP должна одинаково воспроизводить результаты Си.

function php_DO_HASH($srcStr)
{
    $hash = 1111;       // Seed. You must NOT change this.
    $index = 0;
    $c = $srcStr[$index];

    while ($c) {
        $hash = (($hash << 5) + $hash) + ord($c);
        $index++;
        $c = $srcStr[$index];
    }

    ... // etc.
}

Это успешно работает в течение нескольких лет. Тем не менее, в последние несколько дней мой сервер обновился до новой версии CentOS, но говорит, что они не изменили версию PHP. С тех пор два кода теперь генерируют разные выходные данные.

Может кто-нибудь посоветовать, что я делаю неправильно в версии PHP? Спасибо.

Ответы [ 4 ]

2 голосов
/ 09 января 2010

Возможно, они изменились на 64-битную систему? Вы должны попытаться изменить значение хеша с 0xffffffff после каждого раунда.

1 голос
/ 09 января 2010

Условия while вашей версии на C и PHP отличаются.
Версия C прерывается, когда есть символ ' \ 0' (ord ('\ 0') === 0, строка с нулевым символом в конце), а версия php - нет. С другой стороны, версия php остановится на символе '0' (ord ('0') === 48), а версия c - нет.

edit: может также иметь проблемы с диапазонами значений и преобразованием типов. В php нет типа unsigned long. Но php преобразует целое число в число с плавающей точкой, когда результат добавления больше, чем PHP_INT_MAX. например,

var_dump(PHP_INT_MAX);
var_dump(PHP_INT_MAX + 1);

отпечатки (на моем 32-битном компьютере)

int(2147483647)
float(2147483648)

I думаю, next << «исправит» эту проблему (поскольку php преобразует float обратно в int таким образом, который «работает» с вашим алгоритмом). Но в зависимости от того, что вы делаете с $ hash после цикла, это может быть проблемой. </p>

0 голосов
/ 11 января 2010

Вы столкнулись с той же проблемой переполнения PHP (где поведение варьируется в зависимости от версии), что и этот вопрос . В принятом ответе есть все подробности, в том числе функция усечения до 32 битов, которая, очевидно, работает во всех версиях PHP:

function thirtyTwoBitIntval($value)
{
    if ($value < -2147483648)
    {
        return -(-($value) & 0xffffffff);
    }
    elseif ($value > 2147483647)
    {
        return ($value & 0xffffffff);
    }
    return $value;
}

Если вы передаете свое хеш-значение через эту функцию thirtyTwoBitIntval() каждый раз, когда оно пересчитывается, то есть:

hash = thirtyTwoBitIntval(($hash << 5) + $hash + ord($c));

тогда это должно решить проблему.

0 голосов
/ 09 января 2010

Я не очень разбираюсь в PHP, но, похоже, я помню, что вы можете выбрать, будут ли индексы массива начинаться с 0 или 1. Возможно, стоит проверить это и изменилось ли это значение по умолчанию для вашей реализации.

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


Кроме того, while $c выглядит очень буквально переведенным с C. Вы уверены, что в конце строки по-прежнему есть нулевой символ для завершения цикла?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...