[ Web Proxy ]
URL:
Viewing: https://it.javascript.info/arraybuffer-binary-arrays [Back]  [Original]

ArrayBuffer, array binari
IT

Vorremo rendere disponibile questo progetto open-source per persone in tutto il mondo.

Aiutaci a tradurre il contenuto di questo tutorial nella tua lingua!

    Cerca in Javascript.info:
    Cerca nel tutorial:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    ArrayBuffer, array binari

    Nello sviluppo web incontriamo dati di tipo binario principalmente quando lavoriamo con i file (creazione, upload o download). Un altro tipico caso duso lelaborazione di immagini.

    Sono tutte cose possibili con JavaScript, e le operazioni binarie ottimizzano le prestazioni.

    Sebbene ci sia un po di confusione, poich esistono molte classi. Per citarne alcune:

    • ArrayBuffer, Uint8Array, DataView, Blob, File, etc.

    I dati binari in JavaScript sono implementati in maniera non standard, rispetto ad altri linguaggi. Ma se riusciamo a riorganizzare le idee, tutto diventa piuttosto semplice.

    Loggetto binario di base ArrayBuffer, un riferimento ad unarea di memoria contigua di lunghezza fissa.

    Lo possiamo creare in questo modo:

    let buffer = new ArrayBuffer(16); // creiamo un buffer di lunghezza 16
    alert(buffer.byteLength); // 16

    Questo alloca unarea di memoria contigua di 16 byte, e la popola con degli zeri.

    ArrayBuffer un array di

    Eliminiamo subito una possibile fonte di confusione. ArrayBuffer non ha nulla in comune con Array:

    • Ha una dimensione prefissata, non possiamo aumentarla o diminuirla.
    • Occupa esattamente quello spazio in memoria.
    • Per accedere a uno specifico byte, richiesto un ulteriore oggetto. buffer[index] non funzionerebbe.

    ArrayBuffer rappresenta unarea di memoria. Cosa vi memorizzano? Non ne ha assolutamente idea. Semplicemente una sequenza di byte.

    Per manipolare un ArrayBuffer, dobbiamo utilizzare un oggetto visualizzatore.

    Un oggetto visualizzatore non memorizza nulla. Funge da lente di ingrandimento che fornisce un interpretazione dei byte memorizzati in un ArrayBuffer.

    Ad esempio:

    • Uint8Array: tratta ogni byte contenuto nellArrayBuffer come un numero separato, che pu assumere valori compresi tra 0 e 255 (un byte composta da 8-bit, quindi quellintervallo il massimo rappresentabile). Questo valore viene definito un interno a 8-bit senza segno.
    • Uint16Array: interpreta 2 byte come un intero, possono quindi assumere valori nellintervallo da 0 a 65535. Questo viene definito un intero a 16-bit senza segno.
    • Uint32Array: interpreta 4 byte come un intero, possono quindi assumere valori nellintervallo da 0 a 4294967295. Questo viene definito un intero a 32-bit senza segno.
    • Float64Array: interpreta 8 byte come un numero in virgola mobile, possono quindi assumere valori nellintervallo da 5.0x10-324 a 1.8x10308.

    Quindi, i dati binari contenuti in un ArrayBuffer di 16 byte possono essere interpretati con 16 piccoli numeri, oppure 8 grandi numeri (2 byte ciascuno), oppure 4 numeri ancora pi grandi (4 byte ciascuno), oppure 2 valori in virgola mobile ad alta precisione (8 byte ciascuno).

    []

    ArrayBuffer loggetto principale, la radice di tutto, quello che contiene i dati binari.

    Ma nel momento in cui andiamo a scriverci qualcosa, o iteriamo su di esso, e per praticamente qualsiasi altra operazione, dobbiamo utilizzare un visualizzatore, e.g:

    let buffer = new ArrayBuffer(16); // crea un buffer di lunghezza 16
    
    let view = new Uint32Array(buffer); // interpreta un buffer come una sequenza di interi a 32 bit
    
    alert(Uint32Array.BYTES_PER_ELEMENT); // intero a 4 byte
    
    alert(view.length); // 4, numero di interi memorizzati
    alert(view.byteLength); // 16, la dimensione in byte
    
    // scriviamo un valore
    view[0] = 123456;
    
    // iteriamo sui valori
    for(let num of view) {
      alert(num); // 123456, poi 0, 0, 0 (4 valori in totale)
    }

    TypedArray

    Il termine comune utilizzato per i visualizzatori (Uint8Array, Uint32Array, etc) TypedArray. Questi condividono lo stesso insieme di metodi e propriet.

    Da notare, non esiste alcun costruttore per il tipo TypedArray, semplicemente un ombrello comune per rappresentare un visualizzatore per ArrayBuffer: Int8Array, Uint8Array e cos via, vedremo la lista completa a breve.

    Quando vedete qualcosa come new TypedArray, equivale a: new Int8Array, new Uint8Array, etc.

    I TypedArray si comportano come gli array regolari: sono indicizzabili ed iterabili.

    Un costruttore di TypedArray (che sia Int8Array o Float64Array, non ha importanza) si comporta diversamente in base al tipo degli argomenti.

    Esistono 5 varianti degli argomenti:

    new TypedArray(buffer, [byteOffset], [length]);
    new TypedArray(object);
    new TypedArray(typedArray);
    new TypedArray(length);
    new TypedArray();
    1. Se viene fornito un ArrayBuffer come argomento, il visualizzatore verr creato su questo. Abbiamo gi usato questa sintassi.

      Come parametri opzionali, possiamo fornire il byteOffset da cui iniziare (0 di default) e la length (fino alla fine del buffer di default), in questo modo il visualizzatore coprir solamente una parte del buffer.

    2. Se viene fornito un Array, o un oggetto simil-array, viene creato un TypedArray delle stessa lunghezza e ne viene copiato il contenuto.

      Possiamo utilizzarlo per pre-popolare larray con dei dati:

      let arr = new Uint8Array([0, 1, 2, 3]);
      alert( arr.length ); // 4, creazione di un array binario delle stessa lunghezza
      alert( arr[1] ); // 1, popolato con 4 byte (intero a 8-bit senza segno) con i valori forniti
    3. Se viene fornito un ulteriore TypedArray, accadr la stessa cosa: verr creato un TypedArray delle stessa lunghezza e ne verr copiato il contenuto. I valori verranno convertiti al nuovo tipo, se necessario.

      let arr16 = new Uint16Array([1, 1000]);
      let arr8 = new Uint8Array(arr16);
      alert( arr8[0] ); // 1
      alert( arr8[1] ); // 232, prova a copiare 1000, ma non pu essere rappresentato con 8 bit (vedi la spiegazione sotto)
    4. Per una argomento numerico length, verr creato un TypedArray in grado di contenere quel numero di elementi. La sua dimensione in byte sar length moltiplicato per il numero di byte per singolo elemento TypedArray.BYTES_PER_ELEMENT:

      let arr = new Uint16Array(4); //  crea un TypedArray per 4 interi
      alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 byte per intero
      alert( arr.byteLength ); // 8 (dimensione in byte)
    5. Senza argomenti, crea un TypedArray di lunghezza zero.

    Possiamo creare un TypedArray direttamente, senza menzionare ArrayBuffer. Ma un visualizzatore non pu esistere senza un relativo ArrayBuffer, quindi questo verr creato automaticamente in tutti i casi, ad eccezione del primo (in cui viene fornito).

    Per accedere allArrayBuffer, abbiamo a disposizione le seguenti propriet:

    • arr.buffer, che fa riferimento a ArrayBuffer.
    • arr.byteLength, la lunghezza dellArrayBuffer.

    Quindi possiamo sempre cambiare da un visualizzatore ad un altro:

    let arr8 = new Uint8Array([0, 1, 2, 3]);
    
    // un altro visualizzatore per gli stessi dati
    let arr16 = new Uint16Array(arr8.buffer);

    Qui vediamo la lista dei TypedArray:

    • Uint8Array, Uint16Array, Uint32Array: per numeri interi a 8, 16 e 32 bit.
      • Uint8ClampedArray: per interi a 8-bit, bloccati in fase di assegnazione (vedi sotto).
    • Int8Array, Int16Array, Int32Array: per numeri interi con segno (possono essere negativi).
    • Float32Array, Float64Array: per numeri in virgola mobile con segno, a 32 e 64 bit.
    Non esistono int8, o simili, tipi a singolo valore

    Da notare che, nonostante i nomi, come Int8Array, non esiste alcun tipo a singolo valore come int, o int8 in JavaScript.

    E abbastanza logico, poich Int8Array non un array per questi valori, ma piuttosto un visualizzatore su un ArrayBuffer.

    Comportamento per valori fuori dai limiti

    Cosa accade se proviamo a scrivere un valore fuori dai limiti in un TypedArray? Non verr generata alcuna eccezione. Ma i bit di troppo verranno ignorati.

    Ad esempio, proviamo a scrivere 256 in Uint8Array. Nella forma binaria, 256 equivale a 100000000 (9 bit), ma Uint8Array fornisce solamente 8 bit per valore, questo fa si che lintervallo di valori disponibili vada da 0 a 255.

    Per numeri pi grandi, vengono memorizzati solamente gli 8 bit pi a destra (meno significativi), gli altri verranno ignorati:

    []

    Quindi otterremo uno zero.

    Nel caso di 257, la forma binaria 100000001 (9 bit), vengono memorizzati gli 8 bit pi a destra, quindi il valore memorizzato nellarray sar 1:

    []

    In altre parole, viene memorizzato il numero modulo 28.

    Qui vediamo un esempio:

    let uint8array = new Uint8Array(16);
    
    let num = 256;
    alert(num.toString(2)); // 100000000 (rappresentazione binaria)
    
    uint8array[0] = 256;
    uint8array[1] = 257;
    
    alert(uint8array[0]); // 0
    alert(uint8array[1]); // 1

    Uint8ClampedArray speciale sotto questo aspetto, il suo comportamento differente. Memorizza 255 per qualsiasi numero maggiore di 255, e 0 per qualsiasi valore negativo. Questo comportamento risulta essere molto utile nellelaborazione di immagini.

    Metodi dei TypedArray

    TypedArray possiede gli stessi metodi di Array, con un paio di eccezioni.

    Possiamo iterare su di essi con map, slice, find, reduce etc.

    Ci sono per un p di cose che non possiamo fare:

    • Non possiamo utilizzare splice. Non possiamo eliminare un valore, poich i TypedArray sono dei visualizzatori su un buffer, e questi sono di dimensioni fissata, su unarea contigua di memoria. Tutto ci che possiamo fare assegnargli zero.
    • Non esiste il metodo concat.

    Abbiamo a disposizione due metodi aggiuntivi:

    • arr.set(fromArr, [offset]) copia tutti gli elementi da fromArr a arr, iniziando dalla posizione offset (0 di default).
    • arr.subarray([begin, end]) crea un nuovo visualizzatore dello stesso tipo, a partire da begin fino a end (esclusi). Questo comportamento molto simile al metodo slice (il quale supportato), ma non copia nulla, crea semplicemente un nuovo visualizzatore, utile per operare in uno specifico pezzo di dati.

    Questi metodi ci consentono di copiare i TypedArray, mescolarli, creare nuovi array partendo da quelli gi esistenti, e cos via.

    DataView

    DataView uno speciale, super flessibile visualizzatore non tipizzato su ArrayBuffer. Consente di accedere ai dati in qualsiasi offset in qualsiasi formato.

    • Nel caso di TypedArray, il costruttore ne definiva il formato. Si supponeva che lintero array fosse uniforme. Li-esimo numero arr[i].
    • Con DataView accediamo ai dati con metodi come .getUint8(i) o .getUint16(i). Scegliamo il formato al momento della lettura, piuttosto che al momento della costruzione.

    La sintassi:

    new DataView(buffer, [byteOffset], [byteLength])
    • buffer, lArrayBuffer di riferimento. A differenza dei TypedArray, DataView non crea un suo buffer. Dobbiamo averne gi uno pronto.
    • byteOffset, la posizione del byte di partenza del visualizzatore (di default 0).
    • byteLength, la lunghezza in byte del visualizzatore (di default fino alla fine di buffer).

    Ad esempio, qui estraiamo numeri in formati differenti dallo stesso buffer:

    // array binario di 4 byte, tutti con valore massimo 255
    let buffer = new Uint8Array([255, 255, 255, 255]).buffer;
    
    let dataView = new DataView(buffer);
    
    // prendiamo un numero a 8 bit all'offset 0
    alert( dataView.getUint8(0) ); // 255
    
    // ora prendiamo un numero a 16 bit all'offset 0, consiste di 2 byte, insieme vengono interpretati come 65535
    alert( dataView.getUint16(0) ); // 65535 (il pi grande intero a 16-bit senza segno)
    
    // prendiamo un numero a 32-bit all'offset 0
    alert( dataView.getUint32(0) ); // 4294967295 (il pi grande intero a 32-bit senza segno)
    
    dataView.setUint32(0, 0); // imposta un numero a 4 byte a zero, questo imposta tutti i byte a 0

    DataView ottimo quando memorizziamo dati in formati misti nello stesso buffer. Ad esempio, quando memorizziamo una sequenza di coppie (intero a 16-bit, 32-bit in virgola mobile), DataView ci consente di accedervi in maniera semplice.

    Riepilogo

    ArrayBuffer loggetto principale, un riferimento unarea di memoria contigua di lunghezza fissa.

    Per eseguire qualsiasi operazioni su un ArrayBuffer, abbiamo bisogno di un visualizzatore.

    • Questo pu essere un TypedArray:
      • Uint8Array, Uint16Array, Uint32Array, per interi senza segno a 8, 16, e 32 bit.
      • Uint8ClampedArray, per interi a 8 bit, fissati in fase di assegnazione.
      • Int8Array, Int16Array, Int32Array, per numeri interi con segno (possono essere negativi).
      • Float32Array, Float64Array, per numeri in virgola mobile con segno di 32 e 64 bit.
    • Oppure un DataView, un visualizzatore che utilizza che utilizza i metodi per specificare un formato, e.g. getUint8(offset).

    Nella maggior parte dei casi creiamo ed operiamo direttamente sui TypedArray, tenendo ArrayBuffer sotto copertura, come denominatore comune. Possiamo accedervi come .buffer e creare un altro visualizzatore, se necessario.

    Ci sono due ulteriori termini, utilizzati nei descrittori dei metodi che operano sui dati binari:

    • ArrayBufferView un termine ombrello che fa riferimento a tutti questi tipi di visualizzatori.
    • BufferSource un termine ombrello per ArrayBuffer o ArrayBufferView.

    Vederemo questi termini nei prossimi capitoli. BufferSource uno dei termini pi comuni, poich equivale a qualsiasi tipo di dato binario; un ArrayBuffer o un suo visualizzatore.

    Eccovi un cheatsheet:

    []

    Esercizi

    Dato un array di Uint8Array, scrivete una funzione concat(arrays) che ritorna una loro concatenazione in un singolo array.

    Apri una sandbox con i test.

    soluzione
    function concat(arrays) {
      // somma delle lunghezze dei singoli array
      let totalLength = arrays.reduce((acc, value) => acc + value.length, 0);
    
      if (!arrays.length) return null;
    
      let result = new Uint8Array(totalLength);
    
      // ogni array, lo copiamo in result
      // l'array successivo viene copiato subito dopo quello precedente
      let length = 0;
      for(let array of arrays) {
        result.set(array, length);
        length += array.length;
      }
    
      return result;
    }

    Apri la soluzione con i test in una sandbox.

    Mappa del tutorial

    Commenti

    leggi questo prima di lasciare un commento
    • Per qualsiasi suggerimento - per favore, apri una issue su GitHub o una pull request, piuttosto di lasciare un commento.
    • Se non riesci a comprendere quanto scitto nell'articolo ti preghiamo di fornire una spiegazione chiara.
    • Per inserire delle righe di codice utilizza il tag <code>, per molte righe includile nel tag <pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page