Предпочтительная идиома для чтения, не зависящего от порядка байтов

В исходном коде Plan 9 я часто нахожу такой код для чтения сериализованных данных из буфера с четко определенным порядком байтов:

#include <stdint.h>

uint32_t le32read(uint8_t buf[static 4]) {
    return (buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
}

Я ожидал, что и gcc, и clang скомпилируют этот код во что-то такое же простое, как эта сборка на amd64:

    .global le32read
    .type le32read,@function
le32read:
    mov (%rdi),%eax
    ret
    .size le32read,.-le32read

Но вопреки моим ожиданиям, ни gcc, ни clang не распознают этот шаблон и вместо этого производят сложную сборку с несколькими сменами.

Существует ли идиома для такого рода операций, которая переносима на все реализации C99 и дает хороший (т. Е. Подобный представленному выше) код в разных реализациях?

3 ответа

Решение

После некоторых исследований я обнаружил (с помощью потрясающих людей в ##c на Freenode), что gcc 5.0 будет реализовывать оптимизацию для шаблона, описанного выше. Фактически, он компилирует источник C, указанный в моем вопросе, в точную сборку, указанную ниже.

Я не нашел похожую информацию о Clang, поэтому я подал отчет об ошибке.

Если вы хотите обеспечить преобразование между порядком собственной платформы и определенным порядком (например, порядком в сети), вы можете позволить системным библиотекам работать и просто использовать функции <netinet/in.h>: hton, htons, htonl и ntoh, ntohs, nthol.

Но я должен признать, что включаемый файл не гарантирован: под Windows я думаю, что это winsock.h,

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

#include <stdint.h>

uint32_t le32read(uint8_t buf[static 4]) {
    if (O32_HOST_ORDER == O32_LITTLE_ENDIAN) {
        return *(uint32_t *)&buf[0];
    }
    return (buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
}
Другие вопросы по тегам