mirror of
https://github.com/musix-org/musix-oss
synced 2024-12-23 13:03:18 +00:00
188 lines
5.5 KiB
JavaScript
188 lines
5.5 KiB
JavaScript
/*
|
|
* Copyright 2017 Sam Thorogood. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations under
|
|
* the License.
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Polyfill for TextEncoder and TextDecoder.
|
|
*
|
|
* You probably want `text.min.js`, and not this file directly.
|
|
*/
|
|
|
|
(function(scope) {
|
|
'use strict';
|
|
|
|
// fail early
|
|
if (scope['TextEncoder'] && scope['TextDecoder']) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string=} utfLabel
|
|
*/
|
|
function FastTextEncoder(utfLabel='utf-8') {
|
|
if (utfLabel !== 'utf-8') {
|
|
throw new RangeError(
|
|
`Failed to construct 'TextEncoder': The encoding label provided ('${utfLabel}') is invalid.`);
|
|
}
|
|
}
|
|
|
|
Object.defineProperty(FastTextEncoder.prototype, 'encoding', {value: 'utf-8'});
|
|
|
|
/**
|
|
* @param {string} string
|
|
* @param {{stream: boolean}=} options
|
|
* @return {!Uint8Array}
|
|
*/
|
|
FastTextEncoder.prototype.encode = function(string, options={stream: false}) {
|
|
if (options.stream) {
|
|
throw new Error(`Failed to encode: the 'stream' option is unsupported.`);
|
|
}
|
|
|
|
let pos = 0;
|
|
const len = string.length;
|
|
const out = [];
|
|
|
|
let at = 0; // output position
|
|
let tlen = Math.max(32, len + (len >> 1) + 7); // 1.5x size
|
|
let target = new Uint8Array((tlen >> 3) << 3); // ... but at 8 byte offset
|
|
|
|
while (pos < len) {
|
|
let value = string.charCodeAt(pos++);
|
|
if (value >= 0xd800 && value <= 0xdbff) {
|
|
// high surrogate
|
|
if (pos < len) {
|
|
const extra = string.charCodeAt(pos);
|
|
if ((extra & 0xfc00) === 0xdc00) {
|
|
++pos;
|
|
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
|
}
|
|
}
|
|
if (value >= 0xd800 && value <= 0xdbff) {
|
|
continue; // drop lone surrogate
|
|
}
|
|
}
|
|
|
|
// expand the buffer if we couldn't write 4 bytes
|
|
if (at + 4 > target.length) {
|
|
tlen += 8; // minimum extra
|
|
tlen *= (1.0 + (pos / string.length) * 2); // take 2x the remaining
|
|
tlen = (tlen >> 3) << 3; // 8 byte offset
|
|
|
|
const update = new Uint8Array(tlen);
|
|
update.set(target);
|
|
target = update;
|
|
}
|
|
|
|
if ((value & 0xffffff80) === 0) { // 1-byte
|
|
target[at++] = value; // ASCII
|
|
continue;
|
|
} else if ((value & 0xfffff800) === 0) { // 2-byte
|
|
target[at++] = ((value >> 6) & 0x1f) | 0xc0;
|
|
} else if ((value & 0xffff0000) === 0) { // 3-byte
|
|
target[at++] = ((value >> 12) & 0x0f) | 0xe0;
|
|
target[at++] = ((value >> 6) & 0x3f) | 0x80;
|
|
} else if ((value & 0xffe00000) === 0) { // 4-byte
|
|
target[at++] = ((value >> 18) & 0x07) | 0xf0;
|
|
target[at++] = ((value >> 12) & 0x3f) | 0x80;
|
|
target[at++] = ((value >> 6) & 0x3f) | 0x80;
|
|
} else {
|
|
// FIXME: do we care
|
|
continue;
|
|
}
|
|
|
|
target[at++] = (value & 0x3f) | 0x80;
|
|
}
|
|
|
|
return target.slice(0, at);
|
|
}
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {string=} utfLabel
|
|
* @param {{fatal: boolean}=} options
|
|
*/
|
|
function FastTextDecoder(utfLabel='utf-8', options={fatal: false}) {
|
|
if (utfLabel !== 'utf-8') {
|
|
throw new RangeError(
|
|
`Failed to construct 'TextDecoder': The encoding label provided ('${utfLabel}') is invalid.`);
|
|
}
|
|
if (options.fatal) {
|
|
throw new Error(`Failed to construct 'TextDecoder': the 'fatal' option is unsupported.`);
|
|
}
|
|
}
|
|
|
|
Object.defineProperty(FastTextDecoder.prototype, 'encoding', {value: 'utf-8'});
|
|
|
|
Object.defineProperty(FastTextDecoder.prototype, 'fatal', {value: false});
|
|
|
|
Object.defineProperty(FastTextDecoder.prototype, 'ignoreBOM', {value: false});
|
|
|
|
/**
|
|
* @param {(!ArrayBuffer|!ArrayBufferView)} buffer
|
|
* @param {{stream: boolean}=} options
|
|
*/
|
|
FastTextDecoder.prototype.decode = function(buffer, options={stream: false}) {
|
|
if (options['stream']) {
|
|
throw new Error(`Failed to decode: the 'stream' option is unsupported.`);
|
|
}
|
|
|
|
const bytes = new Uint8Array(buffer);
|
|
let pos = 0;
|
|
const len = bytes.length;
|
|
const out = [];
|
|
|
|
while (pos < len) {
|
|
const byte1 = bytes[pos++];
|
|
if (byte1 === 0) {
|
|
break; // NULL
|
|
}
|
|
|
|
if ((byte1 & 0x80) === 0) { // 1-byte
|
|
out.push(byte1);
|
|
} else if ((byte1 & 0xe0) === 0xc0) { // 2-byte
|
|
const byte2 = bytes[pos++] & 0x3f;
|
|
out.push(((byte1 & 0x1f) << 6) | byte2);
|
|
} else if ((byte1 & 0xf0) === 0xe0) {
|
|
const byte2 = bytes[pos++] & 0x3f;
|
|
const byte3 = bytes[pos++] & 0x3f;
|
|
out.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
|
|
} else if ((byte1 & 0xf8) === 0xf0) {
|
|
const byte2 = bytes[pos++] & 0x3f;
|
|
const byte3 = bytes[pos++] & 0x3f;
|
|
const byte4 = bytes[pos++] & 0x3f;
|
|
|
|
// this can be > 0xffff, so possibly generate surrogates
|
|
let codepoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
|
|
if (codepoint > 0xffff) {
|
|
// codepoint &= ~0x10000;
|
|
codepoint -= 0x10000;
|
|
out.push((codepoint >>> 10) & 0x3ff | 0xd800)
|
|
codepoint = 0xdc00 | codepoint & 0x3ff;
|
|
}
|
|
out.push(codepoint);
|
|
} else {
|
|
// FIXME: we're ignoring this
|
|
}
|
|
}
|
|
|
|
return String.fromCharCode.apply(null, out);
|
|
}
|
|
|
|
scope['TextEncoder'] = FastTextEncoder;
|
|
scope['TextDecoder'] = FastTextDecoder;
|
|
|
|
}(typeof window !== 'undefined' ? window : (typeof global !== 'undefined' ? global : this)));
|