Подтвердить что ты не робот

Javascript Typed Arrays и Endianness

Я использую WebGL для рендеринга двоичного кодированного файла сетки. Бинарный файл выписывается в формате big-endian (я могу проверить это, открыв файл в шестнадцатеричном редакторе или просмотрев сетевой трафик с помощью скрипача). Когда я пытаюсь прочитать двоичный ответ с помощью Float32Array или Int32Array, двоичный файл интерпретируется как little-endian, а мои значения неверны:

// Interpret first 32bits in buffer as an int
var wrongValue = new Int32Array(binaryArrayBuffer)[0];

Я не могу найти ссылки на стандартную спецификацию типизированных массивов в http://www.khronos.org/registry/typedarray/specs/latest/, поэтому мне интересно, что это за сделка? Должен ли я предполагать, что все двоичные данные должны быть малозначительными при чтении с использованием типизированных массивов?

Чтобы обойти проблему, я могу использовать объект DataView (обсуждается в предыдущей ссылке) и вызывать:

// Interpret first 32bits in buffer as an int
var correctValue = new DataView(binaryArrayBuffer).getInt32(0);

Функции DataView, такие как "getInt32", по умолчанию считывают значения по умолчанию.

(Примечание: я тестировал Google Chrome 15 и Firefox 8, и они оба ведут себя одинаково)

4b9b3361

Ответ 1

Нынешнее поведение, к сожалению, заключается в том, что континентность относится к базовому оборудованию. Поскольку почти все настольные компьютеры - это x86, это означает, что это мало-endian. Большинство ОС ARM используют режим little-endian (процессы ARM являются двунаправленными и могут работать в обоих).

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

Ответ 2

Взято отсюда http://www.khronos.org/registry/typedarray/specs/latest/ (когда эта спецификация полностью реализована) вы можете использовать:

new DataView(binaryArrayBuffer).getInt32(0, true) // For little endian
new DataView(binaryArrayBuffer).getInt32(0, false) // For big endian

Однако, если вы не можете использовать этот метод, потому что они не реализованы, вы всегда можете проверить значение магического файла (почти каждый формат имеет магическое значение) в заголовке, чтобы увидеть, нужно ли его инвертировать в соответствии с ваши эндианны.

Кроме того, вы можете сохранить файлы, зависящие от Endiannes, на своем сервере и использовать их соответственно обнаруженным конечным конечным пользователям.

Ответ 3

FYI вы можете использовать следующую функцию javascript для определения конечности машины, после чего вы можете передать файл с соответствующим образом отформатированным клиентом (вы можете сохранить две версии файла на сервере, большой конец и маленький конец):

function checkEndian() {
    var arrayBuffer = new ArrayBuffer(2);
    var uint8Array = new Uint8Array(arrayBuffer);
    var uint16array = new Uint16Array(arrayBuffer);
    uint8Array[0] = 0xAA; // set first byte
    uint8Array[1] = 0xBB; // set second byte
    if(uint16array[0] === 0xBBAA) return "little endian";
    if(uint16array[0] === 0xAABB) return "big endian";
    else throw new Error("Something crazy just happened");
}

В вашем случае вам, вероятно, придется либо воссоздать файл в небольшом конце, либо пропустить всю структуру данных, чтобы сделать его малозначимым. Используя твист вышеупомянутого метода, вы можете поменять его на лету (на самом деле не рекомендуется и имеет смысл только в том случае, если вся структура имеет одинаковые плотно упакованные типы, в действительности вы можете создать функцию заглушки, которая поменяет байты по мере необходимости):

function swapBytes(buf, size) {
    var bytes = new Uint8Array(buf);
    var len = bytes.length;
    var holder;

    if (size == 'WORD') {
        // 16 bit
        for (var i = 0; i<len; i+=2) {
            holder = bytes[i];
            bytes[i] = bytes[i+1];
            bytes[i+1] = holder;
        }
    } else if (size == 'DWORD') {
        // 32 bit
        for (var i = 0; i<len; i+=4) {
            holder = bytes[i];
            bytes[i] = bytes[i+3];
            bytes[i+3] = holder;
            holder = bytes[i+1];
            bytes[i+1] = bytes[i+2];
            bytes[i+2] = holder;
        }
    }
}

Ответ 4

Другие ответы кажутся мне устаревшими, поэтому здесь ссылка на последнюю спецификацию:

http://www.khronos.org/registry/typedarray/specs/latest/#2.1

В частности:

Типы типизированных типов массивов работают с контентом главного компьютера.

Тип DataView работает с данными с заданным контентом (big-endian или little-endian).

Итак, если вы хотите читать/записывать данные в Big Endian (Network Byte Order), см. http://www.khronos.org/registry/typedarray/specs/latest/#DATAVIEW

// For multi-byte values, the optional littleEndian argument
// indicates whether a big-endian or little-endian value should be
// read. If false or undefined, a big-endian value is read.