Kulcskérdések

Nem minden adat az, aminek látszik

Van az az ősi maja mondás, hogy számítógéppel rengeteg olyan problémát lehet megoldani, amit számítógép nélkül nem is kellene megoldanunk. Ma is egy ilyen problémán fogunk egy kicsit rágódni. Kísérőnek tudom ajánlani még a Wat című mini-előadást, 2012-es évjárat, ennek ellenére remekül passzol a mai témánkhoz.

Saját kis Wat-pillanatom a Buffer osztállyal kezdődött. Vegyünk is rögtön kettőt belőle.

$ node
> data1 = Buffer.from([0xf5, 0xcf, 0xe2, 0xf0, 0xef])
<Buffer f5 cf e2 f0 ef>
> data2 = Buffer.from([0xfe, 0x99, 0x88, 0xeb, 0xd9])
<Buffer fe 99 88 eb d9>

Ez a két buffer szabad szemmel is jól láthatóan különbözik egymástól, de ezt a Node.js is meg tudja nekünk erősíteni:

> data1 === data2
false

Eddig minden rendben, de nézzük csak meg a következő példát.

> container = {}
{}
> container[data1] = 'foo'
'foo'
> container[data2]
???

Vajon mi lesz az utolsó kifejezés értéke?

a) null
b) undefined
c) fekete lyuk keletkezik a Node.js interpreter helyén
d) semmi

Biztos vannak, akik a b-vel mennének, vagy a Node.js-t jobban ismerők talán a c-vel. De a helyes válasz annyira borzalmas, hogy még csak az opciók között sincs.

> container[data2]
'foo'

Mi is történik a háttérben? Egy object kulcsa nem lehet Buffer típusú, úgyhogy automatikusan hívódik rajta egy toString függvény. A Buffer esetén ez kaphat egy encoding paramétert is, de ha nem kap, akkor utf8 lesz az alapértelmezett.

Jóképű byte sorozatunk mit sem tud arról, hogy hogyan kell valid UTF-8-ként viselkedni (ezért is választottuk példaként), ezért az összes byte lecserélődik benne a Unicode helyettesítő karakterre, ami így néz ki: �.

Mivel mindkét bufferünk ettől a problémától szenved, ezért a konvertálás végére mindkettőből csak öt darab helyettesítő karakter marad.

> data1.toString() === data2.toString()
true
> container
{ '�����': 'foo' }

Így már azért el lehet azt fogadni, hogy miért az első adathoz tartozó értéket kapjuk meg a második adatra is. Most képzeljük el ezt a helyzetet egy in-memory cache réteg mélyén, amiből csak annyit érzékelünk, hogy százezerből egyszer valami baj van a cache-ből jövő adattal. A móka és kacagás garantált.

Mit lehet ellene tenni? Valószínűleg akkor járunk a legjobban, ha nem használjuk kulcsként a Buffer típust, vagy ha mindenképpen szükséges, akkor mi magunk végezzük el a toString hívást egy másik encoding-ot használva. Az alábbiak bármelyike megfelelően működhet erre a célra:

> data1.toString('hex') === data2.toString('hex')
false
> data1.toString('base64') === data2.toString('base64')
false
> data1.toString('binary') === data2.toString('binary')
false

This post is also available in english: Key questions

Hozzáfűznél valamit?

Dobj egy emailt a blog kukac deadlime pont hu címre.

Feliratkoznál?

Az RSS feed-et ajánljuk, ha kedveled a régi jó dolgokat.