TypedArray dans TypeScript

TypedArray dans TypeScript

Cet article explique TypedArray dans TypeScript.

Nous allons expliquer les TypedArray en TypeScript, y compris avec des exemples pratiques.

YouTube Video

TypedArray dans TypeScript

TypedArray est un mécanisme pour gérer efficacement les données binaires. C'est particulièrement utile pour les opérations binaires de bas niveau comme les grandes images, les flux d’octets réseau et les tableaux numériques pour WebGL.

Comment créer un ArrayBuffer

ArrayBuffer représente une zone de mémoire de longueur fixe en octets. Commencez par créer un buffer et vérifier sa taille et sa longueur en octets.

1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
  • Ce code crée une zone vide de 16 octets. ArrayBuffer n'a pas de fonctions de lecture/écriture directement, il faut donc y accéder via un TypedArray ou un DataView.

Types de TypedArray

Il existe de nombreux types de TypedArray, comme les suivants. Ils varient en fonction du type de données et de leur taille que chaque type gère.

TypedArray Type de données Taille en bits
Int8Array Entier 8 bits 8 bits
Uint8Array Entier non signé 8 bits 8 bits
Uint8ClampedArray Entier non signé 8 bits limité 8 bits
Int16Array Entier 16 bits 16 bits
Uint16Array Entier non signé 16 bits 16 bits
Int32Array Entier 32 bits 32 bits
Uint32Array Entier non signé 32 bits 32 bits
Float32Array Nombre à virgule flottante 32 bits 32 bits
Float64Array Nombre à virgule flottante 64 bits 64 bits

TypedArray de base (Uint8Array, Int16Array, Float32Array, etc.)

TypedArray crée une « vue typée » au-dessus d’un ArrayBuffer. Voici des exemples de création et d’utilisation de quelques TypedArray typiques.

 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);
  • En créant plusieurs vues sur le même ArrayBuffer, vous pouvez lire et écrire dans la même mémoire avec des types ou des granularités différents. La propriété length de la vue donne le nombre d’éléments, et byteLength le nombre d’octets.

Écriture et lecture (opérations au niveau des octets)

Quand vous écrivez une valeur dans un TypedArray, les octets correspondants en mémoire sont mis à jour. Vous pouvez voir les modifications en lisant le même buffer avec une autre vue.

 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
  • Dans cet exemple, nous écrivons une séquence d’octets puis nous lisons la même zone comme un entier 32 bits. Notez que le résultat dépend de l’endianness (l’ordre des octets) de l’environnement d’exécution.

Endianness (ordre des octets) et DataView

DataView est utile si vous souhaitez gérer les problèmes d’endianness dépendants de l’environnement. DataView vous permet de spécifier l’endianness lors de la lecture et l’écriture.

 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 est un mécanisme pratique pour lire et écrire les octets en détail. Il prend en charge plusieurs types comme les entiers signés ou non signés et les flottants, et permet de spécifier explicitement l’endianness (l’ordre des octets), ce qui est très utile lors de l’implémentation de protocoles binaires.

Différence entre subarray et slice

La méthode subarray d’un TypedArray retourne une vue partageant le buffer d’origine, alors que slice retourne une nouvelle copie. Les performances et les effets de bord varient selon la méthode utilisée.

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
  • Si vous modifiez la vue partagée, le tableau original sera également modifié, ce qui peut entraîner des effets secondaires inattendus. Si vous souhaitez préserver en toute sécurité le tableau original, vous pouvez en créer une copie à l'avance en utilisant slice().

Copier des buffers et conversion des types (conversion entre TypedArray)

Nous expliquerons comment copier des données entre TypedArray et comment coller des portions avec la méthode 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]
  • Si les types d’éléments diffèrent, il faut convertir les valeurs (arrondir, vérifier la plage), pas simplement copier. set permet une copie rapide entre TypedArray du même type d’éléments.

Exemple pratique : un parseur de protocole binaire (implémentation simple)

Voici un exemple simple de parseur lisant un format binaire fixe composé d’un type (1 octet), d’une longueur de données (2 octets) et d’une charge utile (payload) qui suit.

 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));
  • Dans cet exemple, l'en-tête est lu à l'aide de DataView, et la portion de données utiles (payload) est créée avec Uint8Array. L’endianness et la vérification de la longueur du buffer sont importantes.

Web API et TypedArray (exemple : téléchargement de données binaires)

Ceci est un exemple classique de manipulation d’un ArrayBuffer obtenu par une requête réseau avec 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");
  • Vous pouvez passer directement l’ArrayBuffer obtenu via Response.arrayBuffer() à un TypedArray. On l’utilise pour les images, l’audio ou les protocoles binaires personnalisés.

Astuces de performances et pièges courants

Voici quelques conseils de performance et pièges fréquents à connaître lors de l'utilisation des TypedArray :.

  • Évitez les copies inutiles Pour traiter efficacement de grandes quantités de données, réduisez les copies inutiles en créant des vues partielles avec subarray ou en partageant le même ArrayBuffer entre plusieurs vues.

  • Attention à l’endianness Pour la communication réseau ou les formats de fichiers, l’ordre des données (endianness) est souvent spécifié. Utiliser DataView vous permet de spécifier explicitement l’endianness à la lecture/écriture, ce qui évite des interprétations erronées.

  • Attention aux plages de valeurs de chaque type Par exemple, Uint8 ne peut représenter que des valeurs de 0 à 255. Si vous saisissez une valeur négative, une troncature ou un dépassement de valeur peut se produire ; il est donc recommandé de définir des règles de conversion selon les besoins.

  • Attention à la pression sur le ramasse-miettes (garbage collection) Recréer fréquemment de grands ArrayBuffer augmente la charge de la gestion de la mémoire. Dans les situations où les performances sont critiques, vous pouvez envisager de concevoir le code afin de réutiliser autant que possible les tampons (buffers) existants.

Résumé

TypedArray est un mécanisme pour traiter les données binaires de manière rapide et efficace. En combinant l'ArrayBuffer, qui réserve une zone d’octets de taille fixe, avec les TypedArray ou DataView qui lisent et écrivent les contenus en types spécifiques, vous pouvez réaliser des opérations binaires flexibles. Selon votre cas d'utilisation, vous pouvez choisir d'utiliser subarray/slice ou DataView, et concevoir votre implémentation en prêtant attention à l'endianness et aux copies.

Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.

YouTube Video