TypedArray ใน TypeScript

TypedArray ใน TypeScript

บทความนี้อธิบายเกี่ยวกับ TypedArray ใน TypeScript

เราจะอธิบายเกี่ยวกับ TypedArray ใน TypeScript พร้อมตัวอย่างการใช้งานจริง

YouTube Video

TypedArray ใน TypeScript

TypedArray เป็นกลไก สำหรับจัดการกับข้อมูลไบนารี่ได้อย่างมีประสิทธิภาพ มันมีประโยชน์อย่างยิ่งสำหรับการดำเนินการระดับต่ำกับข้อมูลไบนารี่ เช่น ข้อมูลภาพขนาดใหญ่ สตรีมไบต์ในเครือข่าย และอาร์เรย์ตัวเลขสำหรับ WebGL

วิธีสร้าง ArrayBuffer

ArrayBuffer คือพื้นที่ของไบต์ที่มีความยาวคงที่ ก่อนอื่น สร้าง buffer และตรวจสอบขนาดและความยาวเป็นไบต์ของมัน

1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
  • โค้ดนี้จะสร้างพื้นที่ว่าง 16 ไบต์ ArrayBuffer เองไม่มีฟังก์ชันการอ่าน/เขียน คุณต้องเข้าถึงผ่าน TypedArray หรือ DataView

ประเภทของ TypedArray

TypedArray มีหลายประเภท เช่น ต่อไปนี้ TypedArrays แต่ละแบบแตกต่างกันตามประเภทข้อมูลและขนาดที่จัดการได้

TypedArray ประเภทข้อมูล ขนาดบิต
Int8Array จำนวนเต็ม 8 บิต 8 บิต
Uint8Array จำนวนเต็ม 8 บิตแบบไม่มีเครื่องหมาย 8 บิต
Uint8ClampedArray จำนวนเต็ม 8 บิตแบบไม่มีเครื่องหมาย (ค่าจำกัด) 8 บิต
Int16Array จำนวนเต็ม 16 บิต 16 บิต
Uint16Array จำนวนเต็ม 16 บิตแบบไม่มีเครื่องหมาย 16 บิต
Int32Array จำนวนเต็ม 32 บิต 32 บิต
Uint32Array จำนวนเต็ม 32 บิตแบบไม่มีเครื่องหมาย 32 บิต
Float32Array จำนวนทศนิยมแบบ 32 บิต 32 บิต
Float64Array จำนวนทศนิยมแบบ 64 บิต 64 บิต

TypedArray พื้นฐาน (Uint8Array, Int16Array, Float32Array ฯลฯ)

TypedArray สร้าง 'วิวแบบมีชนิดข้อมูล' บน ArrayBuffer ด้านล่างนี้คือตัวอย่างการสร้างและใช้งาน TypedArray แบบต่าง ๆ

 1// Create a buffer and different typed views over it
 2const buf = new ArrayBuffer(8); // 8 bytes
 3
 4// Create views
 5const u8 = new Uint8Array(buf);    // 8 x uint8
 6const i16 = new Int16Array(buf);   // 4 x int16 (since each int16 is 2 bytes)
 7const f32 = new Float32Array(buf); // 2 x float32 (4 bytes each)
 8
 9console.log("Uint8 length:", u8.length);
10console.log("Int16 length:", i16.length);
11console.log("Float32 length:", f32.length);
  • โดยการสร้างวิวหลาย ๆ อันบน ArrayBuffer เดียวกัน คุณสามารถอ่านและเขียนหน่วยความจำเดียวกันได้ในรูปแบบที่แตกต่างกันหรือความละเอียดต่างกัน length ของวิวคือจำนวนสมาชิกในอาร์เรย์ ส่วน byteLength คือจำนวนไบต์

การเขียนและการอ่าน (การดำเนินการในระดับไบต์)

เมื่อเขียนค่าลงใน TypedArray ไบต์ที่เกี่ยวข้องในหน่วยความจำจะถูกอัปเดตด้วย คุณสามารถเห็นการเปลี่ยนแปลงเมื่ออ่าน buffer เดียวกันด้วยวิวแบบอื่น

 1// Demonstrate writing via one view and reading via another
 2const buffer2 = new ArrayBuffer(4);
 3const u8view = new Uint8Array(buffer2);
 4const u32view = new Uint32Array(buffer2);
 5
 6u8view[0] = 0x78;
 7u8view[1] = 0x56;
 8u8view[2] = 0x34;
 9u8view[3] = 0x12;
10
11console.log("Uint8 bytes:", Array.from(u8view)); // [120, 86, 52, 18]
12console.log("Uint32 value (platform endianness):", u32view[0]); // value depends on endianness
  • ในตัวอย่างนี้ เราเขียนชุดไบต์ลงไป จากนั้นอ่านพื้นที่เดียวกันเป็นจำนวนเต็ม 32 บิต โปรดสังเกตว่าผลลัพธ์ที่แสดงขึ้นอยู่กับ endianness ของสภาพแวดล้อมที่ทำงาน

