Array yang memiliki jenis - Data biner di browser

Ilmari Heikkinen

Pengantar

Array Berjenis adalah tambahan yang relatif baru untuk browser, yang lahir dari kebutuhan untuk memiliki cara yang efisien dalam menangani data biner di WebGL. Typed Array adalah bagian memori dengan tampilan berjenis, seperti cara kerja array di C. Karena Array Berjenis didukung oleh memori mentah, mesin JavaScript dapat meneruskan memori langsung ke library native tanpa harus susah payah mengonversi data ke representasi native. Akibatnya, array berjenis berperforma jauh lebih baik daripada array JavaScript untuk meneruskan data ke WebGL dan API lain yang menangani data biner.

Tampilan array berjenis berfungsi seperti array jenis tunggal ke segmen ArrayBuffer. Ada tampilan untuk semua jenis numerik biasa, dengan nama deskriptif mandiri seperti Float32Array, Float64Array, Int32Array, dan Uint8Array. Ada juga tampilan khusus yang telah menggantikan jenis array piksel di ImageData Canvas: Uint8ClampedArray.

DataView adalah jenis tampilan kedua dan dimaksudkan untuk menangani data heterogen. Alih-alih memiliki API seperti array, objek DataView memberi Anda API get/set untuk membaca dan menulis jenis data arbitrer pada offset byte arbitrer. DataView berfungsi dengan baik untuk membaca dan menulis header file serta data seperti struct lainnya.

Dasar-Dasar Penggunaan Array Berjenis

Tampilan array berjenis

Untuk menggunakan Array Berjenis, Anda harus membuat ArrayBuffer dan tampilan untuknya. Cara termudah adalah membuat tampilan array yang memiliki ukuran dan jenis yang diinginkan.

// Typed array views work pretty much like normal arrays.
var f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];

Ada beberapa jenis tampilan array berjenis. Semuanya memiliki API yang sama, jadi setelah mengetahui cara menggunakan salah satunya, Anda akan tahu cara menggunakannya semua. Saya akan membuat salah satu dari setiap tampilan array berjenis yang ada saat ini dalam contoh berikutnya.

// Floating point arrays.
var f64 = new Float64Array(8);
var f32 = new Float32Array(16);

// Signed integer arrays.
var i32 = new Int32Array(16);
var i16 = new Int16Array(32);
var i8 = new Int8Array(64);

// Unsigned integer arrays.
var u32 = new Uint32Array(16);
var u16 = new Uint16Array(32);
var u8 = new Uint8Array(64);
var pixels = new Uint8ClampedArray(64);

Yang terakhir sedikit khusus, fungsi ini menjepit nilai input antara 0 dan 255. Hal ini sangat berguna untuk algoritma pemrosesan gambar Kanvas karena sekarang Anda tidak perlu membatasi matematika pemrosesan gambar secara manual untuk menghindari kelebihan rentang 8-bit.

Misalnya, berikut cara menerapkan faktor gamma ke gambar yang disimpan dalam Uint8Array. Tidak terlalu bagus:

u8[i] = Math.min(255, Math.max(0, u8[i] * gamma));

Dengan Uint8ClampedArray Anda dapat melewati penjepit manual:

pixels[i] *= gamma;

Cara lain untuk membuat tampilan array berjenis adalah dengan membuat ArrayBuffer terlebih dahulu, lalu membuat tampilan yang mengarah ke ArrayBuffer tersebut. API yang menghasilkan data eksternal Anda biasanya berhubungan dalam ArrayBuffer, jadi ini adalah cara Anda mendapatkan tampilan array yang diketik untuk API tersebut.

var ab = new ArrayBuffer(256); // 256-byte ArrayBuffer.
var faFull = new Uint8Array(ab);
var faFirstHalf = new Uint8Array(ab, 0, 128);
var faThirdQuarter = new Uint8Array(ab, 128, 64);
var faRest = new Uint8Array(ab, 192);

Anda juga dapat memiliki beberapa tampilan ke ArrayBuffer yang sama.

var fa = new Float32Array(64);
var ba = new Uint8Array(fa.buffer, 0, Float32Array.BYTES_PER_ELEMENT); // First float of fa.

