stringview.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. "use strict";
  2. /*\
  3. |*|
  4. |*| :: Number.isInteger() polyfill ::
  5. |*|
  6. |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
  7. |*|
  8. \*/
  9. if (!Number.isInteger) {
  10. Number.isInteger = function isInteger (nVal) {
  11. return typeof nVal === "number" && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal;
  12. };
  13. }
  14. /*\
  15. |*|
  16. |*| StringView - Mozilla Developer Network
  17. |*|
  18. |*| Revision #8, October 6, 2014
  19. |*|
  20. |*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView
  21. |*| https://developer.mozilla.org/en-US/docs/User:fusionchess
  22. |*|
  23. |*| This framework is released under the GNU Lesser General Public License, version 3 or later.
  24. |*| http://www.gnu.org/licenses/lgpl-3.0.html
  25. |*|
  26. \*/
  27. function StringView (vInput, sEncoding /* optional (default: UTF-8) */, nOffset /* optional */, nLength /* optional */) {
  28. var fTAView, aWhole, aRaw, fPutOutptCode, fGetOutptChrSize, nInptLen, nStartIdx = isFinite(nOffset) ? nOffset : 0, nTranscrType = 15;
  29. if (sEncoding) { this.encoding = sEncoding.toString(); }
  30. encSwitch: switch (this.encoding) {
  31. case "UTF-8":
  32. fPutOutptCode = StringView.putUTF8CharCode;
  33. fGetOutptChrSize = StringView.getUTF8CharLength;
  34. fTAView = Uint8Array;
  35. break encSwitch;
  36. case "UTF-16":
  37. fPutOutptCode = StringView.putUTF16CharCode;
  38. fGetOutptChrSize = StringView.getUTF16CharLength;
  39. fTAView = Uint16Array;
  40. break encSwitch;
  41. case "UTF-32":
  42. fTAView = Uint32Array;
  43. nTranscrType &= 14;
  44. break encSwitch;
  45. default:
  46. /* case "ASCII", or case "BinaryString" or unknown cases */
  47. fTAView = Uint8Array;
  48. nTranscrType &= 14;
  49. }
  50. typeSwitch: switch (typeof vInput) {
  51. case "string":
  52. /* the input argument is a primitive string: a new buffer will be created. */
  53. nTranscrType &= 7;
  54. break typeSwitch;
  55. case "object":
  56. classSwitch: switch (vInput.constructor) {
  57. case StringView:
  58. /* the input argument is a stringView: a new buffer will be created. */
  59. nTranscrType &= 3;
  60. break typeSwitch;
  61. case String:
  62. /* the input argument is an objectified string: a new buffer will be created. */
  63. nTranscrType &= 7;
  64. break typeSwitch;
  65. case ArrayBuffer:
  66. /* the input argument is an arrayBuffer: the buffer will be shared. */
  67. aWhole = new fTAView(vInput);
  68. nInptLen = this.encoding === "UTF-32" ?
  69. vInput.byteLength >>> 2
  70. : this.encoding === "UTF-16" ?
  71. vInput.byteLength >>> 1
  72. :
  73. vInput.byteLength;
  74. aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
  75. aWhole
  76. : new fTAView(vInput, nStartIdx, !isFinite(nLength) ? nInptLen - nStartIdx : nLength);
  77. break typeSwitch;
  78. case Uint32Array:
  79. case Uint16Array:
  80. case Uint8Array:
  81. /* the input argument is a typedArray: the buffer, and possibly the array itself, will be shared. */
  82. fTAView = vInput.constructor;
  83. nInptLen = vInput.length;
  84. aWhole = vInput.byteOffset === 0 && vInput.length === (
  85. fTAView === Uint32Array ?
  86. vInput.buffer.byteLength >>> 2
  87. : fTAView === Uint16Array ?
  88. vInput.buffer.byteLength >>> 1
  89. :
  90. vInput.buffer.byteLength
  91. ) ? vInput : new fTAView(vInput.buffer);
  92. aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
  93. vInput
  94. : vInput.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen);
  95. break typeSwitch;
  96. default:
  97. /* the input argument is an array or another serializable object: a new typedArray will be created. */
  98. aWhole = new fTAView(vInput);
  99. nInptLen = aWhole.length;
  100. aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
  101. aWhole
  102. : aWhole.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen);
  103. }
  104. break typeSwitch;
  105. default:
  106. /* the input argument is a number, a boolean or a function: a new typedArray will be created. */
  107. aWhole = aRaw = new fTAView(Number(vInput) || 0);
  108. }
  109. if (nTranscrType < 8) {
  110. var vSource, nOutptLen, nCharStart, nCharEnd, nEndIdx, fGetInptChrSize, fGetInptChrCode;
  111. if (nTranscrType & 4) { /* input is string */
  112. vSource = vInput;
  113. nOutptLen = nInptLen = vSource.length;
  114. nTranscrType ^= this.encoding === "UTF-32" ? 0 : 2;
  115. /* ...or...: nTranscrType ^= Number(this.encoding !== "UTF-32") << 1; */
  116. nStartIdx = nCharStart = nOffset ? Math.max((nOutptLen + nOffset) % nOutptLen, 0) : 0;
  117. nEndIdx = nCharEnd = (Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0) + nStartIdx, nOutptLen) : nOutptLen) - 1;
  118. } else { /* input is stringView */
  119. vSource = vInput.rawData;
  120. nInptLen = vInput.makeIndex();
  121. nStartIdx = nCharStart = nOffset ? Math.max((nInptLen + nOffset) % nInptLen, 0) : 0;
  122. nOutptLen = Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0), nInptLen - nCharStart) : nInptLen;
  123. nEndIdx = nCharEnd = nOutptLen + nCharStart;
  124. if (vInput.encoding === "UTF-8") {
  125. fGetInptChrSize = StringView.getUTF8CharLength;
  126. fGetInptChrCode = StringView.loadUTF8CharCode;
  127. } else if (vInput.encoding === "UTF-16") {
  128. fGetInptChrSize = StringView.getUTF16CharLength;
  129. fGetInptChrCode = StringView.loadUTF16CharCode;
  130. } else {
  131. nTranscrType &= 1;
  132. }
  133. }
  134. if (nOutptLen === 0 || nTranscrType < 4 && vSource.encoding === this.encoding && nCharStart === 0 && nOutptLen === nInptLen) {
  135. /* the encoding is the same, the length too and the offset is 0... or the input is empty! */
  136. nTranscrType = 7;
  137. }
  138. conversionSwitch: switch (nTranscrType) {
  139. case 0:
  140. /* both the source and the new StringView have a fixed-length encoding... */
  141. aWhole = new fTAView(nOutptLen);
  142. for (var nOutptIdx = 0; nOutptIdx < nOutptLen; aWhole[nOutptIdx] = vSource[nStartIdx + nOutptIdx++]);
  143. break conversionSwitch;
  144. case 1:
  145. /* the source has a fixed-length encoding but the new StringView has a variable-length encoding... */
  146. /* mapping... */
  147. nOutptLen = 0;
  148. for (var nInptIdx = nStartIdx; nInptIdx < nEndIdx; nInptIdx++) {
  149. nOutptLen += fGetOutptChrSize(vSource[nInptIdx]);
  150. }
  151. aWhole = new fTAView(nOutptLen);
  152. /* transcription of the source... */
  153. for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx++) {
  154. nOutptIdx = fPutOutptCode(aWhole, vSource[nInptIdx], nOutptIdx);
  155. }
  156. break conversionSwitch;
  157. case 2:
  158. /* the source has a variable-length encoding but the new StringView has a fixed-length encoding... */
  159. /* mapping... */
  160. nStartIdx = 0;
  161. var nChrCode;
  162. for (nChrIdx = 0; nChrIdx < nCharStart; nChrIdx++) {
  163. nChrCode = fGetInptChrCode(vSource, nStartIdx);
  164. nStartIdx += fGetInptChrSize(nChrCode);
  165. }
  166. aWhole = new fTAView(nOutptLen);
  167. /* transcription of the source... */
  168. for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode), nOutptIdx++) {
  169. nChrCode = fGetInptChrCode(vSource, nInptIdx);
  170. aWhole[nOutptIdx] = nChrCode;
  171. }
  172. break conversionSwitch;
  173. case 3:
  174. /* both the source and the new StringView have a variable-length encoding... */
  175. /* mapping... */
  176. nOutptLen = 0;
  177. var nChrCode;
  178. for (var nChrIdx = 0, nInptIdx = 0; nChrIdx < nCharEnd; nInptIdx += fGetInptChrSize(nChrCode)) {
  179. nChrCode = fGetInptChrCode(vSource, nInptIdx);
  180. if (nChrIdx === nCharStart) { nStartIdx = nInptIdx; }
  181. if (++nChrIdx > nCharStart) { nOutptLen += fGetOutptChrSize(nChrCode); }
  182. }
  183. aWhole = new fTAView(nOutptLen);
  184. /* transcription... */
  185. for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode)) {
  186. nChrCode = fGetInptChrCode(vSource, nInptIdx);
  187. nOutptIdx = fPutOutptCode(aWhole, nChrCode, nOutptIdx);
  188. }
  189. break conversionSwitch;
  190. case 4:
  191. /* DOMString to ASCII or BinaryString or other unknown encodings */
  192. aWhole = new fTAView(nOutptLen);
  193. /* transcription... */
  194. for (var nIdx = 0; nIdx < nOutptLen; nIdx++) {
  195. aWhole[nIdx] = vSource.charCodeAt(nIdx) & 0xff;
  196. }
  197. break conversionSwitch;
  198. case 5:
  199. /* DOMString to UTF-8 or to UTF-16 */
  200. /* mapping... */
  201. nOutptLen = 0;
  202. for (var nMapIdx = 0; nMapIdx < nInptLen; nMapIdx++) {
  203. if (nMapIdx === nCharStart) { nStartIdx = nOutptLen; }
  204. nOutptLen += fGetOutptChrSize(vSource.charCodeAt(nMapIdx));
  205. if (nMapIdx === nCharEnd) { nEndIdx = nOutptLen; }
  206. }
  207. aWhole = new fTAView(nOutptLen);
  208. /* transcription... */
  209. for (var nOutptIdx = 0, nChrIdx = 0; nOutptIdx < nOutptLen; nChrIdx++) {
  210. nOutptIdx = fPutOutptCode(aWhole, vSource.charCodeAt(nChrIdx), nOutptIdx);
  211. }
  212. break conversionSwitch;
  213. case 6:
  214. /* DOMString to UTF-32 */
  215. aWhole = new fTAView(nOutptLen);
  216. /* transcription... */
  217. for (var nIdx = 0; nIdx < nOutptLen; nIdx++) {
  218. aWhole[nIdx] = vSource.charCodeAt(nIdx);
  219. }
  220. break conversionSwitch;
  221. case 7:
  222. aWhole = new fTAView(nOutptLen ? vSource : 0);
  223. break conversionSwitch;
  224. }
  225. aRaw = nTranscrType > 3 && (nStartIdx > 0 || nEndIdx < aWhole.length - 1) ? aWhole.subarray(nStartIdx, nEndIdx) : aWhole;
  226. }
  227. this.buffer = aWhole.buffer;
  228. this.bufferView = aWhole;
  229. this.rawData = aRaw;
  230. Object.freeze(this);
  231. }
  232. /* CONSTRUCTOR'S METHODS */
  233. StringView.loadUTF8CharCode = function (aChars, nIdx) {
  234. var nLen = aChars.length, nPart = aChars[nIdx];
  235. return nPart > 251 && nPart < 254 && nIdx + 5 < nLen ?
  236. /* (nPart - 252 << 30) may be not safe in ECMAScript! So...: */
  237. /* 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
  238. : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ?
  239. /* five bytes */ (nPart - 248 << 24) + (aChars[nIdx + 1] - 128 << 18) + (aChars[nIdx + 2] - 128 << 12) + (aChars[nIdx + 3] - 128 << 6) + aChars[nIdx + 4] - 128
  240. : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ?
  241. /* four bytes */(nPart - 240 << 18) + (aChars[nIdx + 1] - 128 << 12) + (aChars[nIdx + 2] - 128 << 6) + aChars[nIdx + 3] - 128
  242. : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ?
  243. /* three bytes */ (nPart - 224 << 12) + (aChars[nIdx + 1] - 128 << 6) + aChars[nIdx + 2] - 128
  244. : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ?
  245. /* two bytes */ (nPart - 192 << 6) + aChars[nIdx + 1] - 128
  246. :
  247. /* one byte */ nPart;
  248. };
  249. StringView.putUTF8CharCode = function (aTarget, nChar, nPutAt) {
  250. var nIdx = nPutAt;
  251. if (nChar < 0x80 /* 128 */) {
  252. /* one byte */
  253. aTarget[nIdx++] = nChar;
  254. } else if (nChar < 0x800 /* 2048 */) {
  255. /* two bytes */
  256. aTarget[nIdx++] = 0xc0 /* 192 */ + (nChar >>> 6);
  257. aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
  258. } else if (nChar < 0x10000 /* 65536 */) {
  259. /* three bytes */
  260. aTarget[nIdx++] = 0xe0 /* 224 */ + (nChar >>> 12);
  261. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
  262. aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
  263. } else if (nChar < 0x200000 /* 2097152 */) {
  264. /* four bytes */
  265. aTarget[nIdx++] = 0xf0 /* 240 */ + (nChar >>> 18);
  266. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
  267. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
  268. aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
  269. } else if (nChar < 0x4000000 /* 67108864 */) {
  270. /* five bytes */
  271. aTarget[nIdx++] = 0xf8 /* 248 */ + (nChar >>> 24);
  272. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */);
  273. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
  274. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
  275. aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
  276. } else /* if (nChar <= 0x7fffffff) */ { /* 2147483647 */
  277. /* six bytes */
  278. aTarget[nIdx++] = 0xfc /* 252 */ + /* (nChar >>> 30) may be not safe in ECMAScript! So...: */ (nChar / 1073741824);
  279. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 24) & 0x3f /* 63 */);
  280. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */);
  281. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
  282. aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
  283. aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
  284. }
  285. return nIdx;
  286. };
  287. StringView.getUTF8CharLength = function (nChar) {
  288. return nChar < 0x80 ? 1 : nChar < 0x800 ? 2 : nChar < 0x10000 ? 3 : nChar < 0x200000 ? 4 : nChar < 0x4000000 ? 5 : 6;
  289. };
  290. StringView.loadUTF16CharCode = function (aChars, nIdx) {
  291. /* UTF-16 to DOMString decoding algorithm */
  292. var nFrstChr = aChars[nIdx];
  293. return nFrstChr > 0xD7BF /* 55231 */ && nIdx + 1 < aChars.length ?
  294. (nFrstChr - 0xD800 /* 55296 */ << 10) + aChars[nIdx + 1] + 0x2400 /* 9216 */
  295. : nFrstChr;
  296. };
  297. StringView.putUTF16CharCode = function (aTarget, nChar, nPutAt) {
  298. var nIdx = nPutAt;
  299. if (nChar < 0x10000 /* 65536 */) {
  300. /* one element */
  301. aTarget[nIdx++] = nChar;
  302. } else {
  303. /* two elements */
  304. aTarget[nIdx++] = 0xD7C0 /* 55232 */ + (nChar >>> 10);
  305. aTarget[nIdx++] = 0xDC00 /* 56320 */ + (nChar & 0x3FF /* 1023 */);
  306. }
  307. return nIdx;
  308. };
  309. StringView.getUTF16CharLength = function (nChar) {
  310. return nChar < 0x10000 ? 1 : 2;
  311. };
  312. /* Array of bytes to base64 string decoding */
  313. StringView.b64ToUint6 = function (nChr) {
  314. return nChr > 64 && nChr < 91 ?
  315. nChr - 65
  316. : nChr > 96 && nChr < 123 ?
  317. nChr - 71
  318. : nChr > 47 && nChr < 58 ?
  319. nChr + 4
  320. : nChr === 43 ?
  321. 62
  322. : nChr === 47 ?
  323. 63
  324. :
  325. 0;
  326. };
  327. StringView.uint6ToB64 = function (nUint6) {
  328. return nUint6 < 26 ?
  329. nUint6 + 65
  330. : nUint6 < 52 ?
  331. nUint6 + 71
  332. : nUint6 < 62 ?
  333. nUint6 - 4
  334. : nUint6 === 62 ?
  335. 43
  336. : nUint6 === 63 ?
  337. 47
  338. :
  339. 65;
  340. };
  341. /* Base64 string to array encoding */
  342. StringView.bytesToBase64 = function (aBytes) {
  343. var sB64Enc = "";
  344. for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
  345. nMod3 = nIdx % 3;
  346. if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
  347. nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
  348. if (nMod3 === 2 || aBytes.length - nIdx === 1) {
  349. sB64Enc += String.fromCharCode(StringView.uint6ToB64(nUint24 >>> 18 & 63), StringView.uint6ToB64(nUint24 >>> 12 & 63), StringView.uint6ToB64(nUint24 >>> 6 & 63), StringView.uint6ToB64(nUint24 & 63));
  350. nUint24 = 0;
  351. }
  352. }
  353. return sB64Enc.replace(/A(?=A$|$)/g, "=");
  354. };
  355. StringView.base64ToBytes = function (sBase64, nBlockBytes) {
  356. var
  357. sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
  358. nOutLen = nBlockBytes ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockBytes) * nBlockBytes : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen);
  359. for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
  360. nMod4 = nInIdx & 3;
  361. nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
  362. if (nMod4 === 3 || nInLen - nInIdx === 1) {
  363. for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
  364. aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
  365. }
  366. nUint24 = 0;
  367. }
  368. }
  369. return aBytes;
  370. };
  371. StringView.makeFromBase64 = function (sB64Inpt, sEncoding, nByteOffset, nLength) {
  372. return new StringView(sEncoding === "UTF-16" || sEncoding === "UTF-32" ? StringView.base64ToBytes(sB64Inpt, sEncoding === "UTF-16" ? 2 : 4).buffer : StringView.base64ToBytes(sB64Inpt), sEncoding, nByteOffset, nLength);
  373. };
  374. /* DEFAULT VALUES */
  375. StringView.prototype.encoding = "UTF-8"; /* Default encoding... */
  376. /* INSTANCES' METHODS */
  377. StringView.prototype.makeIndex = function (nChrLength, nStartFrom) {
  378. var
  379. aTarget = this.rawData, nChrEnd, nRawLength = aTarget.length,
  380. nStartIdx = nStartFrom || 0, nIdxEnd = nStartIdx, nStopAtChr = isNaN(nChrLength) ? Infinity : nChrLength;
  381. if (nChrLength + 1 > aTarget.length) { throw new RangeError("StringView.prototype.makeIndex - The offset can\'t be major than the length of the array - 1."); }
  382. switch (this.encoding) {
  383. case "UTF-8":
  384. var nPart;
  385. for (nChrEnd = 0; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) {
  386. nPart = aTarget[nIdxEnd];
  387. nIdxEnd += nPart > 251 && nPart < 254 && nIdxEnd + 5 < nRawLength ? 6
  388. : nPart > 247 && nPart < 252 && nIdxEnd + 4 < nRawLength ? 5
  389. : nPart > 239 && nPart < 248 && nIdxEnd + 3 < nRawLength ? 4
  390. : nPart > 223 && nPart < 240 && nIdxEnd + 2 < nRawLength ? 3
  391. : nPart > 191 && nPart < 224 && nIdxEnd + 1 < nRawLength ? 2
  392. : 1;
  393. }
  394. break;
  395. case "UTF-16":
  396. for (nChrEnd = nStartIdx; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) {
  397. nIdxEnd += aTarget[nIdxEnd] > 0xD7BF /* 55231 */ && nIdxEnd + 1 < aTarget.length ? 2 : 1;
  398. }
  399. break;
  400. default:
  401. nIdxEnd = nChrEnd = isFinite(nChrLength) ? nChrLength : nRawLength - 1;
  402. }
  403. if (nChrLength) { return nIdxEnd; }
  404. return nChrEnd;
  405. };
  406. StringView.prototype.toBase64 = function (bWholeBuffer) {
  407. return StringView.bytesToBase64(
  408. bWholeBuffer ?
  409. (
  410. this.bufferView.constructor === Uint8Array ?
  411. this.bufferView
  412. :
  413. new Uint8Array(this.buffer)
  414. )
  415. : this.rawData.constructor === Uint8Array ?
  416. this.rawData
  417. :
  418. new Uint8Array(this.buffer, this.rawData.byteOffset, this.rawData.length << (this.rawData.constructor === Uint16Array ? 1 : 2))
  419. );
  420. };
  421. StringView.prototype.subview = function (nCharOffset /* optional */, nCharLength /* optional */) {
  422. var
  423. nChrLen, nCharStart, nStrLen, bVariableLen = this.encoding === "UTF-8" || this.encoding === "UTF-16",
  424. nStartOffset = nCharOffset, nStringLength, nRawLen = this.rawData.length;
  425. if (nRawLen === 0) {
  426. return new StringView(this.buffer, this.encoding);
  427. }
  428. nStringLength = bVariableLen ? this.makeIndex() : nRawLen;
  429. nCharStart = nCharOffset ? Math.max((nStringLength + nCharOffset) % nStringLength, 0) : 0;
  430. nStrLen = Number.isInteger(nCharLength) ? Math.max(nCharLength, 0) + nCharStart > nStringLength ? nStringLength - nCharStart : nCharLength : nStringLength;
  431. if (nCharStart === 0 && nStrLen === nStringLength) { return this; }
  432. if (bVariableLen) {
  433. nStartOffset = this.makeIndex(nCharStart);
  434. nChrLen = this.makeIndex(nStrLen, nStartOffset) - nStartOffset;
  435. } else {
  436. nStartOffset = nCharStart;
  437. nChrLen = nStrLen - nCharStart;
  438. }
  439. if (this.encoding === "UTF-16") {
  440. nStartOffset <<= 1;
  441. } else if (this.encoding === "UTF-32") {
  442. nStartOffset <<= 2;
  443. }
  444. return new StringView(this.buffer, this.encoding, nStartOffset, nChrLen);
  445. };
  446. StringView.prototype.forEachChar = function (fCallback, oThat, nChrOffset, nChrLen) {
  447. var aSource = this.rawData, nRawEnd, nRawIdx;
  448. if (this.encoding === "UTF-8" || this.encoding === "UTF-16") {
  449. var fGetInptChrSize, fGetInptChrCode;
  450. if (this.encoding === "UTF-8") {
  451. fGetInptChrSize = StringView.getUTF8CharLength;
  452. fGetInptChrCode = StringView.loadUTF8CharCode;
  453. } else if (this.encoding === "UTF-16") {
  454. fGetInptChrSize = StringView.getUTF16CharLength;
  455. fGetInptChrCode = StringView.loadUTF16CharCode;
  456. }
  457. nRawIdx = isFinite(nChrOffset) ? this.makeIndex(nChrOffset) : 0;
  458. nRawEnd = isFinite(nChrLen) ? this.makeIndex(nChrLen, nRawIdx) : aSource.length;
  459. for (var nChrCode, nChrIdx = 0; nRawIdx < nRawEnd; nChrIdx++) {
  460. nChrCode = fGetInptChrCode(aSource, nRawIdx);
  461. fCallback.call(oThat || null, nChrCode, nChrIdx, nRawIdx, aSource);
  462. nRawIdx += fGetInptChrSize(nChrCode);
  463. }
  464. } else {
  465. nRawIdx = isFinite(nChrOffset) ? nChrOffset : 0;
  466. nRawEnd = isFinite(nChrLen) ? nChrLen + nRawIdx : aSource.length;
  467. for (nRawIdx; nRawIdx < nRawEnd; nRawIdx++) {
  468. fCallback.call(oThat || null, aSource[nRawIdx], nRawIdx, nRawIdx, aSource);
  469. }
  470. }
  471. };
  472. StringView.prototype.valueOf = StringView.prototype.toString = function () {
  473. if (this.encoding !== "UTF-8" && this.encoding !== "UTF-16") {
  474. /* ASCII, UTF-32 or BinaryString to DOMString */
  475. return String.fromCharCode.apply(null, this.rawData);
  476. }
  477. var fGetCode, fGetIncr, sView = "";
  478. if (this.encoding === "UTF-8") {
  479. fGetIncr = StringView.getUTF8CharLength;
  480. fGetCode = StringView.loadUTF8CharCode;
  481. } else if (this.encoding === "UTF-16") {
  482. fGetIncr = StringView.getUTF16CharLength;
  483. fGetCode = StringView.loadUTF16CharCode;
  484. }
  485. for (var nChr, nLen = this.rawData.length, nIdx = 0; nIdx < nLen; nIdx += fGetIncr(nChr)) {
  486. nChr = fGetCode(this.rawData, nIdx);
  487. sView += String.fromCharCode(nChr);
  488. }
  489. return sView;
  490. };