?

Log in

No account? Create an account

mercury13_kiev


Это не баг, это фича!


Previous Entry Share Next Entry
Странная ошибка
mercury13_kiev

Не так давно на работе случилась странная ошибка. При переходе на x64 самодельная библиотека XLSX отказала при попытке импорта. Забавная, но на задачу не тянет. Проблема была вот в чём.

В компьютерах есть две стратегии работы с многобайтовыми числами. Число 90AB16 может записать в два байта как 90 AB (порядок Motorola, или big-endian), или AB 90 (порядок Intel, или little-endian). x86 и x64 относятся ко вторым, раз уж они Intel.

А ещё некоторые машины для упрощения схемы не могут обращаться к 4-байтовым числам, чей адрес не кратен четырём. То есть по адресам 0 и 4 могут, а по адресам 1, 2, 3 — уже нет. Машины x86 и x64 свободны от такого ограничения (правда, по некратным адресам обращение несколько медленнее), а вот у несовместимого с ними Itanium это ограничение есть.

Задача: сравнить пять байтов с «xmlns». Неоптимальный способ: сравнивать по байту или вызвать стандартную функцию сравнения. Оптимальный: сравнить четыре байта с «xmln» и пятый с «s». Но для этого нужно, чтобы эти четыре байта схватило одним махом (нет ограничения на адреса). И, разумеется, константа «xmln» по-разному переводится в число в зависимости от того, Intel машина или Motorola. На Intel старший байт «n», на Motorola — «x».

Код такой (простите, не хочу проверять, как в ЖЖ работают хэштэги — потому пускай будет «собака»).

@ifdef _M_IX86
  @define LITTLE_ENDIAN
  @define UNALIGNED
@endif
// аналогично для x64, проверяется макрос _M_IX64

@ifdef UNALIGNED
  @ifdef LITTLE_ENDIAN
    // Константа для порядка байтов Intel
  @else
    // Константа для порядка байтов Motorola
  @endif
  // Оптимальный вариант
@else
  // Неоптимальный вариант
@endif

Ошибка случилась двойная. Макрос проверки на машину x64, _M_IX64, оказался неверным (вроде правильно _M_AMD64), но где-то глубоко в хедерах MinGW определён макрос UNALIGNED. А теперь посмотрите на вторую часть кода. Макрос UNALIGNED есть — включается оптимизация. Макроса LITTLE_ENDIAN нет — оптимизация под порядок байтов Motorola. Вот и говорит, что в XML нет пространств имён: вместо префикса «xmlns» искало «nlmxs».

Как исправить? Уже замена UNALIGNED → MACHINE_UNALIGNED (и аналогично для LITTLE_ENDIAN) всё решает методом выключения оптимизации. Ну и _M_IX64 → _M_AMD64, чтобы оптимизация-то включилась.

Выводы. 1) Макросы — зло, но замены этому злу пока не придумали. 2) По-хорошему, все системные макросы должны иметь такие префиксы, чтобы с ними было сложно пересечься. 3) Хотелось бы иметь в языке возможность преобразовать несколько символов в число на уровне компилятора, с учётом порядка байтов. В Турбо-Паскале было, но в плохом виде было: машина x86 всё-таки Intel, и чтобы одним махом сравнить два байта, надо было писать 'mx', а не 'xm'. 4) Если ты придумал ветку под какую-то машину, но не можешь её проверить, поскольку этой машины нет — ставь аварийный останов с сообщением: «Ветка не проверена, испытывайте на свой риск».