[О блоге] [наверх] [пред] [2022-10-16 13:17:41+03:00] [4a1cde747924fa08548d1092f1c5e366cf2ce877]
Темы: [hate]

Про endianness работу в Си

https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
В b45f896af5ca60802cbae0a00d8223857a771eba я давал ссылки на тему про
endianness конвертацию в Си. Но на днях на работе мне прислали вопрос
уверен ли я что с endianness нет проблем в таком куске кода тривиальном:
    size_t len = (size_t)(data[2] << 8) | data[3];
Отвечаю что возможно я какой-то очередной подвох в Си не знаю, но что в
нём не так? Мне кидают информацией об endian.h и be16toh(). Отвечаю что
в курсе их существования, но:

* конкретно у be16toh менее удобный API (указатели все эти)
* be16dec, который удобен для использования, не факт что есть в glibc,
  как например в нём, как оказалось, не было be64dec
* endian.h include не портабельный: в BSD системах это sys/endian.h
* плюсом будет только отсутствие *нескольких* арифметических операций
  при запуске на BE-архитектуре. Если бы речь шла про миллионы пакетов в
  секунду, то возможно можно было бы начать задумываться об этом, но
  речь про один пакет за нескольких секунд (это просто парсинг его размера)

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

Можно или писать чуть-чуть более эффективный код, который, в зависимости
от endianness платформы, будет тасовать байты или будет использовать их
as-is без преобразований. Или писать код независимый от endianness
вообще, в котором несколько тривиальных инструкций выполняются всегда,
пускай даже когда это и не надо (совпадающий endianness платформы и данных).

Позже вспомнил про статью Роба Пайка, в которой он тоже считает всё это
фигнёй. Мол, если в коде есть #ifdef BIG_ENDIAN, то это не порядок.
Вместо того, чтобы написать:

    i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

такие люди будут писать что-то типа:

    i = *((int*)data);
    #ifdef BIG_ENDIAN
    i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
    #endif

или, для случая с моим 16-бит кодом:

    #include <stdint.h>
    #ifdef __FreeBSD__ || ...
    #include <sys/endian.h>
    #elif __linux__
    #include <endian.h>
    #endif
    [...]
    uint16_t *lenp = (uint16_t *)(&data[2]);
    size_t len = (be16toh(*rtcplen);

Что, очевидно, гораздо больше года. Плюс не везде возможно целые числа
адресовать по любому адресу (выравнивания).

    In fact, byte-swapping is the surest indicator the programmer
    doesn't understand how byte order works. Why do people make the byte
    order mistake so often? I think it's because they've seen a lot of
    bad code that has convinced them byte order matters.

Plan 9 вообще без единого, как говорит Пайк, platform-specific #ifdef-а
был написан.

    [оставить комментарий]