Endianness (ลำดับไบต์) และ DataView

DataView มีประโยชน์มากถ้าคุณต้องการควบคุมปัญหาเกี่ยวกับ endianness ที่ขึ้นกับสภาพแวดล้อม DataView ช่วยให้คุณกำหนด endianness ได้ขณะอ่านและเขียนข้อมูล

 1// Use DataView to read/write with explicit endianness control
 2const b = new ArrayBuffer(4);
 3const dv = new DataView(b);
 4
 5// write little-endian 32-bit integer
 6dv.setUint32(0, 0x12345678, true);
 7
 8// read as little-endian and big-endian
 9const little = dv.getUint32(0, true);
10const big = dv.getUint32(0, false);
11
12console.log("little-endian read:", little.toString(16)); // "12345678"
13console.log("big-endian read:", big.toString(16));       // different value
  • DataView เป็นกลไกที่สะดวกสำหรับการอ่านและเขียนไบต์อย่างละเอียด มันรองรับข้อมูลหลายรูปแบบ เช่น จำนวนเต็มแบบ signed หรือ unsigned และค่าทศนิยม รวมถึงสามารถกำหนด endianness ได้ชัดเจน ซึ่งมีประโยชน์มากเมื่อใช้งานกับไบนารี่โปรโตคอล

ความแตกต่างระหว่าง subarray และ slice

subarray ของ TypedArray จะคืนค่าเป็นวิวที่แชร์ buffer เดิม ขณะที่ slice จะคืนค่าเป็น buffer ใหม่ที่ถูกคัดลอกมา ประสิทธิภาพและผลข้างเคียงจะแตกต่างกันตามที่คุณเลือกใช้งาน

1const base = new Uint8Array([1, 2, 3, 4, 5]);
2
3const shared = base.subarray(1, 4); // shares underlying buffer
4const copied = base.slice(1, 4);    // copies data
5
6shared[0] = 99;
7console.log("base after shared modification:", base); // shows change
8console.log("copied remains:", copied);              // unaffected
  • หากคุณแก้ไขวิวที่ใช้ร่วมกัน อาร์เรย์ต้นฉบับจะเปลี่ยนแปลงด้วย ซึ่งอาจทำให้เกิดผลข้างเคียงที่ไม่ได้ตั้งใจ หากคุณต้องการเก็บรักษาอาร์เรย์ต้นฉบับอย่างปลอดภัย คุณสามารถสร้างสำเนาล่วงหน้าโดยใช้ slice()

การคัดลอก buffer และการแปลงชนิดข้อมูล (ระหว่าง TypedArray)

เราจะอธิบายวิธีคัดลอกข้อมูลระหว่าง TypedArray และการแทรกข้อมูลบางส่วนด้วยเมธอด set

 1// Copy and convert between typed arrays
 2const src = new Float32Array([1.5, 2.5, 3.5]);
 3const dst = new Uint16Array(src.length);
 4
 5// Convert by mapping (explicit conversion)
 6for (let i = 0; i < src.length; i++) {
 7  dst[i] = Math.round(src[i]); // simple conversion rule
 8}
 9console.log("converted dst:", Array.from(dst)); // [2, 2, 4]
10
11// Using set to copy bytes (requires compatible underlying buffer or same element type)
12const src2 = new Uint8Array([10, 20, 30]);
13const dst2 = new Uint8Array(6);
14dst2.set(src2, 2); // copy src2 into dst2 starting at index 2
15console.log("dst2 after set:", Array.from(dst2)); // [0,0,10,20,30,0]
  • ถ้าประเภทของสมาชิกในอาร์เรย์แตกต่างกัน คุณจำเป็นต้องแปลงค่า เช่น ปัดเศษหรือเช็คขอบเขต ไม่สามารถคัดลอกค่าตรง ๆ ได้ set ช่วยให้คัดลอกข้อมูลระหว่าง TypedArray ที่มีชนิดสมาชิกเดียวกันได้อย่างรวดเร็ว

ตัวอย่างในทางปฏิบัติ: ตัวแยกไบนารี่โปรโตคอล (การนำไปใช้แบบง่าย)

