Soru Javascript Typed Arrays ve Endianness


Bir ikili kodlanmış örgü dosyası oluşturmak için WebGL kullanıyorum. İkili dosya büyük-endian biçimde yazılmıştır (bunu bir hex editöründe açarak veya fiddler kullanarak ağ trafiğini görüntüleyerek bunu doğrulayabilirim). Bir Float32Array veya Int32Array kullanarak ikili yanıt okumaya çalıştığımda, ikili küçük endian olarak yorumlanır ve değerlerim yanlıştır:

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

Yazılan dizilerin varsayılan endianitesine herhangi bir başvuru bulamıyorum. http://www.khronos.org/registry/typedarray/specs/latest/ bu yüzden anlaşma nedir merak ediyorum? Yazılan dizileri okurken tüm ikili verilerin az endian olması gerektiğini varsayalım mı?

Sorunun üstesinden gelmek için bir DataView nesnesini kullanabilirim (önceki bağlantıda ele alınmıştır) ve şunları yapabilirsiniz:

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

"GetInt32" gibi DataView işlevleri varsayılan olarak büyük endian değerleri okur.

(Not: Google Chrome 15 ve Firefox 8 kullanarak test ettim ve ikisi de aynı şekilde davranıyor)


51
2017-10-23 22:37


Menşei


Birisinin merak ettiği bir durumda, cevabın sanırım dosyamı küçük-endian kullanarak yazmalıydım. - Bob
Sorunu ele almak yerine sorunu önlemek deniyor. - Pacerier


Cevaplar:


Mevcut davranış, bir bakıma, ne yazık ki, endianitenin altında yatan donanımın olmasıdır. Hemen hemen tüm masaüstü bilgisayarlar x86 olduğu için, bu küçük endian anlamına gelir. Çoğu ARM İşletim Sistemi, küçük-endian modunu kullanır (ARM işlemcileri bi-endiandır ve bu nedenle her ikisi de çalışabilir).

Bunun bir hayli üzücü olmasının sebebi, neredeyse hiç kimsenin kodlarının büyük-endonlu bir donanım üzerinde çalışıp çalışmadığını, neyin zarar verdiğini ve tüm web platformunun kodlar etrafında, uygulamalar ve platformlar arasında düzgün bir şekilde çalışıp çalışmadığını test edeceğidir. Bu kırılıyor.


53
2017-10-23 23:57



Her nasılsa böyle olacağını düşündüm. - Bob


Buradan alındı http://www.khronos.org/registry/typedarray/specs/latest/ (Bu özellik tamamen uygulandığında) şunları kullanabilirsiniz:

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

Ancak, eğer uygulanmadıkları için bu yöntemi kullanamıyorsanız, endianna göre ters çevirmeniz gerekip gerekmediğini görmek için dosyanın sihirli değerini (hemen hemen her formatın bir sihirli değeri vardır) her zaman kontrol edebilirsiniz.

Ayrıca, sunucunuza endianne özgü dosyaları kaydedebilir ve bunları algılanan host endiannesine göre kullanabilirsiniz.


24
2017-10-24 13:22



Hm, bu iyi bir fikir! Daha önce DataView kullanıyordum, ancak şu anda yalnızca Chrome destekliyor. - Bob
Bir takip gibi, kendi ikili yazarımı JavaScript üzerinde uygularım ve hem firefox hem de krom üzerinde çalışıyor gibi görünüyor. - Chiguireitor


FYI, makinenin endianitesini belirlemek için aşağıdaki javascript işlevini kullanabilir, daha sonra uygun biçimlendirilmiş bir dosyayı istemciye iletebilirsiniz (dosyanın iki sürümünü sunucuya, büyük endiana ve küçük endian'a kaydedebilirsiniz):

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");
}

Sizin durumunuzda, dosyayı küçük endian olarak yeniden oluşturmanız ya da küçük bir endian yapmak için tüm veri yapısından geçmeniz gerekecektir. Yukarıdaki yöntemin bir bükümünü kullanarak, endianness'ı anında değiştirebilirsiniz (gerçekten tavsiye edilmez ve tüm yapı aynı sıkıca paketlenmiş tiplerde ise sadece mantıklıdır), gerçekte, baytları gerektiğinde değiştiren bir saplama işlevi oluşturabilirsiniz.

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;
        }
    }
}

24
2017-10-26 11:14



Güzel! Yeni ekledim new ve return bytes; koduna. Bunlar kodun benim için çalışmasına yardımcı oldu. Teşekkürler. - Theo
Aslında tamponun kendisi değiştirildiği için geri dönüş gerekli değildi. - Theo
dolgu metni sadece bunu yapmak için: :-D - Ryan
@Ryan, neden 2 yerine 4 bayt kullanıyorsunuz? - AngularInDepth.com
@Maximus bu 32bit nedeniyle, örneğin bir Uint32ArrayBuffer - Stefan Rein


Diğer cevaplar benim için biraz modası geçmiş gibi görünüyor, bu yüzden en son özelliklerin bir bağlantısı:

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

Özellikle:

Yazılan dizi görünümü türleri, ana bilgisayarın endianitesiyle çalışır.

DataView türü, belirtilen bir endianness (büyük endian veya küçük endian) ile veri üzerinde çalışır.

Dolayısıyla, Big Endian'da (Ağ Bayt Sırası) veri okumak / yazmak isterseniz, bkz: 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.

14
2017-07-03 12:05



"Yanlış veya tanımlanmamışsa, büyük bir endian değeri okunur." - sadece birkaç saatime ya da hayatıma malol. - Meirion Hughes