Jak przekonwertować obiekt SlateBuffer na ciąg znaków i z niego

Renato Mangini

ArrayBuffers służą do transportowania danych nieprzetworzonych i są używane przez kilka nowych interfejsów API, w tym WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) i WebWorkers. Jednak ponieważ pojawiły się one niedawno w świecie JavaScriptu, czasami są źle interpretowane lub używane w niewłaściwy sposób.

Semantycznie ArrayBuffer to po prostu tablica bajtów wyświetlana za pomocą określonej maski. Ta maska, czyli instancja ArrayBufferView, określa, jak bajty są dopasowywane do oczekiwanej struktury treści. Jeśli na przykład wiesz, że bajty w ArrayBuffer reprezentują tablicę 16-bitowych liczb całkowitych bez znaku, wystarczy owinąć ArrayBuffer w widoku Uint16Array i można manipulować jego elementami za pomocą nawiasów tak, jakby Uint16Array była tablicą liczb całkowitych:

// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) {   // 258 === 0x0102
    console.log("ok");
}
bufView[0] = 255;    // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]

Jednym z częstych praktycznych pytań dotyczących ArrayBuffer jest to, jak zamienić String na ArrayBuffer i odwrotnie. Ponieważ ArrayBuffer jest w istocie tablicą bajtów, ta konwersja wymaga, aby obie strony uzgodniły sposób reprezentacji znaków w ciągu znaków jako bajtów. Prawdopodobnie widzisz tę „umowę” po raz kolejny: jest to kodowanie znaków ciągu tekstowego (a standardowe „warunki umowy” to na przykład Unicode UTF-16 i iso8859-1). Zakładając, że Ty i druga strona uzgodziliście kodowanie UTF-16, kod konwersji może wyglądać tak:

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

Uwaga: Uint16Array. To widok ArrayBuffer, który wyrównuje bajty ArrayBuffers jako elementy 16-bitowe. Nie obsługuje kodowania znaków, które jest traktowane jako Unicode przez tagi String.fromCharCode i str.charCodeAt.

Popularne pytanie na StackOverflow dotyczące tego zagadnienia ma wysoko ocenioną odpowiedź z nieco zawiłym rozwiązaniem konwersji: utwórz obiekt FileReader, który będzie pełnić funkcję konwertera, i prześlij do niego obiekt Blob zawierający ciąg znaków. Chociaż ta metoda działa, jest słabo czytelna i prawdopodobnie powolna. Ponieważ nieuzasadnione podejrzenia były przyczyną wielu błędów w historii ludzkości, zastosujmy tu bardziej naukowe podejście. jsperf'ed the two methods, a wynik potwierdza moje podejrzenia. Tutaj możesz obejrzeć wersję demonstracyjną.

W Chrome 20 użycie kodu manipulacji bezpośredniej ArrayBuffer opisanego w tym artykule jest prawie 27 razy szybsze niż użycie metody FileReader/Blob.