Untuk menyalin array berjenis ke array berjenis lain, cara tercepat adalah dengan menggunakan metode set array berjenis. Untuk penggunaan seperti memcpy, buat Uint8Arrays ke buffer tampilan dan gunakan set untuk menyalin data.

function memcpy(dst, dstOffset, src, srcOffset, length) {
  var dstU8 = new Uint8Array(dst, dstOffset, length);
  var srcU8 = new Uint8Array(src, srcOffset, length);
  dstU8.set(srcU8);
};

DataView

Untuk menggunakan ArrayBuffer yang berisi data dengan jenis heterogen, cara termudah adalah menggunakan DataView ke buffer. Misalkan kita memiliki format file yang memiliki header dengan int 8-bit tanpa tanda tangan, diikuti dengan dua int 16-bit, diikuti dengan array payload dari float 32-bit. Membacanya kembali dengan tampilan array yang diketik dapat dilakukan, tetapi agak merepotkan. Dengan DataView, kita dapat membaca header dan menggunakan tampilan array berjenis untuk array float.

var dv = new DataView(buffer);
var vector_length = dv.getUint8(0);
var width = dv.getUint16(1); // 0+uint8 = 1 bytes offset
var height = dv.getUint16(3); // 0+uint8+uint16 = 3 bytes offset
var vectors = new Float32Array(width*height*vector_length);
for (var i=0, off=5; i<vectors.length; i++, off+=4) {
  vectors[i] = dv.getFloat32(off);
}

Pada contoh di atas, semua nilai yang saya baca adalah big-endian. Jika nilai dalam buffer adalah little-endian, Anda dapat meneruskan parameter littleEndian opsional ke pengambil:

...
var width = dv.getUint16(1, true);
var height = dv.getUint16(3, true);
...
vectors[i] = dv.getFloat32(off, true);
...

Perlu diketahui bahwa tampilan array yang memiliki jenis selalu dalam urutan byte native. Hal ini untuk membuatnya cepat. Anda harus menggunakan DataView untuk membaca dan menulis data jika endianness akan menjadi masalah.

DataView juga memiliki metode untuk menulis nilai ke buffering. Penyetel ini diberi nama dengan cara yang sama seperti pengambil, "set" diikuti dengan jenis data.

dv.setInt32(0, 25, false); // set big-endian int32 at byte offset 0 to 25
dv.setInt32(4, 25); // set big-endian int32 at byte offset 4 to 25
dv.setFloat32(8, 2.5, true); // set little-endian float32 at byte offset 8 to 2.5

Diskusi tentang endianness

Endianness, atau urutan byte, adalah urutan penyimpanan angka multi-byte di memori komputer. Istilah big-endian menjelaskan arsitektur CPU yang menyimpan byte paling signifikan terlebih dahulu; little-endian, byte paling tidak signifikan terlebih dahulu. Endianness yang digunakan dalam arsitektur CPU tertentu sepenuhnya arbitrer; ada alasan bagus untuk memilih salah satunya. Bahkan, beberapa CPU dapat dikonfigurasi untuk mendukung data {i>big-endian<i} dan {i>small-endian<i}.

Mengapa Anda perlu memperhatikan endianness? Alasannya sederhana. Saat membaca atau menulis data dari disk atau jaringan, endian data harus ditentukan. Ini memastikan bahwa data ditafsirkan dengan benar, terlepas dari endianness CPU yang bekerja dengannya. Di dunia yang semakin terhubung, sangat penting untuk mendukung semua jenis perangkat, big- atau little-endian, dengan benar, yang mungkin perlu berfungsi dengan data biner yang berasal dari server atau peer lain di jaringan.

Antarmuka DataView dirancang khusus untuk membaca dan menulis data ke dan dari file serta jaringan. DataView beroperasi pada data dengan endianness yang ditentukan. Endianness, besar atau kecil, harus ditentukan dengan setiap akses dari setiap nilai, sehingga memastikan Anda mendapatkan hasil yang konsisten dan benar saat membaca atau menulis data biner, apa pun endianness CPU tempat browser berjalan.

