`DataView` dalam TypeScript

`DataView` dalam TypeScript

Artikel ini menjelaskan DataView dalam TypeScript.

Kami akan menerangkan DataView dalam TypeScript, dari asas hingga kepada penggunaan praktikal, secara langkah demi langkah.

YouTube Video

DataView dalam TypeScript

Dengan menggunakan DataView dalam TypeScript, anda boleh melakukan operasi baca dan tulis pada peringkat bait secara terperinci ke atas ArrayBuffer. DataView sangat berguna untuk pemprosesan binari peringkat rendah seperti pelaksanaan protokol, analisis fail binari, dan penghantaran/penerimaan data binari melalui WebSocket.

Konsep Asas: Perbezaan antara ArrayBuffer, TypedArray, dan DataView

ArrayBuffer ialah struktur data asas yang digunakan untuk menyimpan urutan bait dengan panjang tetap. TypedArray seperti Uint8Array adalah pandangan yang menganggap penimbal sebagai satu larik jenis tertentu, di mana setiap elemen mempunyai jenis tetap.

Sebaliknya, DataView ialah paparan yang fleksibel yang membolehkan anda membaca dan menulis nilai dari sebarang jenis data pada mana-mana kedudukan offset. Kerana ia tidak menganggap jenis tetap dan membenarkan kawalan terperinci pada peringkat bait, ia sesuai untuk menganalisis protokol binari dan pemprosesan peringkat rendah.

Berikut adalah contoh penciptaan ArrayBuffer dan seterusnya penciptaan Uint8Array serta DataView daripada penimbal tersebut.

 1// Create an ArrayBuffer (8 bytes)
 2const buffer = new ArrayBuffer(8);
 3
 4// Create a Uint8Array view (easy to manipulate individual bytes)
 5const u8 = new Uint8Array(buffer);
 6u8[0] = 0x12;
 7u8[1] = 0x34;
 8
 9// Create a DataView (allows reading/writing any type at any offset)
10const dv = new DataView(buffer);
11
12// Read a 16-bit unsigned integer (big-endian assumed if second argument is omitted)
13console.log(dv.getUint16(0)); // 0x1234
  • Dalam kod ini, dua jenis pandangan berbeza—Uint8Array dan DataView—digunakan serentak ke atas satu penimbal. Dengan menggunakan DataView, anda boleh membaca dan menulis nilai dengan fleksibel, dengan menentukan offset dan endianness sewenang-wenangnya.

Kaedah Utama DataView

DataView ialah antara muka untuk memanipulasi ArrayBuffer pada peringkat bait, dan menyediakan kaedah untuk membaca dan menulis pelbagai jenis seperti integer serta nombor titik apung.

Kaedah utamanya adalah seperti berikut:.

  • Baca: getUint8, getInt8, getUint16, getInt16, getUint32, getInt32, getFloat32, getFloat64
  • Tulis: setUint8, setInt8, setUint16, setInt16, setUint32, setInt32, setFloat32, setFloat64

Semua kaedah ini memerlukan 'byte offset' sebagai argumen pertama: get akan membaca nilai pada posisi tersebut, dan set akan menulis nilai ke posisi itu. Anda juga boleh menetapkan endianness melalui argumen kedua semasa mengendalikan data 16-, 32-, atau 64-bit. Secara praktikal, adalah paling selamat untuk sentiasa menetapkan endianness mengikut spesifikasi data.

Contoh berikut menunjukkan cara menulis integer 32-bit dan nombor titik apung 32-bit dalam format little endian ke dalam penimbal, dan kemudian membacanya kembali dalam format yang sama.

 1const buf = new ArrayBuffer(12); // Enough space for integer + float
 2const view = new DataView(buf);
 3
 4// Write a 32-bit unsigned integer at offset 0 (little-endian)
 5view.setUint32(0, 305419896, true); // 305419896 = 0x12345678
 6
 7// Read the same 32-bit unsigned integer back from offset 0 (little-endian)
 8const intVal = view.getUint32(0, true);
 9console.log(intVal); // 305419896
10
11// Write a 32-bit floating number at offset 4 (little-endian)
12view.setFloat32(4, 3.1415926, true);
13
14// Read the 32-bit floating number back from offset 4 (little-endian)
15const floatVal = view.getFloat32(4, true);
16console.log(floatVal); // 3.1415926 (approx)
  • Dengan menetapkan endianness secara jelas, anda boleh memastikan keserasian dengan pelbagai platform dan spesifikasi binari.

