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، مثل التالية۔ تختلف هذه الأنواع حسب نوع وحجم البيانات التي تعالجها.۔

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 مفيدة إذا كنت ترغب في التحكم في مسائل ترتيب البايتات حسب البيئة۔ يتيح لك 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 آلية مريحة لقراءة وكتابة البايتات بدقة۔ يدعم أنواعًا متعددة مثل الأعداد الصحيحة الموقعة وغير الموقعة والأعداد العشرية، ويسمح لك بتحديد ترتيب البايتات (endianness) صراحة، وهو مفيد جدًا عند تنفيذ بروتوكولات ثنائية۔

الفرق بين 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()۔

نسخ المخازن المؤقتة (buffers) وتحويل الأنواع (التحويل بين أنواع 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 من نفس نوع العنصر۔

مثال عملي: محلل بروتوكول ثنائي (تنفيذ بسيط)

هنا، نقدم مثالاً بسيطاً لمحلل يقرأ بيانات ثنائية ذات تنسيق ثابت مكونة من نوع بحجم بايت واحد، وطول بيانات بحجم بايتين، وحمولة (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، ويتم إنشاء مقطع الحمولة باستخدام 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۔ يتم استخدامه للصور أو الصوت أو بروتوكولات ثنائية مخصصة۔

نصائح خاصة بالأداء وأخطاء شائعة

فيما يلي بعض 'نصائح الأداء' و'أخطاء شائعة' من المفيد معرفتها عند استخدام أنواع TypedArray

  • تجنب النسخ غير الضروري لمعالجة البيانات الكبيرة بكفاءة، يمكنك تقليل النسخ غير الضروري عن طريق إنشاء عروض جزئية باستخدام subarray أو مشاركة نفس ArrayBuffer عبر عدة عروض۔

  • كن حذرًا مع ترتيب البايتات (endianness) بالنسبة للاتصال بالشبكة أو تنسيقات الملفات، غالباً ما يتم تحديد ترتيب البيانات (ترتيب البايتات)۔ استخدام DataView يتيح لك تحديد ترتيب البايتات صراحةً عند القراءة والكتابة، مما يمنع سوء التفسير غير المقصود۔

  • كن على دراية بنطاق القيم لكل نوع على سبيل المثال، Uint8 يمكنه فقط تمثيل القيم من 0 إلى 255۔ إذا أدخلت قيمة سالبة، فقد يحدث اقتطاع أو التفاف للقيمة، لذلك يجب عليك تحديد قواعد التحويل حسب الحاجة۔

  • ضع في اعتبارك ضغط جمع القمامة (garbage collection) إعادة إنشاء كائنات ArrayBuffer كبيرة باستمرار يزيد عبء إدارة الذاكرة۔ في الحالات التي تكون حساسة للأداء، قد تفكر في تصميم الكود لإعادة استخدام المخازن الموجودة قدر الإمكان۔

الملخص

TypedArray هي آلية للتعامل بسرعة وكفاءة مع البيانات الثنائية۔ من خلال الجمع بين ArrayBuffer، الذي يحجز منطقة بايت بطول ثابت، مع TypedArray أو DataView اللذَين يقرآن ويكتبان المحتوى بواسطة أنواع محددة، يمكنك إجراء عمليات ثنائية مرنة۔ اعتمادًا على حالة الاستخدام الخاصة بك، يمكنك الاختيار بين استخدام subarray/slice أو DataView، وتصميم التنفيذ مع الانتباه لترتيب البايتات والنسخ۔

يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔

YouTube Video