Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.
Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.
Starting from Firefox 57, only extensions developed using WebExtensions APIs will be supported on Desktop Firefox and Firefox for Android.
Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to use WebExtensions APIs if they can. See the "Compatibility Milestones" document for more information.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
The aims of this library are:
- to create a C-like interface for strings (i.e., an array of character codes — an
ArrayBufferView
in JavaScript) based upon the JavaScriptArrayBuffer
interface - to create a highly extensible library that anyone can extend by adding methods to the object
StringView.prototype
- to create a collection of methods for such string-like objects (since now:
stringView
s) which work strictly on arrays of numbers rather than on creating new immutable JavaScript strings - to work with Unicode encodings other than JavaScript's default UTF-16
DOMString
s
Introduction
As web applications become more and more powerful, adding features such as audio and video manipulation, access to raw data using WebSockets, and so forth, it has become clear that there are times when it would be helpful for JavaScript code to be able to quickly and easily manipulate raw binary data. In the past, this had to be simulated by treating the raw data as a string and using the charCodeAt()
method to read the bytes from the data buffer.
However, this is slow and error-prone, due to the need for multiple conversions (especially if the binary data is not actually byte-format data, but, for example, 32-bit integers or floats).
JavaScript typed arrays provide a mechanism for accessing raw binary data much more efficiently. The StringView
constructor is one level above typed arrays.
The code
stringview.js
"use strict"; /*\ |*| |*| :: Number.isInteger() polyfill :: |*| |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger |*| \*/ if (!Number.isInteger) { Number.isInteger = function isInteger (nVal) { return typeof nVal === "number" && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal; }; } /*\ |*| |*| StringView - Mozilla Developer Network |*| |*| Revision #12, March 21st, 2017 |*| |*| https://developer.mozilla.org/en-US/Add-ons/Code_snippets/StringView |*| https://developer.mozilla.org/en-US/docs/User:fusionchess |*| https://github.com/madmurphy/stringview.js |*| |*| This framework is released under the GNU Lesser General Public License, version 3 or later. |*| http://www.gnu.org/licenses/lgpl-3.0.html |*| \*/ function StringView (vInput, sEncoding /* optional (default: UTF-8) */, nOffset /* optional */, nLength /* optional */) { var fTAView, aWhole, aRaw, fPutOutptCode, fGetOutptChrSize, nInptLen, nStartIdx = isFinite(nOffset) ? nOffset : 0, nTranscrType = 15; if (sEncoding) { this.encoding = sEncoding.toString(); } encSwitch: switch (this.encoding) { case "UTF-8": fPutOutptCode = StringView.putUTF8CharCode; fGetOutptChrSize = StringView.getUTF8CharLength; fTAView = Uint8Array; break encSwitch; case "UTF-16": fPutOutptCode = StringView.putUTF16CharCode; fGetOutptChrSize = StringView.getUTF16CharLength; fTAView = Uint16Array; break encSwitch; case "UTF-32": fTAView = Uint32Array; nTranscrType &= 14; break encSwitch; default: /* case "ASCII", or case "BinaryString" or unknown cases */ fTAView = Uint8Array; nTranscrType &= 14; } typeSwitch: switch (typeof vInput) { case "string": /* the input argument is a primitive string: a new buffer will be created. */ nTranscrType &= 7; break typeSwitch; case "object": classSwitch: switch (vInput.constructor) { case StringView: /* the input argument is a stringView: a new buffer will be created. */ nTranscrType &= 3; break typeSwitch; case String: /* the input argument is an objectified string: a new buffer will be created. */ nTranscrType &= 7; break typeSwitch; case ArrayBuffer: /* the input argument is an arrayBuffer: the buffer will be shared. */ aWhole = new fTAView(vInput); nInptLen = this.encoding === "UTF-32" ? vInput.byteLength >>> 2 : this.encoding === "UTF-16" ? vInput.byteLength >>> 1 : vInput.byteLength; aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? aWhole : new fTAView(vInput, nStartIdx, !isFinite(nLength) ? nInptLen - nStartIdx : nLength); break typeSwitch; case Uint32Array: case Uint16Array: case Uint8Array: /* the input argument is a typedArray: the buffer, and possibly the array itself, will be shared. */ fTAView = vInput.constructor; nInptLen = vInput.length; aWhole = vInput.byteOffset === 0 && vInput.length === ( fTAView === Uint32Array ? vInput.buffer.byteLength >>> 2 : fTAView === Uint16Array ? vInput.buffer.byteLength >>> 1 : vInput.buffer.byteLength ) ? vInput : new fTAView(vInput.buffer); aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? vInput : vInput.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); break typeSwitch; default: /* the input argument is an array or another serializable object: a new typedArray will be created. */ aWhole = new fTAView(vInput); nInptLen = aWhole.length; aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ? aWhole : aWhole.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen); } break typeSwitch; default: /* the input argument is a number, a boolean or a function: a new typedArray will be created. */ aWhole = aRaw = new fTAView(Number(vInput) || 0); } if (nTranscrType < 8) { var vSource, nOutptLen, nCharStart, nCharEnd, nEndIdx, fGetInptChrSize, fGetInptChrCode; if (nTranscrType & 4) { /* input is string */ vSource = vInput; nOutptLen = nInptLen = vSource.length; nTranscrType ^= this.encoding === "UTF-32" ? 0 : 2; /* ...or...: nTranscrType ^= Number(this.encoding !== "UTF-32") << 1; */ nStartIdx = nCharStart = nOffset ? Math.max((nOutptLen + nOffset) % nOutptLen, 0) : 0; nEndIdx = nCharEnd = (Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0) + nStartIdx, nOutptLen) : nOutptLen) - 1; } else { /* input is stringView */ vSource = vInput.rawData; nInptLen = vInput.makeIndex(); nStartIdx = nCharStart = nOffset ? Math.max((nInptLen + nOffset) % nInptLen, 0) : 0; nOutptLen = Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0), nInptLen - nCharStart) : nInptLen; nEndIdx = nCharEnd = nOutptLen + nCharStart; if (vInput.encoding === "UTF-8") { fGetInptChrSize = StringView.getUTF8CharLength; fGetInptChrCode = StringView.loadUTF8CharCode; } else if (vInput.encoding === "UTF-16") { fGetInptChrSize = StringView.getUTF16CharLength; fGetInptChrCode = StringView.loadUTF16CharCode; } else { nTranscrType &= 1; } } if (nOutptLen === 0 || nTranscrType < 4 && vSource.encoding === this.encoding && nCharStart === 0 && nOutptLen === nInptLen) { /* the encoding is the same, the length too and the offset is 0... or the input is empty! */ nTranscrType = 7; } conversionSwitch: switch (nTranscrType) { case 0: /* both the source and the new StringView have a fixed-length encoding... */ aWhole = new fTAView(nOutptLen); for (var nOutptIdx = 0; nOutptIdx < nOutptLen; aWhole[nOutptIdx] = vSource[nStartIdx + nOutptIdx++]); break conversionSwitch; case 1: /* the source has a fixed-length encoding but the new StringView has a variable-length encoding... */ /* mapping... */ nOutptLen = 0; for (var nInptIdx = nStartIdx; nInptIdx < nEndIdx; nInptIdx++) { nOutptLen += fGetOutptChrSize(vSource[nInptIdx]); } aWhole = new fTAView(nOutptLen); /* transcription of the source... */ for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx++) { nOutptIdx = fPutOutptCode(aWhole, vSource[nInptIdx], nOutptIdx); } break conversionSwitch; case 2: /* the source has a variable-length encoding but the new StringView has a fixed-length encoding... */ /* mapping... */ nStartIdx = 0; var nChrCode; for (nChrIdx = 0; nChrIdx < nCharStart; nChrIdx++) { nChrCode = fGetInptChrCode(vSource, nStartIdx); nStartIdx += fGetInptChrSize(nChrCode); } aWhole = new fTAView(nOutptLen); /* transcription of the source... */ for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode), nOutptIdx++) { nChrCode = fGetInptChrCode(vSource, nInptIdx); aWhole[nOutptIdx] = nChrCode; } break conversionSwitch; case 3: /* both the source and the new StringView have a variable-length encoding... */ /* mapping... */ nOutptLen = 0; var nChrCode; for (var nChrIdx = 0, nInptIdx = 0; nChrIdx < nCharEnd; nInptIdx += fGetInptChrSize(nChrCode)) { nChrCode = fGetInptChrCode(vSource, nInptIdx); if (nChrIdx === nCharStart) { nStartIdx = nInptIdx; } if (++nChrIdx > nCharStart) { nOutptLen += fGetOutptChrSize(nChrCode); } } aWhole = new fTAView(nOutptLen); /* transcription... */ for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode)) { nChrCode = fGetInptChrCode(vSource, nInptIdx); nOutptIdx = fPutOutptCode(aWhole, nChrCode, nOutptIdx); } break conversionSwitch; case 4: /* DOMString to ASCII or BinaryString or other unknown encodings */ aWhole = new fTAView(nOutptLen); /* transcription... */ for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { aWhole[nIdx] = vSource.charCodeAt(nIdx) & 0xff; } break conversionSwitch; case 5: /* DOMString to UTF-8 or to UTF-16 */ /* mapping... */ nOutptLen = 0; for (var nMapIdx = 0; nMapIdx < nInptLen; nMapIdx++) { if (nMapIdx === nCharStart) { nStartIdx = nOutptLen; } nOutptLen += fGetOutptChrSize(vSource.charCodeAt(nMapIdx)); if (nMapIdx === nCharEnd) { nEndIdx = nOutptLen; } } aWhole = new fTAView(nOutptLen); /* transcription... */ for (var nOutptIdx = 0, nChrIdx = 0; nOutptIdx < nOutptLen; nChrIdx++) { nOutptIdx = fPutOutptCode(aWhole, vSource.charCodeAt(nChrIdx), nOutptIdx); } break conversionSwitch; case 6: /* DOMString to UTF-32 */ aWhole = new fTAView(nOutptLen); /* transcription... */ for (var nIdx = 0; nIdx < nOutptLen; nIdx++) { aWhole[nIdx] = vSource.charCodeAt(nIdx); } break conversionSwitch; case 7: aWhole = new fTAView(nOutptLen ? vSource : 0); break conversionSwitch; } aRaw = nTranscrType > 3 && (nStartIdx > 0 || nEndIdx < aWhole.length - 1) ? aWhole.subarray(nStartIdx, nEndIdx) : aWhole; } this.buffer = aWhole.buffer; this.bufferView = aWhole; this.rawData = aRaw; Object.freeze(this); } /* CONSTRUCTOR'S METHODS */ StringView.loadUTF8CharCode = function (aChars, nIdx) { /* The ISO 10646 view of UTF-8 considers valid codepoints encoded by 1-6 bytes, * while the Unicode view of UTF-8 in 2003 has limited them to 1-4 bytes in order to * match UTF-16's codepoints. In front of a 5/6-byte sequence StringView tries to * encode it in any case. */ var nLen = aChars.length, nPart = aChars[nIdx]; return nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* (nPart - 252 << 30) may be not safe in ECMAScript! So...: */ /* six bytes */ (nPart - 252) * 1073741824 + (aChars[nIdx + 1] - 128 << 24) + (aChars[nIdx + 2] - 128 << 18) + (aChars[nIdx + 3] - 128 << 12) + (aChars[nIdx + 4] - 128 << 6) + aChars[nIdx + 5] - 128 : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */ (nPart - 248 << 24) + (aChars[nIdx + 1] - 128 << 18) + (aChars[nIdx + 2] - 128 << 12) + (aChars[nIdx + 3] - 128 << 6) + aChars[nIdx + 4] - 128 : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */(nPart - 240 << 18) + (aChars[nIdx + 1] - 128 << 12) + (aChars[nIdx + 2] - 128 << 6) + aChars[nIdx + 3] - 128 : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */ (nPart - 224 << 12) + (aChars[nIdx + 1] - 128 << 6) + aChars[nIdx + 2] - 128 : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */ (nPart - 192 << 6) + aChars[nIdx + 1] - 128 : /* one byte */ nPart; }; StringView.putUTF8CharCode = function (aTarget, nChar, nPutAt) { var nIdx = nPutAt; if (nChar < 0x80 /* 128 */) { /* one byte */ aTarget[nIdx++] = nChar; } else if (nChar < 0x800 /* 2048 */) { /* two bytes */ aTarget[nIdx++] = 0xc0 /* 192 */ + (nChar >>> 6); aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); } else if (nChar < 0x10000 /* 65536 */) { /* three bytes */ aTarget[nIdx++] = 0xe0 /* 224 */ + (nChar >>> 12); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); } else if (nChar < 0x200000 /* 2097152 */) { /* four bytes */ aTarget[nIdx++] = 0xf0 /* 240 */ + (nChar >>> 18); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); } else if (nChar < 0x4000000 /* 67108864 */) { /* five bytes */ aTarget[nIdx++] = 0xf8 /* 248 */ + (nChar >>> 24); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); } else /* if (nChar <= 0x7fffffff) */ { /* 2147483647 */ /* six bytes */ aTarget[nIdx++] = 0xfc /* 252 */ + /* (nChar >>> 30) may be not safe in ECMAScript! So...: */ (nChar / 1073741824); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 24) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */); aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */); } return nIdx; }; StringView.getUTF8CharLength = function (nChar) { return nChar < 0x80 ? 1 : nChar < 0x800 ? 2 : nChar < 0x10000 ? 3 : nChar < 0x200000 ? 4 : nChar < 0x4000000 ? 5 : 6; }; StringView.loadUTF16CharCode = function (aChars, nIdx) { /* UTF-16 to DOMString decoding algorithm */ var nFrstChr = aChars[nIdx]; return nFrstChr > 0xD7BF /* 55231 */ && nIdx + 1 < aChars.length ? (nFrstChr - 0xD800 /* 55296 */ << 10) + aChars[nIdx + 1] + 0x2400 /* 9216 */ : nFrstChr; }; StringView.putUTF16CharCode = function (aTarget, nChar, nPutAt) { var nIdx = nPutAt; if (nChar < 0x10000 /* 65536 */) { /* one element */ aTarget[nIdx++] = nChar; } else { /* two elements */ aTarget[nIdx++] = 0xD7C0 /* 55232 */ + (nChar >>> 10); aTarget[nIdx++] = 0xDC00 /* 56320 */ + (nChar & 0x3FF /* 1023 */); } return nIdx; }; StringView.getUTF16CharLength = function (nChar) { return nChar < 0x10000 ? 1 : 2; }; /* Array of bytes to base64 string decoding */ StringView.b64ToUint6 = function (nChr) { return nChr > 64 && nChr < 91 ? nChr - 65 : nChr > 96 && nChr < 123 ? nChr - 71 : nChr > 47 && nChr < 58 ? nChr + 4 : nChr === 43 ? 62 : nChr === 47 ? 63 : 0; }; StringView.uint6ToB64 = function (nUint6) { return nUint6 < 26 ? nUint6 + 65 : nUint6 < 52 ? nUint6 + 71 : nUint6 < 62 ? nUint6 - 4 : nUint6 === 62 ? 43 : nUint6 === 63 ? 47 : 65; }; /* Base64 string to array encoding */ StringView.bytesToBase64 = function (aBytes) { var eqLen = (3 - (aBytes.length % 3)) % 3, sB64Enc = ""; for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { nMod3 = nIdx % 3; /* Uncomment the following line in order to split the output in lines 76-character long: */ /* if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; } */ nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24); if (nMod3 === 2 || aBytes.length - nIdx === 1) { sB64Enc += String.fromCharCode(StringView.uint6ToB64(nUint24 >>> 18 & 63), StringView.uint6ToB64(nUint24 >>> 12 & 63), StringView.uint6ToB64(nUint24 >>> 6 & 63), StringView.uint6ToB64(nUint24 & 63)); nUint24 = 0; } } return eqLen === 0 ? sB64Enc : sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "=="); }; StringView.base64ToBytes = function (sBase64, nBlockBytes) { var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, nOutLen = nBlockBytes ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockBytes) * nBlockBytes : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen); for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { nMod4 = nInIdx & 3; nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; if (nMod4 === 3 || nInLen - nInIdx === 1) { for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; } nUint24 = 0; } } return aBytes; }; StringView.makeFromBase64 = function (sB64Inpt, sEncoding, nByteOffset, nLength) { return new StringView(sEncoding === "UTF-16" || sEncoding === "UTF-32" ? StringView.base64ToBytes(sB64Inpt, sEncoding === "UTF-16" ? 2 : 4).buffer : StringView.base64ToBytes(sB64Inpt), sEncoding, nByteOffset, nLength); }; /* DEFAULT VALUES */ StringView.prototype.encoding = "UTF-8"; /* Default encoding... */ /* INSTANCES' METHODS */ StringView.prototype.makeIndex = function (nChrLength, nStartFrom) { var aTarget = this.rawData, nChrEnd, nRawLength = aTarget.length, nStartIdx = nStartFrom || 0, nIdxEnd = nStartIdx, nStopAtChr = isNaN(nChrLength) ? Infinity : nChrLength; if (nChrLength + 1 > aTarget.length) { throw new RangeError("StringView.prototype.makeIndex - The offset can\'t be major than the length of the array - 1."); } switch (this.encoding) { case "UTF-8": var nPart; for (nChrEnd = 0; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { nPart = aTarget[nIdxEnd]; nIdxEnd += nPart > 251 && nPart < 254 && nIdxEnd + 5 < nRawLength ? 6 : nPart > 247 && nPart < 252 && nIdxEnd + 4 < nRawLength ? 5 : nPart > 239 && nPart < 248 && nIdxEnd + 3 < nRawLength ? 4 : nPart > 223 && nPart < 240 && nIdxEnd + 2 < nRawLength ? 3 : nPart > 191 && nPart < 224 && nIdxEnd + 1 < nRawLength ? 2 : 1; } break; case "UTF-16": for (nChrEnd = nStartIdx; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) { nIdxEnd += aTarget[nIdxEnd] > 0xD7BF /* 55231 */ && nIdxEnd + 1 < aTarget.length ? 2 : 1; } break; default: nIdxEnd = nChrEnd = isFinite(nChrLength) ? nChrLength : nRawLength - 1; } if (nChrLength) { return nIdxEnd; } return nChrEnd; }; StringView.prototype.toBase64 = function (bWholeBuffer) { return StringView.bytesToBase64( bWholeBuffer ? ( this.bufferView.constructor === Uint8Array ? this.bufferView : new Uint8Array(this.buffer) ) : this.rawData.constructor === Uint8Array ? this.rawData : new Uint8Array(this.buffer, this.rawData.byteOffset, this.rawData.length << (this.rawData.constructor === Uint16Array ? 1 : 2)) ); }; StringView.prototype.subview = function (nCharOffset /* optional */, nCharLength /* optional */) { var nRawSubLen, nRawSubOffset, nSubOffset, nSubLen, bVariableLen = this.encoding === "UTF-8" || this.encoding === "UTF-16", nThisLen, nRawLen = this.rawData.length; if (nRawLen === 0) { return new StringView(this.buffer, this.encoding); } nThisLen = bVariableLen ? this.makeIndex() : nRawLen; nSubOffset = nCharOffset ? nCharOffset + 1 > nThisLen ? nThisLen : Math.max((nThisLen + nCharOffset) % nThisLen, 0) : 0; nSubLen = Number.isInteger(nCharLength) ? Math.max(nCharLength, 0) + nSubOffset > nThisLen ? nThisLen - nSubOffset : nCharLength : nThisLen - nSubOffset; if (nSubOffset === 0 && nSubLen === nThisLen) { return this; } if (bVariableLen) { nRawSubOffset = nSubOffset < nThisLen ? this.makeIndex(nSubOffset) : nThisLen; nRawSubLen = nSubLen ? this.makeIndex(nSubLen, nRawSubOffset) - nRawSubOffset : 0; } else { nRawSubOffset = nSubOffset; nRawSubLen = nSubLen; } if (this.encoding === "UTF-16") { nRawSubOffset <<= 1; } else if (this.encoding === "UTF-32") { nRawSubOffset <<= 2; } return new StringView(this.buffer, this.encoding, this.rawData.byteOffset + nRawSubOffset, nRawSubLen); }; StringView.prototype.forEachChar = function (fCallback, oThat, nChrOffset, nChrLen) { var aSource = this.rawData, nRawEnd, nRawIdx; if (this.encoding === "UTF-8" || this.encoding === "UTF-16") { var fGetInptChrSize, fGetInptChrCode; if (this.encoding === "UTF-8") { fGetInptChrSize = StringView.getUTF8CharLength; fGetInptChrCode = StringView.loadUTF8CharCode; } else if (this.encoding === "UTF-16") { fGetInptChrSize = StringView.getUTF16CharLength; fGetInptChrCode = StringView.loadUTF16CharCode; } nRawIdx = isFinite(nChrOffset) ? this.makeIndex(nChrOffset) : 0; nRawEnd = isFinite(nChrLen) ? this.makeIndex(nChrLen, nRawIdx) : aSource.length; for (var nChrCode, nChrIdx = 0; nRawIdx < nRawEnd; nChrIdx++) { nChrCode = fGetInptChrCode(aSource, nRawIdx); if (!oThat) { fCallback(nChrCode, nChrIdx, nRawIdx, aSource); } else { fCallback.call(oThat, nChrCode, nChrIdx, nRawIdx, aSource); } nRawIdx += fGetInptChrSize(nChrCode); } } else { nRawIdx = isFinite(nChrOffset) ? nChrOffset : 0; nRawEnd = isFinite(nChrLen) ? nChrLen + nRawIdx : aSource.length; for (nRawIdx; nRawIdx < nRawEnd; nRawIdx++) { if (!oThat) { fCallback(aSource[nRawIdx], nRawIdx, nRawIdx, aSource); } else { fCallback.call(oThat, aSource[nRawIdx], nRawIdx, nRawIdx, aSource); } } } }; StringView.prototype.valueOf = StringView.prototype.toString = function () { if (this.encoding !== "UTF-8" && this.encoding !== "UTF-16") { /* ASCII, UTF-32 or BinaryString to DOMString */ return String.fromCharCode.apply(null, this.rawData); } var fGetCode, fGetIncr, sView = ""; if (this.encoding === "UTF-8") { fGetIncr = StringView.getUTF8CharLength; fGetCode = StringView.loadUTF8CharCode; } else if (this.encoding === "UTF-16") { fGetIncr = StringView.getUTF16CharLength; fGetCode = StringView.loadUTF16CharCode; } for (var nChr, nLen = this.rawData.length, nIdx = 0; nIdx < nLen; nIdx += fGetIncr(nChr)) { nChr = fGetCode(this.rawData, nIdx); sView += String.fromCharCode(nChr); } return sView; };
The code above is also available on GitHub.
Manual
Methods overview
Constructor |
---|
stringView new StringView(stringView input, optional DOMString encoding, optional unsigned long startOffset, optional unsigned long length); stringView new StringView(DOMString input, optional DOMString encoding, optional unsigned long startOffset, optional unsigned long length); stringView new StringView(unsigned long input); stringView new StringView(arrayBuffer input, optional DOMString encoding, optional unsigned long byteOffset, optional unsigned long length); stringView new StringView(arrayBufferView input, optional DOMString encoding, optional unsigned long startOffset, optional unsigned long length); stringView new StringView(array input, optional DOMString encoding, optional unsigned long startOffset, optional unsigned long length); |
Constructor's methods |
stringView StringView.makeFromBase64(DOMString base64String, optional DOMString encoding, optional unsigned long byteOffset, optional unsigned long length) |
Other constructor's methods |
DOMString StringView.bytesToBase64( uint8Array uint8Array); |
uint8Array StringView.base64ToBytes(DOMString base64String, optional unsigned char charSize); |
unsigned long StringView.loadUTF8CharCode(arrayBufferView typedArray, unsigned long index); |
void StringView.putUTF8CharCode(arrayBufferView typedArray, unsigned long charCode, unsigned long index); |
unsigned char StringView.getUTF8CharLength(unsigned long charCode); |
unsigned long StringView.loadUTF16CharCode(arrayBufferView typedArray, unsigned long index); |
void StringView.putUTF16CharCode(arrayBufferView typedArray, unsigned long charCode, unsigned long index); |
unsigned char StringView.getUTF16CharLength(unsigned long charCode); |
unsigned char StringView.b64ToUint6(unsigned char charCode); |
unsigned char StringView.uint6ToB64(unsigned char uint6); |
Instances' methods |
unsigned long stringView.makeIndex(optional unsigned long charactersLength, optional unsigned long startFrom); |
DOMString stringView.toBase64(optional boolean wholeBuffer); |
stringView stringView.subview(unsigned long characterOffset, optional unsigned long charactersLength); |
void stringView.forEachChar(function callback, optional object thisObject, optional unsigned long characterOffset, optional unsigned long charactersLength); |
DOMString stringView.valueOf(); |
DOMString stringView.toString(); |
Properties overview
Attribute | Type | Description |
---|---|---|
encoding Read only |
DOMString |
A string expressing the encoding type. For actually supported values see here. |
buffer Read only |
arrayBuffer |
The buffer to be shared between stringView. rawData and stringView. bufferView view references. |
rawData Read only |
arrayBufferView |
An arrayBufferView containing the representation of the string as array of 8-bit, 16-bit, or 32-bit integers (depending on the chosen encoding). |
bufferView Read only |
arrayBufferView |
An arrayBufferView containing the representation of the whole buffer as array of 8-bit, 16-bit, or 32-bit integers (depending on the chosen encoding). |
Constructor
Syntax
new StringView(input[, encoding[, startOffset[, length]]])
Description
Create a new string-like object based upon an ArrayBuffer
.
Arguments
input
(required)- The argument the
stringView
is constructed from. It can be:- another stringView
- a string
- a number
- an
arrayBuffer
- a typed array of uint8Array, uint16Array, or uint32Array subclass
- any other kind of ordered object (like arrays, collections, etc.)
stringView
; otherwise it will become its content. The following table shows the behavior of theStringView
constructor.input
classstringView.buffer
stringView.rawData
stringView.bufferView
StringView
A new arrayBuffer will be created. Object reference to stringView.bufferView
or a new arrayBufferView ofstringView.buffer
will be created (depending onstartOffset
andlength
arguments passed).A new arrayBufferView of stringView.buffer
will be created.DOMString
A new arrayBuffer will be created. Object reference to stringView.bufferView
or a new arrayBufferView ofstringView.buffer
will be created (depending onstartOffset
andlength
arguments passed).A new arrayBufferView of stringView.buffer
will be created.Number
A new arrayBuffer will be created. Object reference to stringView.bufferView
.A new arrayBufferView of stringView.buffer
will be created.ArrayBuffer
Object reference to input.buffer
.Object reference to stringView.bufferView
or a new arrayBufferView ofstringView.buffer
will be created (depending onstartOffset
andlength
arguments passed).A new arrayBufferView of stringView.buffer
will be created.ArrayBufferView
Object reference to input.buffer
.Object reference to input
or a new arrayBufferView of the bufferinput.buffer
(depending on many factors regarding the position of the focused parts).Object reference to input
or a new arrayBufferView of the bufferinput.buffer
(depending on many factors regarding the position of the focused parts).other ordered object (like Array
, collections, etc.)A new arrayBuffer will be created. Object reference to stringView.bufferView
or a new arrayBufferView ofstringView.buffer
will be created (depending onstartOffset
andlength
arguments passed).A new arrayBufferView of stringView.buffer
will be created. encoding
Optional- A string expressing the encoding type. Possible values are:
UTF-8
(default)UTF-16
UTF-32
- Another custom string. In this case it will be treated as
ASCII ISO/IEC 8859-15
during conversion to and/or from string. However it is usable also for binary strings.
null
) it will default toUTF-8
. startOffset
Optional- A number expressing:
- The start offset of the new
stringView
in bytes if the input argument is anarrayBuffer
- The start offset of the new
stringView
in raw elements if the input argument is a typed array or other ordered object - The start offset of the new
stringView
in codepoints if the input argument is astringView
or a string
null
) it will default to0
. - The start offset of the new
length
Optional
A number expressing in codepoints the length of the new stringView
if the input
argument is a string
or a stringView
, or in raw elements if the input is a typed array, an arrayBuffer
or any other kind of ordered object (like Array
, collections, etc.). If not specified it will take the length of the input. It never can be more than the length of the input. If you want to see how to create a stringView bigger than its content, please, see this note.
Examples
var myStringView1 = new StringView("Hello world!"); // UTF-8 alert(myStringView1) // "Hello world!" var myStringView2 = new StringView(myStringView1, "UTF-16"); alert(myStringView1.buffer.byteLength); // 12 alert(myStringView2.buffer.byteLength); // 24
StringView
constructor's methods
makeFromBase64()
-
- Syntax
-
StringView.makeFromBase64(base64String[, encoding][, byteOffset][, length])
- Description
- Returns a new instance of
StringView
constructed decoding a given base64-encoded string. - Arguments
-
base64String
(required)- A base64-encoded string which will be decoded and copied into the new
stringView
object. encoding
Optional- A string expressing the encoding type. For possible values see here.
byteOffset
Optional- A number expressing the start offset in bytes of the new
stringView
. If not specified (or specified asnull
) it will be considered as0
. length
Optional- A number expressing the length in codepoints of the new
stringView
. If not specified it will take the length of the input.
- Examples
-
var myStringView = StringView.makeFromBase64("SGVsbG8gd29ybGQh"); alert(myStringView) // "Hello world!"
This function is useful in order to pass binary data containing strings. See also this example.
StringView
instances' properties
encoding
- A string expressing the encoding type. For possible values see here.
buffer
-
The buffer to be shared between
stringView.
rawData
andstringView.
bufferView
view references. rawData
-
An arrayBufferView containing the representation of the string as an array of 8-bit, 16-bit, or 32-bit integers (depending on the chosen encoding).
bufferView
-
An arrayBufferView containing the representation of the whole buffer as an array of 8-bit, 16-bit, or 32-bit integers (depending on the chosen encoding).
StringView
instances' methods
makeIndex()
-
- Syntax
-
stringView.makeIndex([charactersLength[, startFrom]])
- Description
- If the
charactersLength
argument is a number it will be taken as codepoints length andmakeIndex()
will return the index in elements of that position starting from 0. If thestartFrom
argument is passed the analysis will be done starting from it. If thecharactersLength
argument is omitted,makeIndex()
will return the length in codepoints (ASCII or UTF-encoded) of thestringView
object. - Arguments
-
charactersLength
Optional- A number expressing the distance in codepoints from
startFrom
of the index ofstringView.rawData
to be returned. startFrom
Optional- A number expressing the position in raw elements of the characters parts to skip. If omitted it will be considered as
0
.
- Examples
-
var myStringView = new StringView("\u6432\u6432\u6432\u6432"); alert(myStringView.makeIndex()) // 4 alert(myStringView.makeIndex(2)) // 6
…using the
startFrom
argument…var myStringView = StringView.makeFromBase64("5Lit5paHIGVzcGHDsW9sIEVuZ2xpc2gg4KS54KS/4KSo4KWN4KSm4KWAINin2YTYudix2KjZitipIHBvcnR1Z3XDqnMg4Kas4Ka+4KaC4Kay4Ka+INGA0YPRgdGB0LrQuNC5IOaXpeacrOiqniDgqKrgqbDgqJzgqL7gqKzgqY=="); alert(myStringView); /* Get the Hindi subview... */ var mySubview1 = myStringView.subview(19, 6); /* Get the Hindi subview passing an uint8Array as argument... */ var nHindiStart = myStringView.makeIndex(19); var nHindiEnd = myStringView.makeIndex(6, nHindiStart); var mySubview2 = new StringView(myStringView.rawData.subarray(nHindiStart, nHindiEnd), "UTF-8"); alert(mySubview1.rawData.length); // 18 alert(mySubview2.rawData.length); // 18
See also:
stringView.forEachChar()
.Performance note: Each invocation ofstringView.makeIndex()
runs a cycle for all characters contained in thestringView
object betweenstartFrom
andstartFrom
+charactersLength
. Dont't usestringView.makeIndex()
in a cycle as if it were a normallength
property. For custom cycles, look at the example proposed here. toBase64()
-
- Syntax
-
stringView.toBase64([wholeBuffer])
- Description
- Returns a base64-encoded string corresponding to the
stringView
or to its buffer. - Arguments
-
wholeBuffer
Optional- A boolean expressing whether the returned base64-encoded string will correspond to the whole buffer (
true
) or to the stringView (false
or omitted).
- Examples
-
var myStringView = new StringView("\u6432\u6432\u6432\u6432"); alert(StringView.makeFromBase64(myStringView.toBase64())) // 搲搲搲搲
See also: Base64 encoding and decoding.
subview()
-
- Syntax
-
stringView.subview(characterOffset[, charactersLength])
- Description
- Returns a new
stringView
object which will share the same buffer. ArgumentscharacterOffset
andcharactersLength
will be treated as inString.prototype.substr(characterOffset[, charactersLength)
(see). If you want to create a newstringView
object cloning without sharing the same buffer, look at this table. - Arguments
-
characterOffset
Optional- A number expressing (in codepoints) the location at which to begin extracting characters.
charactersLength
Optional- A number expressing (in codepoints) the location at which to stop extracting characters.
- Examples
-
var myStringView1 = new StringView("Hello world!"); var myStringView2 = myStringView1.subview(3, 7); alert(myStringView2); // "lo worl"
characterOffset
is a character index. The index of the first character is 0, and the index of the last character is 1 less than the length of thestringView.
subview
begins extracting characters atcharacterOffset
and collectscharactersLength
characters (unless it reaches the end of the string first, in which case it will return fewer).- If
characterOffset
is positive and is greater than or equal to the length of the string,substr
returns an empty string. - If
characterOffset
is negative,substr
uses it as a character index from the end of the string. IfcharacterOffset
is negative andabs(start)
is larger than the length of the string,substr
uses 0 as the start index. - If
charactersLength
is 0 or negative,substr
returns an empty string. IfcharactersLength
is omitted,substr
extracts characters to the end of the string.
forEachChar()
-
- Syntax
-
stringView.forEachChar(callback[, thisObject[, characterOffset[, charactersLength]]])
- Description
- Repeatedly invokes a function for each character of the
stringView
. - Arguments
-
callback
(required)- A function to be invoked for each character. It will be called with four arguments, expressing respectively:
charCode
- A number expressing the Unicode representation of the actual character (codepoint).
characterOffset
- A number expressing the position, in codepoints, of the passed character.
rawOffset
- A number expressing the position, in codepoints parts, of the passed character.
rawDataArray
- The array containing the raw data of the
stringView
.
callback.call(thisObject, charCode, characterOffset, rawOffset, rawDataArray)
. If the encoding is a fixed-length Unicode encoding,characterOffset
andrawOffset
will be the same number. thisObject
Optional- The
this
object upon which will be executed thecallback
function. characterOffset
Optional- A number expressing (in codepoints) the location at which to begin executing the
callback
function. charactersLength
Optional- A number expressing (in codepoints) the number of invocations of the
callback
function.
- Examples
-
function myCallback (charCode, characterOffset, rawOffset /*, rawDataArray */) { this.appendChild(document.createTextNode("char #" + characterOffset + ", raw index: " + rawOffset + ", character: " + String.fromCharCode(charCode))); this.appendChild(document.createElement("br")); } (new StringView("\u4367\uD889\uDE54\u4343\u5431")).forEachChar(myCallback, document.body);
Note:stringView.forEachChar()
executes a complete cycle through all characters in thestringView
betweencharacterOffset
andcharacterOffset
+charactersLength
. If you want to build a custom cycle through a variable-length-encodedstringView
(UTF-8
,UTF-16
), you can use a code like the following, which does not make use ofstringView.forEachChar()
. If the encoding is a fixed-length one (ASCII
,UTF-32
, etc.), you can do a normal cycle upon thestringView.rawData
array.var myStringView = new StringView("\u4367\uD889\uDE54\u4343\u5431 – Hello world!"); // an UTF-8-encoded stringView... alert(myStringView); for (var nChrCode, nLen = myStringView.rawData.length, nRawIdx = 0, nChrIdx = 0; nRawIdx < nLen; nRawIdx += StringView.getUTF8CharLength(nChrCode), nChrIdx++) { nChrCode = StringView.loadUTF8CharCode(myStringView.rawData, nRawIdx); /* Do something with each character... */ alert(String.fromCharCode(nChrCode)); }
Note: ForUTF-16
, replacegetUTF8CharLength()
andloadUTF8CharCode()
methods respectively withgetUTF16CharLength()
andloadUTF16CharCode()
.See also:
stringView.makeIndex()
and its note. valueOf()
-
- Syntax
-
stringView.valueOf()
- Description
- Converts
stringView
's content into string and returns it. - Arguments
-
- none
- No arguments expected.
- Examples
-
alert((new StringView("Hello world!")).valueOf() === "Hello world!"); // true
JavaScript calls the
stringView.valueOf()
method to convert an object to a primitive value. You rarely need to invoke thestringView.valueOf()
method yourself; JavaScript automatically invokes it when encountering an object where a primitive value is expected. toString()
-
- Syntax
-
stringView.toString()
- Description
- Converts
stringView
's content into string and returns it. - Arguments
-
- none
- No arguments expected.
- Examples
-
alert((new StringView("Hello world!")).toString() === "Hello world!"); // true
The
stringView.toString()
method is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected.
Appendix: other StringView
constructor's methods (utilities)
bytesToBase64()
-
- Syntax
-
StringView.bytesToBase64(uint8Array)
- Description
- Converts an array of bytes (a typed array of
Uint8Array
subclass) into a base64-encoded string. - Arguments
-
typedArray
(required)- The
Uint8Array
of bytes to encode to base64.
- Examples
-
alert(StringView.bytesToBase64([72,101,108,108,111,32,119,111,114,108,100,33])); // "SGVsbG8gd29ybGQh"
StringView.bytesToBase64()
is useful to encode an UTF-8 string to base64. See this article for other stuff. base64ToBytes()
-
- Syntax
-
StringView.base64ToBytes(base64String[, regSize])
- Description
- Decode a base64 string to an array of bytes and return it. The returned array will be a typed array of
Uint8Array
subclass. - Arguments
-
base64String
(required)- The string to decode from base64.
regSize
Optional- The number of bytes of which the length must result in a multiple (
1
or omitted for ASCII, binary strings or UTF-8-encoded strings,2
for UTF-16 strings,4
for UTF-32 strings).
- Examples
-
alert(String.fromCharCode.apply(null, StringView.base64ToBytes("SGVsbG8gd29ybGQh"))); // "Hello world!"
StringView.base64ToBytes()
is a generic utility useful also for binary data. If you want to pass theStringView.base64ToBytes(base64String[, regSize]).buffer
property to anArrayBufferView
subclass different from Uint8Array, you should make use of theregSize
argument. loadUTF8CharCode()
-
- Syntax
-
StringView.loadUTF8CharCode(typedArray, index)
- Description
- Returns the single codepoint at the given location from an array of UTF-8-encoded elements. An UTF-8-encoded codepoint can occupy up to six elements. This function will recompose all these parts into a codepoint.
- Arguments
-
typedArray
(required)- A typed array containing the UTF-8 encoded elements.
index
(required)- The location to be read.
- Examples
-
var myStringView = new StringView("Hello world!"); // an UTF-8 stringView... alert(StringView.loadUTF8CharCode(myStringView.rawData, 6)); // 119, which is the character code for "w"
StringView.loadUTF8CharCode()
is mainly for internal use and generally is of little utility. putUTF8CharCode()
-
- Syntax
-
StringView.putUTF8CharCode(typedArray, charCode, index)
- Description
- Write a single codepoint at the given position into a typed array. A single UTF-8-encoded codepoint can occupy many elements (up to six). This function will split it into the needed parts and will write them. Returns
undefined
. - Arguments
-
typedArray
(required)- A typed array containing the UTF-8 encoded elements.
charCode
(required)- A number expressing the Unicode representation of a character (codepoint).
index
(required)- The location to be overwritten.
- Examples
-
var myStringView = new StringView("Hello world!"); // an UTF-8 stringView... StringView.putUTF8CharCode(myStringView.rawData, "_".charCodeAt(0), 5) alert(myStringView); // "Hello_world!"
StringView.putUTF8CharCode()
is mainly for internal use and generally is of little utility. getUTF8CharLength()
-
- Syntax
-
StringView.getUTF8CharLength(charCode)
- Description
- Returns the number of elements occupied by an UTF-8-encoded codepoint.
- Arguments
- Examples
-
alert(StringView.getUTF8CharLength("C".charCodeAt(0))); // "C" occupies 1 UTF-8-encoded element... alert(StringView.getUTF8CharLength("⁈".charCodeAt(0))); // "⁈" occupies 3 UTF-8-encoded elements...
StringView.getUTF8CharLength()
is mainly for internal use and generally is of little utility. loadUTF16CharCode()
-
- Syntax
-
StringView.loadUTF16CharCode(typedArray, index)
- Description
- Returns the single codepoint at the given location from an array of UTF-16-encoded elements. An UTF-16 codepoint can occupy up to two UTF-16-encoded elements. This function will recompose all these parts into a codepoint.
- Arguments
-
typedArray
(required)- A typed array containing the UTF-16 encoded elements.
index
(required)- The location to be read.
- Examples
-
var myStringView = new StringView("Hello world!", "UTF-16"); // an UTF-16 stringView... alert(StringView.loadUTF16CharCode(myStringView.rawData, 6)); // 119, which is the character code of "w"
StringView.loadUTF16CharCode()
is mainly for internal use and generally is of little utility. putUTF16CharCode()
-
- Syntax
-
StringView.putUTF16CharCode(typedArray, charCode, index)
- Description
- Write a single codepoint at the given position into a typed array. A single UTF-16-encoded codepoint can occupy up to two UTF-16-encoded elements. This function will split it into the needed parts and will write them. Returns
undefined
. - Arguments
-
typedArray
(required)- A typed array containing the UTF-16 encoded elements.
charCode
(required)- A number expressing the Unicode representation of a character (codepoint).
index
(required)- The location to be overwritten.
- Examples
-
var myStringView = new StringView("Hello world!", "UTF-16"); // an UTF-16 stringView... StringView.putUTF16CharCode(myStringView.rawData, "_".charCodeAt(0), 5) alert(myStringView); // "Hello_world!"
StringView.putUTF16CharCode()
is mainly for internal use and generally is of little utility. getUTF16CharLength()
-
- Syntax
-
StringView.getUTF16CharLength(charCode)
- Description
- Returns the number of elements occupied by an UTF-16-encoded codepoint.
- Arguments
- Examples
-
alert("\uD950\uDF21"); // 0x64321 alert(StringView.b64ToUint6(0x64321)); // "\uD950\uDF21" occupies 2 UTF-16-encoded elements...
StringView.getUTF16CharLength()
is mainly for internal use and generally is of little utility. b64ToUint6()
-
- Syntax
-
StringView.b64ToUint6(charCode)
- Description
- Returns the 6-bit number corresponding to the given base64 character code.
- Arguments
- Examples
-
alert(StringView.b64ToUint6("B".charCodeAt(0))); // "B" stands for 1 in base64 encoding...
StringView.b64ToUint6()
is mainly for internal use and generally is of little utility. uint6ToB64()
-
- Syntax
-
StringView.uint6ToB64(uint6)
- Description
- Returns the base64 character code corresponding to the given 6-bit number.
- Arguments
-
uint6
(required)- A 6-bit unsigned number (0 =
uint6
< 64).
- Examples
-
alert(String.fromCharCode(StringView.uint6ToB64(1))); // 1 is represented by "B" in base64 encoding...
StringView.uint6ToB64()
is mainly for internal use and generally is of little utility.
Advanced examples
Edit an ASCII part contained within a binary file
Let's compile this C program:
#include <stdio.h> int main () { printf("Hello world!\n"); return 0; }
In a 64-bit machine it will result in an output like the following first alert
. Let's try to translate the "Hello world!"
string into the Italian "Ciao mondo!!"
…
var swHelloWorld = StringView.makeFromBase64("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AwAAAAAAAAAAAAAAAAAAAAAAAAAYGAAAAAAAAD4CAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAAAkAAAAAAAAAAAAAAAAAAAAAAAAMAAgAcAkAAAAAAAAAAAAAAAAAAAAAAAAMAAwA8AkAAAAAAAAAAAAAAAAAAAAAAAAMABABgAkAAAAAAAAAAAAAAAAAAAAAAAAMABQCAAkAAAAAAAAAAAAAAAAAAAAAAAAMABgDgAkAAAAAAAAAAAAAAAAAAAAAAAAMABwAeA0AAAAAAAAAAAAAAAAAAAAAAAAMACAAoA0AAAAAAAAAAAAAAAAAAAAAAAAMACQBIA0AAAAAAAAAAAAAAAAAAAAAAAAMACgBgA0AAAAAAAAAAAAAAAAAAAAAAAAMACwCoA0AAAAAAAAAAAAAAAAAAAAAAAAMADADQA0AAAAAAAAAAAAAAAAAAAAAAAAMADQAQBEAAAAAAAAAAAAAAAAAAAAAAAAMADgCUBUAAAAAAAAAAAAAAAAAAAAAAAAMADwCgBUAAAAAAAAAAAAAAAAAAAAAAAAMAEAC0BUAAAAAAAAAAAAAAAAAAAAAAAAMAEQDoBUAAAAAAAAAAAAAAAAAAAAAAAAMAEgDgBmAAAAAAAAAAAAAAAAAAAAAAAAMAEwDoBmAAAAAAAAAAAAAAAAAAAAAAAAMAFADwBmAAAAAAAAAAAAAAAAAAAAAAAAMAFQD4BmAAAAAAAAAAAAAAAAAAAAAAAAMAFgDICGAAAAAAAAAAAAAAAAAAAAAAAAMAFwDQCGAAAAAAAAAAAAAAAAAAAAAAAAMAGAAACWAAAAAAAAAAAAAAAAAAAAAAAAMAGQAQCWAAAAAAAAAAAAAAAAAAAAAAAAMAGgAAAAAAAAAAAAAAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAAAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAACAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAEwAAAAEAFADwBmAAAAAAAAAAAAAAAAAAIAAAAAIADQBABEAAAAAAAAAAAAAAAAAANQAAAAIADQBwBEAAAAAAAAAAAAAAAAAASAAAAAIADQCwBEAAAAAAAAAAAAAAAAAAXgAAAAEAGQAQCWAAAAAAAAEAAAAAAAAAbQAAAAEAEwDoBmAAAAAAAAAAAAAAAAAAlAAAAAIADQDQBEAAAAAAAAAAAAAAAAAAoAAAAAEAEgDgBmAAAAAAAAAAAAAAAAAAvwAAAAQA8f8AAAAAAAAAAAAAAAAAAAAACAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAxwAAAAEAEQDYBkAAAAAAAAAAAAAAAAAA1QAAAAEAFADwBmAAAAAAAAAAAAAAAAAAAAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAA4QAAAAAAEgDoBmAAAAAAAAAAAAAAAAAA8gAAAAEAFQD4BmAAAAAAAAAAAAAAAAAA+wAAAAAAEgDgBmAAAAAAAAAAAAAAAAAADgEAAAEAFwDQCGAAAAAAAAAAAAAAAAAAJAEAABIADQCQBUAAAAAAAAIAAAAAAAAANAEAACAAAAAAAAAAAAAAAAAAAAAAAAAAUAEAACAAGAAACWAAAAAAAAAAAAAAAAAAWwEAABIAAAAAAAAAAAAAAAAAAAAAAAAAbQEAABAAGAAQCWAAAAAAAAAAAAAAAAAAdAEAABIADgCUBUAAAAAAAAAAAAAAAAAAegEAABIAAAAAAAAAAAAAAAAAAAAAAAAAmQEAABAAGAAACWAAAAAAAAAAAAAAAAAApgEAACAAAAAAAAAAAAAAAAAAAAAAAAAAtQEAABECGAAICWAAAAAAAAAAAAAAAAAAwgEAABEADwCgBUAAAAAAAAQAAAAAAAAA0QEAABIADQAgBUAAAAAAAGUAAAAAAAAA4QEAABAAGQAYCWAAAAAAAAAAAAAAAAAA5gEAABIADQAQBEAAAAAAAAAAAAAAAAAA7QEAABAAGQAQCWAAAAAAAAAAAAAAAAAA+QEAABIADQAABUAAAAAAABUAAAAAAAAA/gEAACAAAAAAAAAAAAAAAAAAAAAAAAAAEgIAABECGAAQCWAAAAAAAAAAAAAAAAAAHgIAACAAAAAAAAAAAAAAAAAAAAAAAAAAOAIAABIACwCoA0AAAAAAAAAAAAAAAAAAAGluaXQuYwBjcnRzdHVmZi5jAF9fSkNSX0xJU1RfXwBkZXJlZ2lzdGVyX3RtX2Nsb25lcwByZWdpc3Rlcl90bV9jbG9uZXMAX19kb19nbG9iYWxfZHRvcnNfYXV4AGNvbXBsZXRlZC42MzU5AF9fZG9fZ2xvYmFsX2R0b3JzX2F1eF9maW5pX2FycmF5X2VudHJ5AGZyYW1lX2R1bW15AF9fZnJhbWVfZHVtbXlfaW5pdF9hcnJheV9lbnRyeQBoZWxsby5jAF9fRlJBTUVfRU5EX18AX19KQ1JfRU5EX18AX19pbml0X2FycmF5X2VuZABfRFlOQU1JQwBfX2luaXRfYXJyYXlfc3RhcnQAX0dMT0JBTF9PRkZTRVRfVEFCTEVfAF9fbGliY19jc3VfZmluaQBfSVRNX2RlcmVnaXN0ZXJUTUNsb25lVGFibGUAZGF0YV9zdGFydABwdXRzQEBHTElCQ18yLjIuNQBfZWRhdGEAX2ZpbmkAX19saWJjX3N0YXJ0X21haW5AQEdMSUJDXzIuMi41AF9fZGF0YV9zdGFydABfX2dtb25fc3RhcnRfXwBfX2Rzb19oYW5kbGUAX0lPX3N0ZGluX3VzZWQAX19saWJjX2NzdV9pbml0AF9lbmQAX3N0YXJ0AF9fYnNzX3N0YXJ0AG1haW4AX0p2X1JlZ2lzdGVyQ2xhc3NlcwBfX1RNQ19FTkRfXwBfSVRNX3JlZ2lzdGVyVE1DbG9uZVRhYmxlAF9pbml0AA==", "ASCII", 1444, 12); alert(String.fromCharCode.apply(null, swHelloWorld.bufferView)); // swHelloWorld.buffer contains the whole file alert(swHelloWorld); // "Hello world" /* traslating "Hello world" into italian... */ swHelloWorld.rawData.set((new StringView("Ciao mondo!!", "ASCII")).rawData); /* download the new executable file... */ location.assign(URL.createObjectURL(new Blob([swHelloWorld.buffer], { "type": "application\/x-executable" })));
Glossary
- Element
- An item of the backing array (whether
Uint8Array
,Uint16Array
,Uint32Array
, etc.) - Codepoint
- An unique number for each Unicode character. It is rappresented by a collection of 1-6
uint8
elements forUTF-8
, 1-2uint16
elements forUTF-16
, 1uint32
element forUCS4
, 1uint8
element forASCII
, or something else. - Byte
- A collection of 8 bits.
NULL
codepoint- The character whose codepoint is equal to
0
('\0'
).
Notes
- When you include the script stringview.js into a page, no variables other than
StringView
itself will be added to the global scope. StringView
is a highly extensible library, that anyone can extend by adding methods to the objectStringView.prototype
.
For example, imagine you want to create a method similar tostring.replace()
, but forstringView
objects. Maybe you would like to solve a situation like the following:var oMyStringView = new StringView("Hello, strange people!"); // UTF-8 var oMyRegExp = new CLikeRegExp(" ", "g"); alert(oMyStringView.replace(oMyRegExp, " ")); // "Hello, strange people!\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" /* stringView.replace() will be different from string.replace() because it act also on the source: */ alert(oMyStringView); // "Hello, strange people!\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
As you can see, the previous example needs you to create two new algorithms: theCLikeRegExp()
constructor – a constructor of C-like regular expression objects – andStringView.prototype.replace()
– the new method, able to act onstringView
instances. Well, just include stringview.js to your scope and work on them in another script:/* A constructor of C-like regular expression objects... */ function CLikeRegExp (sRegExp, sFlags) { /** * [...] * * CONSTRUCTOR CODE HERE! **/ } /* A "replace" method for StringView... */ StringView.prototype.replace = function (oCLikeRegExp, sWith) { /** * [...] * * METHOD CODE HERE! **/ };
- If you want to create a
stringView
inside a bigger empty buffer, like in the following C line/* myBuffer: 12 character followed by 1012 NULLs... */ char myBuffer[1024] = "Hello world!";
you should do something like this:var myBuffer = new StringView(1024); /* myBuffer: 12 character followed by 1012 NULLs... */ var myContent = new StringView("Hello world!"); myBuffer.rawData.set(myContent.rawData, myContent.rawData.length);
StringView
is a constructor and a collection of methods whose aim is to work strictly on arrays of numbers, rather than on creating new immutable JavaScript strings. Keep it in mind when you try to extend itsprototype
.- Since
stringView
, unlike C strings, has alength
property, there is no reason to add aNULL
codepoint ('\0'
) after the termination of a string. StringView
has been proposed as strawman for ES2015 on ECMAScript Bugs. Everyone can participate in the discussion at bug 1557 or at esdiscuss.
See also
ArrayBuffer
- Encoding API (and TextDecoder)
- Typed arrays
ArrayBufferView
Uint8Array
,Uint16Array
,Uint32Array
String
DOMString
- Binary string
DataView
- Base64 encoding and decoding
- Need a champion? StringView strawman — @esdiscuss.org
- bug 1557 — @bugs.ecmascript.org
- Wikipidia article about Unicode