ที่นี่ เราขอนำเสนอตัวอย่าง parser ง่าย ๆ ที่อ่านข้อมูลไบนารีรูปแบบคงที่ ซึ่งประกอบด้วย ชนิดข้อมูล 1 ไบต์ ความยาวข้อมูล 2 ไบต์ และ payload ตามหลัง

 1// Simple binary message parser:
 2// format: [type: uint8][length: uint16 BE][payload: length bytes]
 3function parseMessages(buffer: ArrayBuffer) {
 4  const dv = new DataView(buffer);
 5  let offset = 0;
 6  const messages: { type: number; payload: Uint8Array }[] = [];
 7
 8  while (offset + 3 <= dv.byteLength) {
 9    const type = dv.getUint8(offset);
10    const length = dv.getUint16(offset + 1, false); // big-endian
11    offset += 3;
12    if (offset + length > dv.byteLength) break; // truncated
13    const payload = new Uint8Array(buffer, offset, length);
14    messages.push({ type, payload });
15    offset += length;
16  }
17
18  return messages;
19}
20
21// Example usage
22const buf = new ArrayBuffer(1 + 2 + 3 + 1 + 2 + 2); // two messages
23const dv = new DataView(buf);
24let off = 0;
25// first message: type=1, length=3, payload=[1,2,3]
26dv.setUint8(off, 1); dv.setUint16(off + 1, 3, false); off += 3;
27new Uint8Array(buf, off, 3).set([1, 2, 3]); off += 3;
28// second message: type=2, length=2, payload=[9,8]
29dv.setUint8(off, 2); dv.setUint16(off + 1, 2, false); off += 3;
30new Uint8Array(buf, off, 2).set([9, 8]);
31
32console.log(parseMessages(buf));
  • ในตัวอย่างนี้ ส่วนหัวจะถูกอ่านโดยใช้ DataView และกำหนดส่วนข้อมูล payload ด้วย Uint8Array การเช็ค endianness และความยาว buffer มีความสำคัญ

Web API กับ TypedArray (ตัวอย่าง: การรับข้อมูลไบนารี)

นี่คือตัวอย่างมาตรฐานของการจัดการ ArrayBuffer ที่ได้จาก request เครือข่ายโดยใช้ TypedArray

 1// Example of fetching binary and mapping to typed array
 2async function fetchBinary(url: string) {
 3  const res = await fetch(url);
 4  const ab = await res.arrayBuffer();
 5  const view = new Uint8Array(ab);
 6  // process view...
 7  console.log("received bytes:", view.length);
 8  return view;
 9}
10
11// Usage (call in async context)
12// fetchBinary("/path/to/file.bin");
  • คุณสามารถส่ง ArrayBuffer ที่ได้จาก Response.arrayBuffer() ไปยัง TypedArray ได้โดยตรง โดยปกติจะใช้งานกับรูปภาพ เสียง หรือโปรโตคอลไบนารีแบบกำหนดเอง

เคล็ดลับด้านประสิทธิภาพและข้อควรระวังทั่วไป

นี่คือ 'เคล็ดลับด้านประสิทธิภาพ' และ 'ข้อควรระวัง' ที่ควรรู้เมื่อต้องใช้ TypedArray:

  • หลีกเลี่ยงการคัดลอกข้อมูลที่ไม่จำเป็น เพื่อประมวลผลข้อมูลขนาดใหญ่ได้อย่างมีประสิทธิภาพ ควรลดการคัดลอกโดยไม่จำเป็น ด้วยการสร้างวิวส่วนย่อยด้วย subarray หรือแชร์ ArrayBuffer ร่วมกันหลาย ๆ วิว

  • ระวังเรื่อง endianness ด้วย สำหรับการสื่อสารผ่านเครือข่ายหรือรูปแบบไฟล์ มักจะกำหนดลำดับการจัดเก็บข้อมูล (byte order) การใช้ DataView ช่วยให้คุณกำหนด endianness ได้ชัดเจนขณะอ่านและเขียน ป้องกันความเข้าใจผิดที่อาจเกิดจากการจัดลำดับไบต์ที่ต่างกัน

  • โปรดระวังช่วงค่าที่แต่ละชนิดข้อมูลสามารถเก็บได้ ตัวอย่างเช่น Uint8 เก็บค่าได้เฉพาะ 0 ถึง 255 เท่านั้น หากคุณใส่ค่าติดลบ อาจเกิดการตัดทอนหรือการหมุนเวียนค่าขึ้นได้ คุณควรกำหนดกฎการแปลงค่าตามความเหมาะสม

  • ควรพิจารณาภาระของการเก็บขยะ (garbage collection) การสร้าง ArrayBuffer ที่มีขนาดใหญ่ใหม่บ่อยครั้ง จะเพิ่มภาระการจัดการหน่วยความจำ ในสถานการณ์ที่เน้นประสิทธิภาพ คุณควรพิจารณาออกแบบโค้ดเพื่อใช้ประโยชน์จากบัฟเฟอร์ที่มีอยู่อย่างเต็มที่

สรุป

TypedArray เป็นกลไกสำหรับจัดการข้อมูลไบนารี่ได้อย่างรวดเร็วและมีประสิทธิภาพ ด้วยการผสมผสาน ArrayBuffer ที่สงวนพื้นที่ไบต์แบบความยาวคงที่ กับ TypedArray หรือ DataView ที่อ่านและเขียนข้อมูลโดยใช้ชนิดข้อมูลที่เฉพาะเจาะจง คุณสามารถดำเนินการกับข้อมูลไบนารีได้อย่างยืดหยุ่น ขึ้นอยู่กับกรณีการใช้งานของคุณ คุณสามารถเลือกใช้ระหว่าง subarray/slice หรือ DataView และออกแบบการใช้งานโดยคำนึงถึง endianness และการทำสำเนา

คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย

YouTube Video