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