PÅi vývoji webů se setkáváme s binárnÃmi daty pÅevážnÄ pÅi práci se soubory (vytváÅenÃ, ukládánÃ, stahovánÃ). Dalšà typický pÅÃpad použità je zpracovánà obrázků.
V JavaScriptu je to všechno proveditelné a binárnà operace jsou vysoce výkonné.
Mohou vÅ¡ak způsobit menšà zmatenÃ, protože pro práci s nimi existuje mnoho tÅÃd. Jmenujme nÄkteré:
ArrayBuffer,Uint8Array,DataView,Blob,File, atd.
Ve srovnánà s jinými jazyky jsou binárnà data v JavaScriptu implementována nestandardnÃm způsobem. Ale když si to vÅ¡echno utÅÃdÃme, zaÄne nám to rychle pÅipadat jednoduché.
ZákladnÃm binárnÃm objektem je ArrayBuffer â odkaz na souvislou oblast pamÄti pevné délky.
VytvoÅÃme jej následovnÄ:
let buffer = new ArrayBuffer(16); // vytvoÅÃme buffer o délce 16
alert(buffer.byteLength); // 16
TÃm obsadÃme souvislou oblast pamÄti o délce 16 bytů a vyplnÃme ji nulami.
ArrayBuffer nenà pole niÄehoVyhnÄme se možnému zdroji zmatků. ArrayBuffer nemá nic spoleÄného s Array:
- Má pevnou délku, kterou nemůžeme zvýšit ani snÞit.
- V pamÄti zabÃrá pÅesnÄ uvedené množstvà mÃsta.
- Pro pÅÃstup k jednotlivým bytům je zapotÅebà dalšà objekt ânáhleduâ, nenà to možné pomocÃ
buffer[index].
ArrayBuffer je oblast v pamÄti. Co je v nà uloženo? O tom nemá ponÄtÃ. Jen planá posloupnost bytů.
K manipulaci s ArrayBuffer musÃme použÃt objekt ânáhleduâ.
Objekt náhledu sám o sobÄ nic neukládá. Jsou to jen âbrýleâ, které nám poskytujà interpretaci bytů uložených v ArrayBuffer.
NapÅÃklad:
Uint8Arrayâ zacházà s každým bytem vArrayBufferjako se samostatným ÄÃslem s možnými hodnotami od 0 do 255 (byte má 8 bitů, takže nemůže uschovat vÄtšà ÄÃslo). Taková hodnota se nazývá â8-bitové celé ÄÃslo bez znaménkaâ.Uint16Arrayâ zacházà s každými 2 byty jako s celým ÄÃslem s možnými hodnotami od 0 do 65535. To se nazývá â16-bitové celé ÄÃslo bez znaménkaâ.Uint32Arrayâ zacházà s každými 4 byty jako s celým ÄÃslem s možnými hodnotami od 0 do 4294967295. To se nazývá â32-bitové celé ÄÃslo bez znaménkaâ.Float64Arrayâ zacházà s každými 8 byty jako s ÄÃslem s pohyblivou Åádovou Äárkou s možnými hodnotami od5.0x10-324do1.8x10308.
Binárnà data v ArrayBuffer o délce 16 bytů je tedy možné interpretovat jako 16 âmalých ÄÃselâ, nebo 8 vÄtÅ¡Ãch ÄÃsel (každé o 2 bytech), nebo 4 jeÅ¡tÄ vÄtšà ÄÃsla (každé o 4 bytech), nebo 2 hodnoty s pohyblivou Åádovou Äárkou s vysokou pÅesnostà (každá o 8 bytech).
ArrayBuffer je jádrový objekt, koÅen vÅ¡eho, planá binárnà data.
Když do nÄj vÅ¡ak chceme zapisovat, iterovat nad nÃm, v zásadÄ pro témÄÅ jakoukoli operaci â musÃme použÃt náhled, napÅÃklad:
let buffer = new ArrayBuffer(16); // vytvoÅÃme buffer o délce 16
let náhled = new Uint32Array(buffer); // zacházÃme s nÃm jako s posloupnostà 32-bitových celých ÄÃsel
alert(Uint32Array.BYTES_PER_ELEMENT); // 4 byty na jedno ÄÃslo
alert(náhled.length); // 4, poÄet ÄÃsel, která dokáže uložit
alert(náhled.byteLength); // 16, velikost v bytech
// zapiÅ¡me do nÄj hodnotu
náhled[0] = 123456;
// iterujme nad hodnotami
for(let ÄÃslo of náhled) {
alert(ÄÃslo); // 123456, pak 0, 0, 0 (celkem 4 hodnoty)
}
TypedArray
SpoleÄným pojmem pro vÅ¡echny tyto náhledy (Uint8Array, Uint32Array, atd.) je TypedArray â typové pole. VÅ¡echny majà spoleÄnou sadu metod a vlastnostÃ.
ProsÃme vÅ¡imnÄte si, že neexistuje konstruktor s názvem TypedArray. Je to jen spoleÄný âzastÅeÅ¡ujÃcÃâ pojem, který pÅedstavuje jeden z náhledů na ArrayBuffer: Int8Array, Uint8Array a tak dále. Ãplný seznam bude brzy následovat.
Když vidÃte nÄco jako new TypedArray, znamená to cokoli z new Int8Array, new Uint8Array, atd.
Typová pole se chovajà stejnÄ jako bÄžná pole: majà indexy a jsou iterovatelná.
Konstruktor typového pole (aÅ¥ je to Int8Array nebo Float64Array, na tom nezáležÃ) se chová různÄ v závislosti na typech svých argumentů.
Má 5 možných variant argumentů:
new TypedArray(buffer, [poziceBytu], [délka]);
new TypedArray(objekt);
new TypedArray(typovéPole);
new TypedArray(délka);
new TypedArray();
-
Pokud je uveden argument
ArrayBuffer, náhled se vytvoÅà nad nÃm. Tuto syntaxi jsme již použili.NepovinnÄ můžeme uvést
poziceBytu, což je pozice, od které se má zaÄÃt (standardnÄ 0), adélka(standardnÄ až do konce bufferu). Pak se náhled vytvoÅà jen nad ÄástÃbuffer. -
Pokud je uvedeno
Arraynebo objekt podobný poli, vytvoÅà se typové pole stejné délky a obsah se do nÄj zkopÃruje.To můžeme použÃt k pÅedvyplnÄnà pole daty:
let pole = new Uint8Array([0, 1, 2, 3]); alert( pole.length ); // 4, vzniklo binárnà pole o stejné délce alert( pole[1] ); // 1, zaplnilo se 4 byty (8-bitová celá ÄÃsla bez znaménka) se zadanými hodnotami -
Pokud je uvedeno jiné
TypedArray, stane se totéž: vytvoÅà se typové pole stejné délky a zkopÃrujà se hodnoty. PÅi tomto procesu se hodnoty pÅevedou na nový typ, je-li to nutné.let pole16 = new Uint16Array([1, 1000]); let pole8 = new Uint8Array(pole16); alert( pole8[0] ); // 1 alert( pole8[1] ); // 232, snažilo se zkopÃrovat 1000, ale 1000 se nevejde do 8 bitů (vysvÄtleno dále) -
Pro ÄÃselný argument
délkase vytvoÅà typové pole, které bude obsahovat uvedený poÄet prvků. Jeho délka v bytech budedélkanásobená poÄtem bytů v jednom prvkuTypedArray.BYTES_PER_ELEMENT:let pole = new Uint16Array(4); // vytvoÅà typové pole pro 4 celá ÄÃsla alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 byty na ÄÃslo alert( pole.byteLength ); // 8 (velikost v bytech) -
Bez argumentů se vytvoÅà typové pole s nulovou délkou.
Můžeme vytvoÅit TypedArray pÅÃmo, bez uvedenà ArrayBuffer. Náhled vÅ¡ak nemůže existovat bez podkladového ArrayBuffer, takže ten se vytvoÅà automaticky ve vÅ¡ech uvedených pÅÃpadech kromÄ prvnÃho (kdy je pÅedán).
Pro pÅÃstup k podkladovému ArrayBuffer sloužà následujÃcà vlastnosti TypedArray:
bufferâ odkaz naArrayBuffer.byteLengthâ délkaArrayBuffer.
Kdykoli tedy můžeme pÅejÃt od jednoho náhledu k druhému:
let pole8 = new Uint8Array([0, 1, 2, 3]);
// jiný náhled na stejná data
let pole16 = new Uint16Array(pole8.buffer);
Seznam typových polà je následujÃcÃ:
Uint8Array,Uint16Array,Uint32Arrayâ pro celá ÄÃsla o velikosti 8, 16 a 32 bitů.Uint8ClampedArrayâ pro 8-bitová celá ÄÃsla, pÅi pÅiÅazenà budou âstlaÄenaâ (clamp, viz dále).
Int8Array,Int16Array,Int32Arrayâ pro celá ÄÃsla se znaménkem (mohou být záporná).Float32Array,Float64Arrayâ pro ÄÃsla s pohyblivou Åádovou Äárkou se znaménkem o velikosti 32 a 64 bitů.
int8 nebo podobný typ pro jedinou hodnotuProsÃme vÅ¡imnÄte si, že i pÅes názvy jako Int8Array v JavaScriptu neexistuje typ pro jedinou hodnotu jako int nebo int8.
Je to logické, neboÅ¥ Int8Array nenà pole tÄchto jednotlivých hodnot, ale náhled na ArrayBuffer.
Chovánà pÅi pÅekroÄenà mezÃ
Co se stane, když se pokusÃme zapsat do typového pole hodnotu mimo jeho meze? Nenastane chyba, ale pÅebyteÄné bity budou odÅÃznuty.
Pokusme se napÅÃklad uložit 256 do Uint8Array. 256 je v binárnà podobÄ 100000000 (9 bitů), ale Uint8Array poskytuje pro každou hodnotu jen 8 bitů, což dává dostupný rozsah od 0 do 255.
Pro vÄtšà ÄÃsla se uložà jen 8 bitů zprava (ménÄ významných) a zbytek se odÅÃzne:
Dostaneme tedy nulu.
Pro 257 je binárnà podoba 100000001 (9 bitů), uložà se 8 bitů zprava, v poli tedy budeme mÃt 1:
Jinými slovy, uložà se zbytek po dÄlenà tohoto ÄÃsla ÄÃslem 28.
Následuje ukázka:
let uint8array = new Uint8Array(16);
let ÄÃslo = 256;
alert(ÄÃslo.toString(2)); // 100000000 (binárnà reprezentace)
uint8array[0] = 256;
uint8array[1] = 257;
alert(uint8array[0]); // 0
alert(uint8array[1]); // 1
Uint8ClampedArray je v tomto smÄru zvláštnÃ, jeho chovánà je odliÅ¡né. MÃsto každého ÄÃsla vÄtÅ¡Ãho než 255 se uložà 255 a mÃsto každého záporného ÄÃsla se uložà 0. Toto chovánà je užiteÄné pÅi zpracovánà obrázků.
Metody TypedArray
TypedArray obsahuje metody bÄžného Array s urÄitými výjimkami.
Můžeme nad nÃm iterovat, volat map, slice, find, reduce atd.
Je tady vÅ¡ak nÄkolik vÄcÃ, které dÄlat nemůžeme:
- Nenà zde
spliceâ nemůžeme âsmazatâ hodnotu, protože typová pole jsou náhledy na buffer a ten pÅedstavuje pevnou, souvislou oblast pamÄti. Jediné, co můžeme dÄlat, je pÅiÅadit nulu. - Nenà zde metoda
concat.
Jsou tady vÅ¡ak dvÄ dalšà metody:
pole.set(zdrojovéPole, [pozice])zkopÃruje vÅ¡echny prvky zezdrojovéPoledopole, poÄÃnajÃc odpozice(standardnÄ 0).pole.subarray([zaÄátek, konec])vytvoÅà nový náhled na stejný typ odzaÄátekdokonec(nebude zahrnut). Podobá se metodÄslice(ta je rovnÄž podporována), ale nic se nekopÃruje â vytvoÅà se jen nový náhled, který bude pracovat nad zadanou Äástà dat.
Tyto metody nám umožÅujà typová pole kopÃrovat, smÄÅ¡ovat, vytváÅet nová pole z existujÃcÃch a podobnÄ.
DataView
DataView je speciálnÃ, vysoce flexibilnà âbeztypovýâ náhled na ArrayBuffer, který nám umožÅuje pÅistupovat k datům na jakékoli pozici v jakémkoli formátu.
- U typových polà je formát stanoven konstruktorem. Celé pole se považuje za uniformnÃ. Jeho i-tý Älen je
pole[i]. - V
DataViewpÅistupujeme k datům pomocà metod jako.getUint8(i)nebo.getUint16(i). Formát si volÃme až pÅi volánà metody, ne pÅi vytvoÅenÃ.
Syntaxe:
new DataView(buffer, [poziceBytu], [délkaVBytech])
bufferâ podkladovýArrayBuffer. Na rozdÃl od typových polÃDataViewnevytváÅà buffer sám o sobÄ. MusÃme ho již mÃt pÅipravený.poziceBytuâ pozice poÄáteÄnÃho bytu náhledu (standardnÄ 0).délkaVBytechâ délka náhledu v bytech (standardnÄ až do koncebuffer).
NapÅÃklad zde vytahujeme ze stejného bufferu ÄÃsla v různých formátech:
// binárnà pole 4 bytů, všechny majà nejvyššà možnou hodnotu 255
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;
let dataView = new DataView(buffer);
// zÃskáme 8-bitové ÄÃslo na pozici 0
alert( dataView.getUint8(0) ); // 255
// nynà zÃskáme 16-bitové ÄÃslo na pozici 0, skládá se ze 2 bytů, spoleÄnÄ interpretovaných jako 65535
alert( dataView.getUint16(0) ); // 65535 (nejvyššà 16-bitové celé ÄÃslo bez znaménka)
// zÃskáme 32-bitové ÄÃslo na pozici 0
alert( dataView.getUint32(0) ); // 4294967295 (nejvyššà 32-bitové celé ÄÃslo bez znaménka)
dataView.setUint32(0, 0); // nastavà 4-bytové ÄÃslo na nulu, tedy nastavà vÅ¡echny byty na 0
DataView je vynikajÃcÃ, když ukládáme do stejného bufferu data v různých formátech. NapÅÃklad když ukládáme posloupnost dvojic (16-bitové celé ÄÃslo, 32-bitové ÄÃslo s pohyblivou Åádovou Äárkou), DataView nám k nim umožÅuje snadno pÅistupovat.
ShrnutÃ
ArrayBuffer je jádrový objekt, odkaz na souvislou oblast pamÄti pevné délky.
K provedenà témÄÅ jakékoli operace na ArrayBuffer potÅebujeme náhled.
- Může to být
TypedArray:Uint8Array,Uint16Array,Uint32Arrayâ pro celá ÄÃsla bez znaménka o velikosti 8, 16 a 32 bitů.Uint8ClampedArrayâ pro 8-bitová celá ÄÃsla, pÅi pÅiÅazenà jsou âstlaÄenaâ.Int8Array,Int16Array,Int32Arrayâ pro celá ÄÃsla se znaménkem (mohou být záporná).Float32Array,Float64Arrayâ pro ÄÃsla s pohyblivou Åádovou Äárkou se znaménkem o velikosti 32 a 64 bitů.
- Nebo
DataViewâ náhled, který ke specifikaci formátu použÃvá metody, napÅ.getUint8(pozice).
Ve vÄtÅ¡inÄ pÅÃpadů vytváÅÃme a pracujeme s typovými poli a ArrayBuffer ponecháváme pod pláštÄm jako âspoleÄného jmenovateleâ. Pokud to potÅebujeme, můžeme k nÄmu pÅistoupit pomocà .buffer a vytvoÅit jiný náhled.
PÅi popisech metod pracujÃcÃch nad binárnÃmi daty se použÃvajà jeÅ¡tÄ následujÃcà dva pojmy:
ArrayBufferViewje zastÅeÅ¡ujÃcà pojem pro vÅ¡echny tyto druhy náhledů.BufferSourceje zastÅeÅ¡ujÃcà pojem proArrayBufferaArrayBufferView.
Tyto pojmy uvidÃme v dalÅ¡Ãch kapitolách. BufferSource je jeden z nejÄastÄji použÃvaných pojmů, neboÅ¥ znamená âjakýkoli druh binárnÃch datâ â ArrayBuffer nebo náhled na nÄj.
Zde je pÅehled:
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)