Tentang Endianness (Susunan Bait)

Sesetengah protokol, seperti yang digunakan dalam rangkaian, menggunakan big-endian, manakala kebanyakan CPU dan format fail menggunakan reka bentuk little-endian. Dalam DataView, jika argumen kedua ditetapkan kepada true, data dianggap sebagai little endian; jika ditetapkan kepada false atau tidak dinyatakan, ia dianggap sebagai big endian.

Dalam contoh berikut, anda boleh melihat bagaimana susunan bait dalam ingatan berubah apabila menulis nombor yang sama dengan menggunakan kedua format big-endian dan little-endian.

 1const b = new ArrayBuffer(4);
 2const a = new Uint8Array(b);
 3const dv = new DataView(b);
 4
 5// Write in big-endian order
 6dv.setUint32(0, 0x0A0B0C0D, false); // big-endian
 7console.log([...a]); // [10, 11, 12, 13]
 8
 9// Write the same value in little-endian order
10dv.setUint32(0, 0x0A0B0C0D, true); // little-endian
11console.log([...a]); // [13, 12, 11, 10]
  • Memahami bagaimana susunan bait berubah antara big endian dan little endian akan memudahkan analisis data komunikasi atau format binari.

Input/Output Rentetan (Menggunakan TextEncoder dan Decoder Bersama-sama)

DataView sangat baik untuk membaca dan menulis nombor, tetapi ia tidak mengendalikan rentetan secara langsung. Adalah kebiasaan untuk mengekod rentetan menggunakan TextEncoder atau TextDecoder (seperti ke UTF-8), kemudian menyalinnya ke dalam penimbal melalui Uint8Array. Berikut ialah contoh menulis rentetan UTF-8 ke dalam penimbal dan membacanya semula.

 1const encoder = new TextEncoder();
 2const decoder = new TextDecoder();
 3
 4const str = "\u3053\u3093\u306b\u3061\u306f";
 5const encoded = encoder.encode(str); // Uint8Array (UTF-8 encoded bytes)
 6
 7// Create a buffer and write the encoded string bytes into it
 8const buf2 = new ArrayBuffer(encoded.length);
 9const u8v = new Uint8Array(buf2);
10u8v.set(encoded);
11
12// Read the bytes and decode them back into a string
13const decodedStr = decoder.decode(new Uint8Array(buf2));
14console.log(decodedStr);
  • Dengan menukarkan rentetan kepada binari dan, jika perlu, menambah panjangnya di hadapan, anda boleh menyimpan rentetan dengan panjang berubah-ubah.

Contoh Praktikal: Penyandian/Pendekodan Format Binari Khas

Di bawah, kami mentakrifkan format mesej ringkas yang mengandungi nombor versi, ID, dan nama. Kami melaksanakan proses pengekodan untuk menukar mesej kepada data binari, dan proses penyahkodan untuk memulihkan objek asal daripada data binari. Dalam format mesej ini, bait pertama menyimpan versi, empat bait seterusnya menyimpan ID dalam format little-endian, bait berikutnya mengandungi panjang nama, dan akhirnya, nama yang telah dikodkan dalam UTF-8 disimpan.

 1// Encode an object into a binary message
 2function encodeMessage(msg: { version: number; id: number; name: string }): ArrayBuffer {
 3  const encoder = new TextEncoder();
 4  const nameBytes = encoder.encode(msg.name);
 5  const total = 1 + 4 + 1 + nameBytes.length;
 6  const buf = new ArrayBuffer(total);
 7  const dv = new DataView(buf);
 8  let offset = 0;
 9
10  dv.setUint8(offset, msg.version); offset += 1;      // write version
11  dv.setUint32(offset, msg.id, true); offset += 4;    // write ID (little-endian)
12  dv.setUint8(offset, nameBytes.length); offset += 1; // write name length
13
14  // write name bytes
15  new Uint8Array(buf, offset).set(nameBytes);
16  return buf;
17}
18
19// Decode a binary message back into an object
20function decodeMessage(buf: ArrayBuffer) {
21  const dv = new DataView(buf);
22  let offset = 0;
23
24  const version = dv.getUint8(offset); offset += 1;   // read version
25  const id = dv.getUint32(offset, true); offset += 4; // read ID (little-endian)
26  const nameLen = dv.getUint8(offset); offset += 1;   // read name length
27
28  // extract name bytes
29  const nameBytes = new Uint8Array(buf, offset, nameLen);
30  const decoder = new TextDecoder();
31  // decode UTF-8 string
32  const name = decoder.decode(nameBytes);
33
34  return { version, id, name };
35}
36
37// Example usage
38const packed = encodeMessage({ version: 1, id: 42, name: "Alice" });
39const unpacked = decodeMessage(packed);
40console.log(unpacked); // { version: 1, id: 42, name: "Alice" }
  • Ini adalah pelaksanaan tipikal untuk mengendalikan format mesej asas yang mengandungi rentetan panjang berubah-ubah, dan boleh digunakan dalam banyak situasi praktikal seperti komunikasi rangkaian atau reka bentuk fail binari khusus.

