Оптимальное и портативное преобразование endian в c/С++

Учитывая двоичный файл с 32-разрядными малоконечными полями, которые мне нужно проанализировать, я хочу написать код разбора, который правильно компилируется независимо от конечности машины, выполняющей этот код. В настоящее время я использую

uint32_t fromLittleEndian(const char* data){
  return uint32_t(data[3]) << (CHAR_BIT*3) |
         uint32_t(data[2]) << (CHAR_BIT*2) |
         uint32_t(data[1]) << CHAR_BIT |
         data[0]; 
}

это, однако, порождает неоптимальную сборку. На моей машине g++ -O3 -S выдается:

_Z16fromLittleEndianPKc:
.LFB4:
    .cfi_startproc
    movsbl  3(%rdi), %eax
    sall    $24, %eax
    movl    %eax, %edx
    movsbl  2(%rdi), %eax
    sall    $16, %eax
    orl %edx, %eax
    movsbl  (%rdi), %edx
    orl %edx, %eax
    movsbl  1(%rdi), %edx
    sall    $8, %edx
    orl %edx, %eax
    ret
    .cfi_endproc

почему это происходит? Как я могу убедить его в создании оптимального кода при компиляции на небольших конечных машинах:

_Z17fromLittleEndian2PKc:
.LFB5:
    .cfi_startproc
    movl    (%rdi), %eax
    ret
    .cfi_endproc

который я получил путем компиляции:

uint32_t fromLittleEndian2(const char* data){
    return *reinterpret_cast<const uint32_t*>(data);
}

Так как я знаю, что моя машина малозначительная, я знаю, что над сборкой оптимальна, но она не сработает, если скомпилирована на машине большого конца. Он также нарушает правила строгого сглаживания, поэтому, если он встроен, он может создавать UB даже на маленьких конечных машинах. Есть ли допустимый код, который будет скомпилирован для оптимальной сборки , если возможно?

Так как я ожидаю, что моя функция будет встроена в много, любой тип определения endian endtime не может быть и речи. Единственной альтернативой написанию оптимального кода C/С++ является использование определения времени компиляции и использование template или #define, чтобы вернуться к неэффективному коду, если конечный endian не является малоподвижным. Это, однако, представляется довольно трудным сделать портативно.

Ответ 1

Различные библиотеки платформ, которые я знаю, делают это с помощью #defining макросов для подпрограмм подкачки, основанных на значении #define BIG_ENDIAN. В случаях, когда исходная цель соответствует вашей целевой цели, вы можете просто:

#ifdef LITTLE_ENDIAN
    #define fromLittleEndian(x) (x)
#else
    #define fromLittleEndian(x) _actuallySwapLittle((x))
#endif

Например:

http://man7.org/linux/man-pages/man3/endian.3.html

http://fxr.watson.org/fxr/source/sys/endian.h

Ответ 2

короткий ответ - используйте htonl - его можно будет оптимизировать wazzoo