TypedArray ב- TypeScript
מאמר זה מסביר את TypedArray ב-TypeScript.
נסביר את TypedArray ב-TypeScript, כולל דוגמאות מעשיות.
YouTube Video
TypedArray ב- TypeScript
TypedArray הוא מנגנון לטיפול יעיל בנתונים בינאריים. זה מועיל במיוחד לפעולות בינאריות ברמה נמוכה כמו נתוני תמונה גדולים, זרמי בתים מהרשת, ומערכים מספריים עבור WebGL.
איך יוצרים ArrayBuffer
ArrayBuffer מייצג אזור של בתים באורך קבוע. קודם כל, צרו באפר ובדקו את גודלו ואורך הבתים שלו.
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, הבתים המתאימים בזיכרון מתעדכנים. ניתן לראות את השינויים כאשר קוראים את אותו באפר עם תצוגה שונה.
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 שימושי כאשר רוצים לשלוט בבעיות סדר בתים התלויות בסביבה. DataView מאפשר לציין את סדר הבתים בזמן קריאה וכתיבה.
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הוא מנגנון נוח לקריאה וכתיבה מפורטת של בתים. הוא תומך במגוון סוגים כמו מספרים שלמים חתומים/לא חתומים ומספרים עשרוניים, ומאפשר לציין במפורש את סדר הבתים, מה שנחוץ במיוחד במימוש פרוטוקולים בינאריים.
הבדל בין subarray ו-slice
subarray של TypedArray מחזיר תצוגה שמשתפת את הבאפר המקורי, בעוד slice מחזיר עותק חדש. הביצועים ותופעות הלוואי משתנים בהתאם למה שתבחרו.
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().
העתקת באפרים והמרת טיפוסים (המרה בין TypedArrays)
נסביר כיצד להעתיק נתונים בין TypedArrays ואיך להדביק חלקים בעזרת הפונקציה 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מאפשר העתקה מהירה ביןTypedArrays עם אותו טיפוס איברים.
דוגמה מעשית: מנתח פרוטוקול בינארי (מימוש פשוט)
כאן נציג דוגמת מנתח פשוט שקורא נתונים בינאריים בפורמט קבוע: טיפוס של בית אחד, אורך נתון של שני בתים, ומטען (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. חשוב לבדוק את סדר הבתים ואת אורך הבאפר.
Web API ו-TypedArray (דוגמה: שליפת נתונים בינאריים)
זו דוגמה טיפוסית לטיפול ב-ArrayBuffer שהתקבל מבקשת רשת באמצעות 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. משתמשים בו עבור תמונות, שמע, או פרוטוקולים בינאריים מותאמים.
טיפים לביצועים ומכשולים נפוצים
להלן מספר טיפים לביצועים ומכשולים נפוצים שכדאי לדעת בשימוש ב-TypedArrays:.
-
הימנעו מהעתקה מיותרת לעיבוד נתונים גדולים ביעילות, ניתן להקטין העתקות מיותרות על ידי יצירת תצוגות חלקיות בעזרת
subarrayאו שיתוף אותוArrayBufferבמספר תצוגות. -
היזהרו עם סדר הבתים (Endianness) בתקשורת רשת או פורמטי קבצים, לרוב סדר הבתים מוגדר מראש. שימוש ב-
DataViewמאפשר לציין במפורש את סדר הבתים בעת קריאה וכתיבה, וכך להימנע מפרשנות שגויה של הנתונים. -
היו מודעים לטווחי הערכים של כל טיפוס לדוגמה,
Uint8יכול לייצג רק ערכים בין 0 ל-255. אם תזין ערך שלילי, עלולה להתרחש קיטום או גלישת ערך, לכן עליך להגדיר כללי המרה לפי הצורך. -
חשבו על עומס על מנגנון איסוף הזבל (garbage collection) יצירה מחדש של
ArrayBuffers גדולים לעיתים קרובות מגדילה את עומס ניהול הזיכרון. במצבים בהם הביצועים קריטיים, כדאי לשקול לעצב את הקוד כך שישתמש במאגרים קיימים (buffers) ככל האפשר.
סיכום
TypedArray הוא מנגנון לטיפול בנתונים בינאריים במהירות וביעילות. על ידי שילוב ArrayBuffer, שמקצה אזור בתים באורך קבוע, עם TypedArray או DataView שמבצעים קריאות וכתיבות בטיפוסים מסוימים, אפשר לבצע פעולות בינאריות גמישות. בהתאם למקרה השימוש שלך, תוכל לבחור בין שימוש ב־subarray/slice או DataView, ולעצב את המימוש שלך תוך תשומת לב לסדר בתים (endianness) ולהעתקות.
תוכלו לעקוב אחר המאמר שלמעלה באמצעות Visual Studio Code בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.