Langkah Berjaga-jaga dan Amalan Terbaik

Apabila bekerja dengan data binari menggunakan DataView, terdapat beberapa perkara praktikal yang perlu dipertimbangkan seperti keselamatan, penggunaan endianness secara konsisten, dan pengendalian jenis data yang betul, bukan sekadar membaca dan menulis nilai. Terutamanya apabila mengendalikan data binari yang diterima daripada sumber luar atau menangani integer besar, adalah penting untuk mereka kod anda bagi mencegah pembacaan tidak tepat dan lebihan penimbal. Di bawah adalah beberapa perkara berguna untuk diingati bagi kegunaan praktikal.

  • Semakan Sempadan DataView akan membuang pengecualian jika offset atau saiz melebihi sempadan penimbal. Sentiasa semak panjang apabila mengendalikan data binari yang tidak dipercayai.

  • Sentiasa Tetapkan Endianness Sentiasa tetapkan sama ada little endian atau big endian secara jelas dan konsisten dalam kod anda.

  • Konsistensi Jenis Data Nombor dalam JavaScript ialah nilai titik apung 64-bit IEEE-754. Kaedah seperti getUint32 dikendalikan dengan betul, tetapi tiada getUint64, jadi pengendalian khas diperlukan untuk integer 64-bit.

  • Pengendalian Integer 64-bit Untuk mengendalikan integer 64-bit, anda perlu menggunakan BigInt atau membahagikan nilai tersebut secara manual kepada bahagian 32-bit tinggi dan rendah. Berikut ialah contoh ringkas pembacaan integer tanpa tanda 64-bit.

1function getUint64(dv: DataView, offset: number, littleEndian = true): bigint {
2  const low = BigInt(dv.getUint32(offset, littleEndian));
3  const high = BigInt(dv.getUint32(offset + 4, littleEndian));
4  return littleEndian ? (high << 32n) | low : (low << 32n) | high;
5}
  • Dengan menggunakan BigInt, anda juga boleh mengendalikan integer lebih besar daripada 64 bit dengan selamat.

Penggunaan dalam Node.js (Interoperasi dengan Buffer)

Walaupun Buffer biasa digunakan dalam Node.js, adalah mudah untuk menukar antara ArrayBuffer dan DataView. Gunakan sifat buffer dalam objek Buffer atau pembina Uint8Array untuk tujuan penukaran.

1// Node.js: Buffer -> ArrayBuffer
2const nodeBuf = Buffer.from([1,2,3,4]);
3const arrBuf = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.byteLength);
4const dvNode = new DataView(arrBuf);
5console.log(dvNode.getUint16(0));
  • Ini boleh digunakan sebagai alat untuk pertukaran data binari antara Node.js dan pelayar.

Ringkasan

DataView ialah mekanisme yang berkuasa untuk membaca dan menulis data binari secara bebas—terutamanya berguna dalam situasi yang memerlukan kawalan fleksibel seperti penentuan endianness dan akses ke posisi sewenang-wenangnya. Dengan menggabungkan ArrayBuffer, TypedArray, dan DataView, anda boleh mengendalikan data binari secara fleksibel dan tepat menggunakan TypeScript, membolehkan pelbagai kegunaan dari pelaksanaan protokol hingga analisis fail. Dengan turut menggabungkan pengekodan rentetan dan pengendalian integer 64-bit jika perlu, anda boleh melaksanakan operasi binari yang lebih praktikal.

Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.

YouTube Video