LINUX.ORG.RU

То что сложно реализовать на других языках

 


0

3

Разбираюсь с библиотекой qsp и решил с вами поделится кусочком кода, а точнее реализация crc32. Автор кода, решил создать свой собственный crc32, на базе CRC-32/ISO-HDLC, взяв от туда таблицу, но изменил стартовое значение, и еще по мелочи:

int qspCRC(void *data, int len)
{
    unsigned char *ptr;
    int crc = 0;
    ptr = (unsigned char *)data;
    while (len--)
        crc = (qspCRCTable[(crc & 0xFF) ^ *ptr++] ^ crc >> 8) ^ 0xD202EF8D;
    return crc;
}

В чем прелесть: Использует int, но фактически работает как unsigned int. Вся логика на переполнение - и это как то работает, хотя вроде это UB. Автор кода крут (без шуток), я не спорю, но переписать это на любой другой язык, который будет контролировать переполнение - будет крайне сложно. Почему автор не использовал любой из стандартных алгоритмов crc, как он использовал библиотеку для regexp - загадка.

★★★★

Вся логика на переполнение - и это как то работает, хотя вроде это UB.

  1. А с чего ты решил что это UB?

  2. Такая же логика переполнения беззнакового числа на C#

uint a = uint.MaxValue;
a += 2;
Console.WriteLine(a);

//1
Ygor ★★★★★
()
Ответ на: комментарий от Ygor

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 6.5/5 «If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.» crc - явно принимает значения больше INT_MAX - они и в таблице больше INT_MAX, 0xD202EF8D - это больше INT_MAX. Потому что изначально таблица была unsigned int

Silerus ★★★★
() автор топика

Использует int, но фактически работает как unsigned int.

А в чём сакральный смысл неиспользования unsigned? Если заменить int на unsigned int код сломается или будет работать дальше, только теперь уже без UB? ;)

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

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

Silerus ★★★★
() автор топика

но переписать это на любой другой язык… будет крайне сложно

Ты ещё к коду на Перле с такой меркой подойди. :) Там это не баг, а фича.

hobbit ★★★★★
()
Ответ на: комментарий от Silerus

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

Как именно? Ты код-то можешь скомпилировать и показать?

LamerOk ★★★★★
()
Ответ на: комментарий от Silerus

Конечно другой. Текущий crc ксорится со значением из таблицы — это одинаково для знаковых и беззнаковых. Результат сдвигается вправо на 8 бит — это одинаково для беззнаковых и положительных знаковых, но по разному для беззнаковых и отрицательных знаковых. Для беззнаковых и положительных знаковых слева добавляется 8 нулевых битов, а для отрицательных знаковых слева добавляется 8 единичных битов.

iliyap ★★★★★
()

тут одни битовые операции и один сдвиг влево. причем знаковый. но похоже это ему жизнь не портит.

это не признак «мастерства». просто автор не понимает. что crc - как и любой хеш - беззнаковый, поскольку есть отображение на множество натуральных.

то есть у него уже прототип функции написан неверно.

alysnix ★★★
()
Ответ на: комментарий от mittorn

От xor с 0xD202EF8D

у битовых операций вообще нет переполнения. поскольку там нет переноса. но знаковый сдвиг вправо оставляет знак на месте. по идее может выставляться sign бит.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

Какой-то crc32 то будет считать и он будет работать. Другое дело на иной платформе рассчитанный таким алгоритмом crc32 может дать другое значение.

Psilocybe ★★★★★
()
Ответ на: комментарий от Silerus

crc >> 8

вот эта фигня с вопросом. раз crc знаковый, то старший бит crc будет неизменный, это явно дает некую корреляцию, и будет весь хеш делать с некими отклонениям от нормального распределения.

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

alysnix ★★★
()

Да отсутствие обязательного контроля переполнения int в половине языков, наверное. Даже в Java можно написать такой же алгоритм.

А в популярных языках, где нельзя, часто вопрос не в защите от переполнения, а в бесконечных int (честная длинная арифметика в Python, float в JavaScript и т. д.). Что позволяет написать кучу других «смотри как я могу» алгоритмов, которые будут портянкой на С.

А в чём крутость использования int, а не unsigned int или даже uint32_t (если хочется поддерживать дремучие компиляторы без stdint, то можно надефайнить свои аналоги как делают всякие либы) для меня загадка.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

("", 0x00000000), // Пустая строка («a», -390611389), // Одиночный ASCII символ («test», 662733068), // ASCII строка («привет», 957064684), // UTF-8 строка («Hello, world!», 1782682705), // Длинная строка справа - это то что посчитала с функция

Silerus ★★★★
() автор топика
Ответ на: комментарий от zurg

Это не уб, а определяется имплементацией. Хотя что им мешало делать просто арифметический (с сохранением знака бит), как стандарт - непонятно.

тут и проблемы никакой нет. знаковый операнд - знаковый сдвиг. беззнаковый операнд - чисто битовый сдвиг с задвиганием нуля в старший разряд.

alysnix ★★★
()
Ответ на: комментарий от iliyap

Результат сдвигается вправо на 8 бит — это одинаково для беззнаковых и положительных знаковых, но по разному для беззнаковых и отрицательных знаковых. Для беззнаковых и положительных знаковых слева добавляется 8 нулевых битов, а для отрицательных знаковых слева добавляется 8 единичных битов.

а для отрицательных знаковых слева добавляется 8 единичных битов.

Сдвиг вправо для отрицательных чисел - это implementation-defined. Могут насыпать как нулей, так и единиц.

LamerOk ★★★★★
()
Ответ на: комментарий от alysnix

что им мешало делать просто арифметический (с сохранением знака бит), как стандарт - непонятно.

The Committee has affirmed the freedom in implementation granted by the Base Document in not requiring the signed right shift operation to sign extend, since such a requirement might slow down fast code and since the usefulness of sign extended shifts is marginal.

LamerOk ★★★★★
()
Ответ на: комментарий от Silerus

Ты просто неправильно функцию написал, возьми мою, и вызови вот так:

function qspCRC(data) {
  let ptr = 0, crc = 0, len = data.length;  
  while (len--) 
    crc = (qspCRCTable[(crc & 0xFF) ^ data[ptr++]] ^ crc >> 8) ^ 0xD202EF8D;
  return crc;
}

console.log(qspCRC(new Buffer.from('привет')));

...
957064684
Тоже самое и с Python.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)
Ответ на: комментарий от LamerOk

might slow down fast code and since the usefulness of sign extended shifts is marginal.

вот. это не УБ. это просто в комитете не определились, что со знаком делать. но вроде gcc и clang не парятся, и делают знаковый шифт в таком случае.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

‘Empty: OK (got 0, expected 0)’ «‘a’: OK (got -390611389, expected -390611389)» «‘test’: FAIL (got -863718235, expected 662733068)» «‘привет’: FAIL (got -721927907, expected 957064684)» Вставил твою функцию

Silerus ★★★★
() автор топика
Последнее исправление: Silerus (всего исправлений: 1)
Ответ на: комментарий от alysnix

но вроде gcc и clang не парятся, и делают знаковый шифт в таком случае.

А потом это говно или утекает в сеть или в рамках той же фирмы кто-то копипастит его в Keil.

PPP328 ★★★★★
()
Ответ на: комментарий от Silerus

Вот код + вывод, не разобрался как на твоем сайте что то редактировать: https://onecompiler.com/nodejs/43qcmy7tu

На Rust и Go тоже должно работать, если перепишешь один в один.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)