Biasanya, saat aplikasi membaca data biner dari server, Anda harus memindainya satu kali untuk mengonversinya menjadi struktur data yang digunakan aplikasi secara internal. DataView harus digunakan selama fase ini. Sebaiknya jangan gunakan tampilan array berjenis multi-byte (Int16Array, Uint16Array, dll.) secara langsung dengan data yang diambil melalui XMLHttpRequest, FileReader, atau API input/output lainnya, karena tampilan array berjenis menggunakan endian native CPU. Selengkapnya akan dibahas nanti.

Mari kita lihat beberapa contoh sederhana. Format file Windows BMP dulunya merupakan format standar untuk menyimpan gambar pada awal Windows. Dokumentasi yang ditautkan di atas dengan jelas menunjukkan bahwa semua nilai bilangan bulat dalam file disimpan dalam format little-endian. Berikut ini cuplikan kode yang menguraikan awal header BMP menggunakan library DataStream.js yang menyertai artikel ini:

function parseBMP(arrayBuffer) {
  var stream = new DataStream(arrayBuffer, 0,
    DataStream.LITTLE_ENDIAN);
  var header = stream.readUint8Array(2);
  var fileSize = stream.readUint32();
  // Skip the next two 16-bit integers
  stream.readUint16();
  stream.readUint16();
  var pixelOffset = stream.readUint32();
  // Now parse the DIB header
  var dibHeaderSize = stream.readUint32();
  var imageWidth = stream.readInt32();
  var imageHeight = stream.readInt32();
  // ...
}

Berikut adalah contoh lain, yang berasal dari demo rendering High Dynamic Range di project contoh WebGL. Demo ini mendownload data floating point mentah dan sedikit endian yang merepresentasikan tekstur rentang dinamis tinggi, dan perlu menguploadnya ke WebGL. Berikut ini cuplikan kode yang menafsirkan nilai floating point dengan benar pada semua arsitektur CPU. Asumsikan variabel “arrayBuffer” adalah ArrayBuffer yang baru saja didownload dari server melalui XMLHttpRequest:

var arrayBuffer = ...;
var data = new DataView(arrayBuffer);
var tempArray = new Float32Array(
  data.byteLength / Float32Array.BYTES_PER_ELEMENT);
var len = tempArray.length;
// Incoming data is raw floating point values
// with little-endian byte ordering.
for (var jj = 0; jj < len; ++jj) {
  tempArray[jj] =
    data.getFloat32(jj * Float32Array.BYTES_PER_ELEMENT, true);
}
gl.texImage2D(...other arguments...,
  gl.RGB, gl.FLOAT, tempArray);

Aturan praktisnya adalah: setelah menerima data biner dari server web, lakukan satu kali penerusan dengan DataView. Baca setiap nilai numerik dan simpan dalam beberapa struktur data lainnya, baik objek JavaScript (untuk data terstruktur dalam jumlah kecil) maupun tampilan array berjenis (untuk blok data yang besar). Hal ini akan memastikan bahwa kode Anda berfungsi dengan benar di semua jenis CPU. Gunakan juga DataView untuk menulis data ke file atau jaringan, dan pastikan untuk menentukan argumen littleEndian dengan tepat ke berbagai metode set untuk menghasilkan format file yang Anda buat atau gunakan.

Ingat, semua data yang melewati jaringan secara implisit memiliki format dan endianness (setidaknya untuk nilai multi-byte). Pastikan untuk menentukan dan mendokumentasikan format semua data yang dikirim aplikasi Anda melalui jaringan dengan jelas.

API Browser yang menggunakan Array Berjenis

Saya akan memberikan ringkasan singkat tentang berbagai API browser yang saat ini menggunakan Array Berjenis. Pemangkasan saat ini mencakup WebGL, Canvas, Web Audio API, XMLHttpRequests, WebSockets, Web Workers, Media Source API, dan File API. Dari daftar API, Anda dapat melihat bahwa Array Berjenis sangat cocok untuk pekerjaan multimedia yang sensitif terhadap performa serta meneruskan data dengan cara yang efisien.

