Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
'use strict'

/*\
|*|
|*|  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()
  }

  switch (this.encoding) {
    case 'UTF-8':
      fPutOutptCode = StringView.putUTF8CharCode
      fGetOutptChrSize = StringView.getUTF8CharLength
      fTAView = Uint8Array
      break
    case 'UTF-16':
      fPutOutptCode = StringView.putUTF16CharCode
      fGetOutptChrSize = StringView.getUTF16CharLength
      fTAView = Uint16Array
      break
    case 'UTF-32':
      fTAView = Uint32Array
      nTranscrType &= 14
      break
    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
    case 'object':
      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
    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
    }

    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

      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

      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

      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

      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

      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

      case 6:
        /* DOMString to UTF-32 */

        aWhole = new fTAView(nOutptLen)

        /* transcription... */

        for (var nIdx = 0; nIdx < nOutptLen; nIdx++) {
          aWhole[nIdx] = vSource.charCodeAt(nIdx)
        }

        break

      case 7:
        aWhole = new fTAView(nOutptLen ? vSource : 0)
        break
    }

    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
}