איך להמיר את ArrayBuffer למחרוזת וממנה

רנאטו מנגיני

ArrayBuffers משמשים להעברת נתונים גולמיים ומספר ממשקי API חדשים מסתמכים עליהם, כולל WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) ו-WebWorkers. עם זאת, מאחר שלאחרונה הם הגיעו לעולם ה-JavaScript, לפעמים הם מתפרשים בצורה שגויה או שמשתמשים בהם לרעה.

מבחינה סמנטית, ArrayBuffer הוא מערך של בייטים שמוצגים דרך מסיכה ספציפית. המסכה הזו, שהיא מכונה של ArrayBufferView, מגדירה את ההתאמה של הבייטים למבנה הצפוי של התוכן. לדוגמה, אם יודעים שהבייטים ב-ArrayBuffer מייצגים מערך של מספרים שלמים לא חתומים של 16 סיביות, צריך רק לעטוף את ה-ArrayBuffer בתצוגת Uint16Array, ואפשר לשנות את הרכיבים שלו באמצעות תחביר הסוגריים כאילו Uint16Array היה מערך שלם:

// 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]

אחת השאלות המעשיות הנפוצות לגבי ArrayBuffer היא איך להמיר String ל-ArrayBuffer ולהיפך. מאחר ש-ArrayBuffer הוא למעשה מערך בייטים, ההמרה הזו דורשת ששני הקצוות יסכימו על האופן שבו צריך לייצג את התווים במחרוזת כבייטים. בטח כבר ראיתם את ה'הסכם' הזה בעבר: זהו קידוד התווים של המחרוזת (ו'תנאי ההסכם' הרגילים הם, לדוגמה, Unicode UTF-16 ו-iso8859-1). לכן, נניח שאתם והצד השני הסכמתם על קידוד UTF-16, קוד ההמרות יכול להיראות כך:

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;
}

יש לשים לב לשימוש ב-Uint16Array. זוהי תצוגת ArrayBuffer שמיישרת בייטים של ArrayBuffer כרכיבים של 16 ביט. היא לא מתמודדת עם קידוד התווים בעצמו, והשיטה הזו מטופלת כ-Unicode על ידי String.fromCharCode ו-str.charCodeAt.

לשאלה נפוצה בנושא StackOverflow יש תשובה עם מספר גבוה של הצבעה ופתרון קצת מסובך להמרה: צריך ליצור FileReader כדי להגדיר ממיר ולהזין Blob שמכיל את המחרוזת. למרות שהשיטה הזו פועלת, הקריאוּת שלה לא טובה ואני חושד שהיא איטית. מכיוון שחשדות חסרי בסיס הובילו לטעויות רבות בהיסטוריה של האנושות, בואו ננקוט גישה מדעית כאן. הפעלתי את שתי השיטות והתוצאה מוכיחה את החשד שלי וכאן אפשר לראות את ההדגמה.

ב-Chrome 20, השימוש בקוד המניפולציה הישיר ArrayBuffer שמופיע במאמר הזה מהיר כמעט פי 27 מאשר השימוש בשיטה FileReader/Blob.