WebGL

Penggunaan pertama Typed Array adalah di WebGL, tempat array ini digunakan untuk meneruskan data buffer dan data gambar. Untuk menetapkan konten objek buffer WebGL, Anda menggunakan panggilan gl.bufferData() dengan Typed Array.

var floatArray = new Float32Array([1,2,3,4,5,6,7,8]);
gl.bufferData(gl.ARRAY_BUFFER, floatArray);

Array Berjenis juga digunakan untuk meneruskan data tekstur. Berikut adalah contoh dasar untuk meneruskan konten tekstur menggunakan Typed Array.

var pixels = new Uint8Array(16*16*4); // 16x16 RGBA image
gl.texImage2D(
  gl.TEXTURE_2D, // target
  0, // mip level
  gl.RGBA, // internal format
  16, 16, // width and height
  0, // border
  gl.RGBA, //format
  gl.UNSIGNED_BYTE, // type
  pixels // texture data
);

Anda juga memerlukan Array Berjenis untuk membaca piksel dari konteks WebGL.

var pixels = new Uint8Array(320*240*4); // 320x240 RGBA image
gl.readPixels(0, 0, 320, 240, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

Kanvas 2D

Baru-baru ini, objek Canvas ImageData dibuat agar berfungsi dengan spesifikasi Typed Arrays. Sekarang Anda bisa mendapatkan representasi Typed Arrays dari piksel pada elemen kanvas. Hal ini berguna karena kini Anda juga dapat membuat dan mengedit array piksel kanvas tanpa harus mengutak-atik elemen kanvas.

var imageData = ctx.getImageData(0,0, 200, 100);
var typedArray = imageData.data // data is a Uint8ClampedArray

XMLHttpRequest2

XMLHttpRequest mendapatkan peningkatan Typed Array dan sekarang Anda dapat menerima respons Typed Array, bukan harus mengurai string JavaScript menjadi Typed Array. Ini sangat rapi untuk meneruskan data yang diambil langsung ke API multimedia dan untuk mengurai file biner yang diambil dari jaringan.

Anda hanya perlu menetapkan responseType objek XMLHttpRequest ke 'arraybuffer'.

xhr.responseType = 'arraybuffer';

Ingat bahwa Anda harus mengetahui masalah endianness saat mendownload data dari jaringan. Lihat bagian tentang endianness di atas.

API File

FileReader dapat membaca konten file sebagai ArrayBuffer. Kemudian, Anda dapat melampirkan tampilan array yang diketik dan DataView ke buffering untuk memanipulasi kontennya.

reader.readAsArrayBuffer(file);

Anda juga harus mengingat sifat endianness. Lihat bagian endianness untuk mengetahui detailnya.

Objek yang dapat ditransfer

Objek yang dapat ditransfer di postMessage membuat penerusan data biner ke jendela lain dan Pekerja Web jauh lebih cepat. Saat Anda mengirim objek ke Pekerja sebagai Transferable, objek tersebut tidak dapat diakses di thread pengiriman dan Pekerja penerima mendapatkan kepemilikan objek. Hal ini memungkinkan implementasi yang sangat dioptimalkan dengan data yang dikirim tidak disalin, hanya kepemilikan Typed Array yang ditransfer ke penerima.

Untuk menggunakan objek Transferable dengan Web Worker, Anda harus menggunakan metode webkitPostMessage pada pekerja. Metode webkitPostMessage berfungsi seperti postMessage, tetapi memerlukan dua argumen, bukan hanya satu. Argumen kedua yang ditambahkan adalah array objek yang ingin Anda transfer ke pekerja.

worker.webkitPostMessage(oneGBTypedArray, [oneGBTypedArray]);

Untuk mendapatkan kembali objek dari pekerja, pekerja dapat meneruskannya kembali ke thread utama dengan cara yang sama.

webkitPostMessage({results: grand, youCanHaveThisBack: oneGBTypedArray}, [oneGBTypedArray]);

Tidak ada salinan, wow!

Media Source API

Baru-baru ini, elemen media juga mendapatkan beberapa manfaat Typed Array dalam bentuk Media Source API. Anda dapat langsung meneruskan Typed Array yang berisi data video ke elemen video menggunakan webkitSourceAppend. Hal ini membuat elemen video menambahkan data video setelah video yang ada. SourceAppend sangat cocok untuk melakukan interstisial, playlist, streaming, dan penggunaan lainnya saat Anda mungkin ingin memutar beberapa video menggunakan satu elemen video.

video.webkitSourceAppend(uint8Array);

WebSocket Biner

Anda juga dapat menggunakan Typed Arrays dengan WebSockets agar tidak perlu melakukan stringing pada semua data Anda. Sangat cocok untuk menulis protokol yang efisien dan meminimalkan traffic jaringan.

socket.binaryType = 'arraybuffer';

Baguslah! Tindakan ini akan mengakhiri peninjauan API. Mari kita lanjutkan dengan melihat library pihak ketiga untuk menangani Array Berjenis.

Library pihak ketiga

jDataView

jDataView menerapkan shim DataView untuk semua browser. DataView sebelumnya merupakan fitur khusus WebKit, tetapi sekarang didukung oleh sebagian besar browser lainnya. Tim developer Mozilla juga sedang dalam proses merilis patch untuk mengaktifkan DataView di Firefox.

Eric Bidelman dari tim Hubungan Developer Chrome menulis contoh pembaca tag ID3 MP3 kecil yang menggunakan jDataView. Berikut adalah contoh penggunaan dari postingan blog:

var dv = new jDataView(arraybuffer);

// "TAG" starts at byte -128 from EOF.
// See http://en.wikipedia.org/wiki/ID3
if (dv.getString(3, dv.byteLength - 128) == 'TAG') {
  var title = dv.getString(30, dv.tell());
  var artist = dv.getString(30, dv.tell());
  var album = dv.getString(30, dv.tell());
  var year = dv.getString(4, dv.tell());
} else {
  // no ID3v1 data found.
}

stringencoding

Saat ini, menggunakan string di Array Berjenis agak merepotkan, tetapi ada library stringencoding yang dapat membantu. Stringencoding mengimplementasikan spesifikasi encoding string Array Berjenis yang diusulkan, sehingga ini juga merupakan cara yang baik untuk merasakan apa yang akan datang.

Berikut adalah contoh penggunaan dasar stringencoding:

var uint8array = new TextEncoder(encoding).encode(string);
var string = new TextDecoder(encoding).decode(uint8array);

BitView.js

Saya telah menulis library manipulasi bit kecil untuk Array Berjenis yang disebut BitView.js. Seperti namanya, fungsi ini berfungsi seperti DataView, kecuali berfungsi dengan bit. Dengan BitView, Anda bisa mendapatkan dan menetapkan nilai bit pada offset bit tertentu dalam ArrayBuffer. BitView juga memiliki metode untuk menyimpan dan memuat int 6-bit dan 12-bit pada offset bit arbitrer.

Int 12-bit bagus untuk digunakan dengan koordinat layar, karena layar cenderung memiliki kurang dari 4096 piksel di sepanjang dimensi yang lebih panjang. Dengan menggunakan int 12-bit, bukan int 32-bit, Anda mendapatkan pengurangan ukuran sebesar 62%. Untuk contoh yang lebih ekstrem, saya bekerja dengan Shapefile yang menggunakan float 64-bit untuk koordinat, tetapi saya tidak memerlukan presisi karena model hanya akan ditampilkan pada ukuran layar. Beralih ke koordinat dasar 12-bit dengan delta 6-bit untuk mengenkode perubahan dari koordinat sebelumnya akan mengurangi ukuran file menjadi sepersepuluh. Anda dapat melihat demonya di sini.

Berikut adalah contoh penggunaan BitView.js:

var bv = new BitView(arrayBuffer);
bv.setBit(4, 1); // Set fourth bit of arrayBuffer to 1.
bv.getBit(17); // Get 17th bit of arrayBuffer.

bv.getBit(50*8 + 3); // Get third bit of 50th byte in arrayBuffer.

bv.setInt6(3, 18); // Write 18 as a 6-bit int to bit position 3 in arrayBuffer.
bv.getInt12(9); // Read a 12-bit int from bit position 9 in arrayBuffer.

DataStream.js

Salah satu hal yang paling menarik tentang array berjenis adalah cara array tersebut mempermudah penanganan file biner di JavaScript. Daripada mengurai karakter string satu per satu dan mengonversi karakter menjadi angka biner secara manual, kini Anda bisa mendapatkan ArrayBuffer dengan XMLHttpRequest dan langsung memprosesnya menggunakan DataView. Hal ini memudahkan untuk, misalnya, memuat dalam file MP3 dan membaca tag metadata untuk digunakan dalam pemutar audio Anda. Atau, muat shapefile dan ubah menjadi model WebGL. Atau, baca tag EXIF dari JPEG dan tampilkan di aplikasi slideshow Anda.

Masalah dengan XHR ArrayBuffer adalah membaca data seperti struct dari buffering agak merepotkan. DataView cocok untuk membaca beberapa angka sekaligus dengan cara yang aman untuk endian, tampilan array berjenis cocok untuk membaca array angka endian native yang selaras dengan ukuran elemen. Yang kami rasa kurang adalah cara untuk membaca array dan struct data dengan cara yang aman dan mudah. Masukkan DataStream.js.

DataStream.js adalah library Array Berjenis yang membaca dan menulis skalar, string, array, dan struktur data dari ArrayBuffers dengan cara seperti file.

Contoh pembacaan dalam array float dari ArrayBuffer:

// without DataStream.js
var dv = new DataView(buffer);
var f32 = new Float32Array(buffer.byteLength / 4);
var littleEndian = true;
for (var i = 0; i<f32.length; i++) {
  f32[i] = dv.getFloat32(i*4, littleEndian);
}

// with DataStream.js
var ds = new DataStream(buffer);
ds.endianness = DataStream.LITTLE_ENDIAN;
var f32 = ds.readFloat32Array(ds.byteLength / 4);

Tempat DataStream.js sangat berguna dalam membaca data yang lebih kompleks. Misalkan Anda memiliki metode yang membaca penanda JPEG:

// without DataStream.js
var dv = new DataView(buffer);
var objs = [];
for (var i=0; i<buffer.byteLength;) {
  var obj = {};
  obj.tag = dv.getUint16(i);
  i += 2;
  obj.length = dv.getUint16(i);
  i += 2;
  obj.data = new Uint8Array(obj.length - 2);
  for (var j=0; j<obj.data.length; j++,i++) {
    obj.data[j] = dv.getUint8(i);
  }
  objs.push(obj);
}

// with DataStream.js
var ds = new DataStream(buffer);
ds.endianness = ds.BIG_ENDIAN;
var objs = [];
while (!ds.isEof()) {
  var obj = {};
  obj.tag = ds.readUint16();
  obj.length = ds.readUint16();
  obj.data = ds.readUint8Array(obj.length - 2);
  objs.push(obj);
}

Atau, gunakan metode DataStream.readStruct untuk membaca struct data. Metode readStruct mengambil array definisi struct yang berisi jenis anggota struct. Terdapat fungsi callback untuk menangani jenis yang kompleks dan juga menangani array data serta struct bertingkat:

// with DataStream.readStruct
ds.readStruct([
  'objs', ['[]', [ // objs: array of tag,length,data structs
    'tag', 'uint16',
    'length', 'uint16',
    'data', ['[]', 'uint8', function(s,ds){ return s.length - 2; }], // get length with a function
  '*'] // read in as many struct as there are
]);

Seperti yang dapat Anda lihat, definisi struct adalah array datar dari pasangan [name, type]. Struct bertingkat dilakukan dengan memiliki array untuk jenis tersebut. Array ditentukan dengan menggunakan array tiga elemen dengan elemen kedua adalah jenis elemen array dan elemen ketiga adalah panjang array (baik sebagai angka, sebagai referensi ke kolom yang dibaca sebelumnya, atau sebagai fungsi callback). Elemen pertama definisi array tidak digunakan.

Kemungkinan nilai untuk jenis ini adalah sebagai berikut:

Number types

Unsuffixed number types use DataStream endianness.
To explicitly specify endianness, suffix the type with
'le' for little-endian or 'be' for big-endian,
e.g. 'int32be' for big-endian int32.

  'uint8' -- 8-bit unsigned int
  'uint16' -- 16-bit unsigned int
  'uint32' -- 32-bit unsigned int
  'int8' -- 8-bit int
  'int16' -- 16-bit int
  'int32' -- 32-bit int
  'float32' -- 32-bit float
  'float64' -- 64-bit float

String types

  'cstring' -- ASCII string terminated by a zero byte.
  'string:N' -- ASCII string of length N.
  'string,CHARSET:N' -- String of byteLength N encoded with given CHARSET.
  'u16string:N' -- UCS-2 string of length N in DataStream endianness.
  'u16stringle:N' -- UCS-2 string of length N in little-endian.
  'u16stringbe:N' -- UCS-2 string of length N in big-endian.

Complex types

  [name, type, name_2, type_2, ..., name_N, type_N] -- Struct

  function(dataStream, struct) {} -- Callback function to read and return data.

  {get: function(dataStream, struct) {}, set: function(dataStream, struct) {}}
  -- Getter/setter functions to reading and writing data. Handy for using the
     same struct definition for both reading and writing.

  ['', type, length] -- Array of given type and length. The length can be either
                        a number, a string that references a previously-read
                        field, or a callback function(struct, dataStream, type){}.
                        If length is set to '*', elements are read from the
                        DataStream until a read fails.

Anda dapat melihat contoh langsung pembacaan metadata JPEG di sini. Demo ini menggunakan DataStream.js untuk membaca struktur tingkat tag file JPEG (bersama dengan beberapa penguraian EXIF), dan jpg.js untuk mendekode dan menampilkan gambar JPEG di JavaScript.

Histori Array yang Memiliki Jenis

Array Berjenis dimulai pada tahap implementasi awal WebGL, saat kami menemukan bahwa meneruskan array JavaScript ke driver grafis menyebabkan masalah performa. Dengan array JavaScript, binding WebGL harus mengalokasikan array native dan mengisinya dengan menelusuri array JavaScript dan mentransmisikan setiap objek JavaScript dalam array ke jenis native yang diperlukan.

Untuk memperbaiki bottleneck konversi data, Vladimir Vukicevic dari Mozilla menulis CanvasFloatArray: array float gaya C dengan antarmuka JavaScript. Sekarang Anda dapat mengedit CanvasFloatArray di JavaScript dan meneruskannya langsung ke WebGL tanpa melakukan pekerjaan tambahan apa pun pada binding. Dalam iterasi lebih lanjut, CanvasFloatArray diganti namanya menjadi WebGLFloatArray, yang selanjutnya diganti namanya menjadi Float32Array dan dibagi menjadi ArrayBuffer pendukung dan tampilan Float32Array yang diketik untuk mengakses buffer. Jenis juga ditambahkan untuk ukuran bilangan bulat dan floating point lainnya serta varian yang ditandatangani/tidak ditandatangani.

Pertimbangan desain

Sejak awal, desain Typed Array didorong oleh kebutuhan untuk meneruskan data biner secara efisien ke library native. Karena alasan ini, tampilan array berjenis beroperasi pada data yang sejajar dalam endian native CPU host. Keputusan ini memungkinkan JavaScript mencapai performa maksimum selama operasi seperti mengirim data vertex ke kartu grafis.

DataView dirancang khusus untuk file dan I/O jaringan, di mana data selalu memiliki endianness yang ditentukan, dan mungkin tidak diselaraskan untuk performa maksimum.

Pemisahan desain antara perakitan data dalam memori (menggunakan tampilan array yang diketik) dan I/O (menggunakan DataView) adalah desain yang disengaja. Mesin JavaScript modern mengoptimalkan tampilan array berjenis secara signifikan, dan mencapai performa tinggi pada operasi numerik dengan tampilan tersebut. Tingkat performa saat ini dari tampilan array yang diketik dimungkinkan oleh keputusan desain ini.

Referensi