UNPKG

@awayjs/graphics

Version:
705 lines (704 loc) 26.4 kB
/* * Copyright 2014 Mozilla Foundation * * 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. */ import { __extends } from "tslib"; import { Settings } from '../Settings'; import { NativeDeflate } from './NativeDeflate'; import { memCopy } from './utilities'; var InflateState; (function (InflateState) { InflateState[InflateState["INIT"] = 0] = "INIT"; InflateState[InflateState["BLOCK_0"] = 1] = "BLOCK_0"; InflateState[InflateState["BLOCK_1"] = 2] = "BLOCK_1"; InflateState[InflateState["BLOCK_2_PRE"] = 3] = "BLOCK_2_PRE"; InflateState[InflateState["BLOCK_2"] = 4] = "BLOCK_2"; InflateState[InflateState["DONE"] = 5] = "DONE"; InflateState[InflateState["ERROR"] = 6] = "ERROR"; InflateState[InflateState["VERIFY_HEADER"] = 7] = "VERIFY_HEADER"; })(InflateState || (InflateState = {})); var WINDOW_SIZE = 32768; var WINDOW_SHIFT_POSITION = 65536; var MAX_WINDOW_SIZE = WINDOW_SHIFT_POSITION + 258; /* plus max copy len */ var Inflate = /** @class */ (function () { function Inflate(verifyHeader) { // } Inflate.prototype.push = function (data) { //Debug.abstractMethod('Inflate.push'); }; Inflate.prototype.close = function () { // }; Inflate.create = function (verifyHeader, size, tryNative) { if (size === void 0) { size = 0; } if (tryNative === void 0) { tryNative = true; } if (tryNative && !NativeDeflate.isSupported) { console.warn('[NativeDeflate] Is not supported!'); } if (tryNative && NativeDeflate.isSupported && Settings.USE_NATIVE_DEFLATE) { if (size) { //console.debug('[NativeDeflate] Decoding API is supported and enabled, use native'); return new NativeDeflate(verifyHeader, size); } else { console.debug('[NativeDeflate] size not presented, can`t use a native implementation'); } } return new BasicInflate(verifyHeader); }; Inflate.prototype._processZLibHeader = function (buffer, start, end) { /* returns -1 - bad header, 0 - not enough data, 1+ - number of bytes processed */ var ZLIB_HEADER_SIZE = 2; if (start + ZLIB_HEADER_SIZE > end) { return 0; } var header = (buffer[start] << 8) | buffer[start + 1]; var error = null; if ((header & 0x0f00) !== 0x0800) { error = 'inflate: unknown compression method'; } else if ((header % 31) !== 0) { error = 'inflate: bad FCHECK'; } else if ((header & 0x20) !== 0) { error = 'inflate: FDICT bit set'; } if (error) { if (this.onError) { this.onError(error); } return -1; } else { return ZLIB_HEADER_SIZE; } }; Inflate.inflate = function (data, expectedLength, zlibHeader) { var position = 0; var output = new Uint8Array(expectedLength); var inflate = Inflate.create(zlibHeader, 0, false); inflate.onData = function (data) { // Make sure we don't cause an exception here when trying to set out-of-bound data by clamping the number of // bytes to write to the remaining space in our output buffer. The Flash Player ignores data that goes over the // expected length, so should we. var length = Math.min(data.length, output.length - position); if (length) { memCopy(output, data, position, 0, length); } position += length; }; inflate.onError = function (error) { throw new Error(error); }; inflate.push(data); inflate.close(); return output; }; return Inflate; }()); export { Inflate }; var BasicInflate = /** @class */ (function (_super) { __extends(BasicInflate, _super); function BasicInflate(verifyHeader) { var _this = _super.call(this, verifyHeader) || this; _this._buffer = null; _this._bufferSize = 0; _this._bufferPosition = 0; _this._bitBuffer = 0; _this._bitLength = 0; _this._window = new Uint8Array(MAX_WINDOW_SIZE); _this._windowPosition = 0; _this._state = verifyHeader ? InflateState.VERIFY_HEADER : InflateState.INIT; _this._isFinalBlock = false; _this._literalTable = null; _this._distanceTable = null; _this._block0Read = 0; _this._block2State = null; _this._copyState = { state: 0, len: 0, lenBits: 0, dist: 0, distBits: 0 }; if (!areTablesInitialized) { initializeTables(); areTablesInitialized = true; } return _this; } BasicInflate.prototype.push = function (data) { if (!this._buffer || this._buffer.length < this._bufferSize + data.length) { var newBuffer = new Uint8Array(this._bufferSize + data.length); if (this._buffer) { newBuffer.set(this._buffer); } this._buffer = newBuffer; } this._buffer.set(data, this._bufferSize); this._bufferSize += data.length; this._bufferPosition = 0; var incomplete = false; do { var lastPosition = this._windowPosition; if (this._state === InflateState.INIT) { incomplete = this._decodeInitState(); if (incomplete) { break; } } switch (this._state) { case InflateState.BLOCK_0: incomplete = this._decodeBlock0(); break; case InflateState.BLOCK_2_PRE: incomplete = this._decodeBlock2Pre(); if (incomplete) { break; } /* fall through */ case InflateState.BLOCK_1: case InflateState.BLOCK_2: incomplete = this._decodeBlock(); break; case InflateState.ERROR: case InflateState.DONE: // skipping all data this._bufferPosition = this._bufferSize; break; case InflateState.VERIFY_HEADER: var processed = this._processZLibHeader(this._buffer, this._bufferPosition, this._bufferSize); if (processed > 0) { this._bufferPosition += processed; this._state = InflateState.INIT; } else if (processed === 0) { incomplete = true; } else { this._state = InflateState.ERROR; } break; } var decoded = this._windowPosition - lastPosition; if (decoded > 0) { this.onData(this._window.subarray(lastPosition, this._windowPosition)); } if (this._windowPosition >= WINDOW_SHIFT_POSITION) { // shift window if ('copyWithin' in this._buffer) { this._window['copyWithin'](0, this._windowPosition - WINDOW_SIZE, this._windowPosition); } else { this._window.set(this._window.subarray(this._windowPosition - WINDOW_SIZE, this._windowPosition)); } this._windowPosition = WINDOW_SIZE; } } while (!incomplete && this._bufferPosition < this._bufferSize); if (this._bufferPosition < this._bufferSize) { // shift buffer if ('copyWithin' in this._buffer) { this._buffer['copyWithin'](0, this._bufferPosition, this._bufferSize); } else { this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize)); } this._bufferSize -= this._bufferPosition; } else { this._bufferSize = 0; } }; BasicInflate.prototype._decodeInitState = function () { if (this._isFinalBlock) { this._state = InflateState.DONE; return false; } var buffer = this._buffer, bufferSize = this._bufferSize; var bitBuffer = this._bitBuffer, bitLength = this._bitLength; var state; var position = this._bufferPosition; if (((bufferSize - position) << 3) + bitLength < 3) { return true; } if (bitLength < 3) { bitBuffer |= buffer[position++] << bitLength; bitLength += 8; } var type = bitBuffer & 7; bitBuffer >>= 3; bitLength -= 3; switch (type >> 1) { case 0: bitBuffer = 0; bitLength = 0; if (bufferSize - position < 4) { return true; } var length = buffer[position] | (buffer[position + 1] << 8); var length2 = buffer[position + 2] | (buffer[position + 3] << 8); position += 4; if ((length ^ length2) !== 0xFFFF) { this._error('inflate: invalid block 0 length'); state = InflateState.ERROR; break; } if (length === 0) { state = InflateState.INIT; } else { this._block0Read = length; state = InflateState.BLOCK_0; } break; case 1: state = InflateState.BLOCK_1; this._literalTable = fixedLiteralTable; this._distanceTable = fixedDistanceTable; break; case 2: if (((bufferSize - position) << 3) + bitLength < 14 + 3 * 4) { return true; } while (bitLength < 14) { bitBuffer |= buffer[position++] << bitLength; bitLength += 8; } var numLengthCodes = ((bitBuffer >> 10) & 15) + 4; if (((bufferSize - position) << 3) + bitLength < 14 + 3 * numLengthCodes) { return true; } var block2State = { numLiteralCodes: (bitBuffer & 31) + 257, numDistanceCodes: ((bitBuffer >> 5) & 31) + 1, codeLengthTable: undefined, bitLengths: undefined, codesRead: 0, dupBits: 0 }; bitBuffer >>= 14; bitLength -= 14; var codeLengths = new Uint8Array(19); for (var i = 0; i < numLengthCodes; ++i) { if (bitLength < 3) { bitBuffer |= buffer[position++] << bitLength; bitLength += 8; } codeLengths[codeLengthOrder[i]] = bitBuffer & 7; bitBuffer >>= 3; bitLength -= 3; } for (; i < 19; i++) { codeLengths[codeLengthOrder[i]] = 0; } block2State.bitLengths = new Uint8Array(block2State.numLiteralCodes + block2State.numDistanceCodes); block2State.codeLengthTable = makeHuffmanTable(codeLengths); this._block2State = block2State; state = InflateState.BLOCK_2_PRE; break; default: this._error('inflate: unsupported block type'); state = InflateState.ERROR; return false; } this._isFinalBlock = !!(type & 1); this._state = state; this._bufferPosition = position; this._bitBuffer = bitBuffer; this._bitLength = bitLength; return false; }; BasicInflate.prototype._error = function (e) { if (this.onError) { this.onError(e); } }; BasicInflate.prototype._decodeBlock0 = function () { var position = this._bufferPosition; var windowPosition = this._windowPosition; var toRead = this._block0Read; var leftInWindow = MAX_WINDOW_SIZE - windowPosition; var leftInBuffer = this._bufferSize - position; var incomplete = leftInBuffer < toRead; var canFit = Math.min(leftInWindow, leftInBuffer, toRead); this._window.set(this._buffer.subarray(position, position + canFit), windowPosition); this._windowPosition = windowPosition + canFit; this._bufferPosition = position + canFit; this._block0Read = toRead - canFit; if (toRead === canFit) { this._state = InflateState.INIT; return false; } return incomplete && leftInWindow < leftInBuffer; }; BasicInflate.prototype._readBits = function (size) { var bitBuffer = this._bitBuffer; var bitLength = this._bitLength; if (size > bitLength) { var pos = this._bufferPosition; var end = this._bufferSize; do { if (pos >= end) { this._bufferPosition = pos; this._bitBuffer = bitBuffer; this._bitLength = bitLength; return -1; } bitBuffer |= this._buffer[pos++] << bitLength; bitLength += 8; } while (size > bitLength); this._bufferPosition = pos; } this._bitBuffer = bitBuffer >> size; this._bitLength = bitLength - size; return bitBuffer & ((1 << size) - 1); }; BasicInflate.prototype._readCode = function (codeTable) { var bitBuffer = this._bitBuffer; var bitLength = this._bitLength; var maxBits = codeTable.maxBits; if (maxBits > bitLength) { var pos = this._bufferPosition; var end = this._bufferSize; do { if (pos >= end) { this._bufferPosition = pos; this._bitBuffer = bitBuffer; this._bitLength = bitLength; return -1; } bitBuffer |= this._buffer[pos++] << bitLength; bitLength += 8; } while (maxBits > bitLength); this._bufferPosition = pos; } var code = codeTable.codes[bitBuffer & ((1 << maxBits) - 1)]; var len = code >> 16; if ((code & 0x8000)) { this._error('inflate: invalid encoding'); this._state = InflateState.ERROR; return -1; } this._bitBuffer = bitBuffer >> len; this._bitLength = bitLength - len; return code & 0xffff; }; BasicInflate.prototype._decodeBlock2Pre = function () { var block2State = this._block2State; var numCodes = block2State.numLiteralCodes + block2State.numDistanceCodes; var bitLengths = block2State.bitLengths; var i = block2State.codesRead; var prev = i > 0 ? bitLengths[i - 1] : 0; var codeLengthTable = block2State.codeLengthTable; var j; if (block2State.dupBits > 0) { j = this._readBits(block2State.dupBits); if (j < 0) { return true; } while (j--) { bitLengths[i++] = prev; } block2State.dupBits = 0; } while (i < numCodes) { var sym = this._readCode(codeLengthTable); if (sym < 0) { block2State.codesRead = i; return true; } else if (sym < 16) { bitLengths[i++] = (prev = sym); continue; } var j, dupBits; switch (sym) { case 16: dupBits = 2; j = 3; sym = prev; break; case 17: dupBits = 3; j = 3; sym = 0; break; case 18: dupBits = 7; j = 11; sym = 0; break; } while (j--) { bitLengths[i++] = sym; } j = this._readBits(dupBits); if (j < 0) { block2State.codesRead = i; block2State.dupBits = dupBits; return true; } while (j--) { bitLengths[i++] = sym; } prev = sym; } this._literalTable = makeHuffmanTable(bitLengths.subarray(0, block2State.numLiteralCodes)); this._distanceTable = makeHuffmanTable(bitLengths.subarray(block2State.numLiteralCodes)); this._state = InflateState.BLOCK_2; this._block2State = null; return false; }; BasicInflate.prototype._decodeBlock = function () { var literalTable = this._literalTable, distanceTable = this._distanceTable; var output = this._window, pos = this._windowPosition; var copyState = this._copyState; var i, j, sym; var len, lenBits, dist, distBits; if (copyState.state !== 0) { // continuing len/distance operation switch (copyState.state) { case 1: j = 0; if ((j = this._readBits(copyState.lenBits)) < 0) { return true; } copyState.len += j; copyState.state = 2; /* fall through */ case 2: if ((sym = this._readCode(distanceTable)) < 0) { return true; } copyState.distBits = distanceExtraBits[sym]; copyState.dist = distanceCodes[sym]; copyState.state = 3; /* fall through */ case 3: j = 0; if (copyState.distBits > 0 && (j = this._readBits(copyState.distBits)) < 0) { return true; } dist = copyState.dist + j; len = copyState.len; i = pos - dist; while (len--) { output[pos++] = output[i++]; } copyState.state = 0; if (pos >= WINDOW_SHIFT_POSITION) { this._windowPosition = pos; return false; } break; } } do { sym = this._readCode(literalTable); if (sym < 0) { this._windowPosition = pos; return true; } else if (sym < 256) { output[pos++] = sym; } else if (sym > 256) { this._windowPosition = pos; sym -= 257; lenBits = lengthExtraBits[sym]; len = lengthCodes[sym]; j = lenBits === 0 ? 0 : this._readBits(lenBits); if (j < 0) { copyState.state = 1; copyState.len = len; copyState.lenBits = lenBits; return true; } len += j; sym = this._readCode(distanceTable); if (sym < 0) { copyState.state = 2; copyState.len = len; return true; } distBits = distanceExtraBits[sym]; dist = distanceCodes[sym]; j = distBits === 0 ? 0 : this._readBits(distBits); if (j < 0) { copyState.state = 3; copyState.len = len; copyState.dist = dist; copyState.distBits = distBits; return true; } dist += j; i = pos - dist; while (len--) { output[pos++] = output[i++]; } } else { this._state = InflateState.INIT; break; // end of block } } while (pos < WINDOW_SHIFT_POSITION); this._windowPosition = pos; return false; }; return BasicInflate; }(Inflate)); var codeLengthOrder; var distanceCodes; var distanceExtraBits; var fixedDistanceTable; var lengthCodes; var lengthExtraBits; var fixedLiteralTable; var areTablesInitialized = false; function initializeTables() { codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]); distanceCodes = new Uint16Array(30); distanceExtraBits = new Uint8Array(30); for (var i = 0, j = 0, code = 1; i < 30; ++i) { distanceCodes[i] = code; code += 1 << (distanceExtraBits[i] = ~~((j += (i > 2 ? 1 : 0)) / 2)); } var bitLengths = new Uint8Array(288); for (var i = 0; i < 32; ++i) { bitLengths[i] = 5; } fixedDistanceTable = makeHuffmanTable(bitLengths.subarray(0, 32)); lengthCodes = new Uint16Array(29); lengthExtraBits = new Uint8Array(29); for (var i = 0, j = 0, code = 3; i < 29; ++i) { lengthCodes[i] = code - (i == 28 ? 1 : 0); code += 1 << (lengthExtraBits[i] = ~~(((j += (i > 4 ? 1 : 0)) / 4) % 6)); } for (var i = 0; i < 288; ++i) { bitLengths[i] = i < 144 || i > 279 ? 8 : (i < 256 ? 9 : 7); } fixedLiteralTable = makeHuffmanTable(bitLengths); } function makeHuffmanTable(bitLengths) { var maxBits = Math.max.apply(null, bitLengths); var numLengths = bitLengths.length; var size = 1 << maxBits; var codes = new Uint32Array(size); // avoiding len == 0: using max number of bits var dummyCode = (maxBits << 16) | 0xFFFF; for (var j = 0; j < size; j++) { codes[j] = dummyCode; } for (var code = 0, len = 1, skip = 2; len <= maxBits; code <<= 1, ++len, skip <<= 1) { for (var val = 0; val < numLengths; ++val) { if (bitLengths[val] === len) { var lsb = 0; for (var i = 0; i < len; ++i) lsb = (lsb * 2) + ((code >> i) & 1); for (var i = lsb; i < size; i += skip) codes[i] = (len << 16) | val; ++code; } } } return { codes: codes, maxBits: maxBits }; } var DeflateState; (function (DeflateState) { DeflateState[DeflateState["WRITE"] = 0] = "WRITE"; DeflateState[DeflateState["DONE"] = 1] = "DONE"; DeflateState[DeflateState["ZLIB_HEADER"] = 2] = "ZLIB_HEADER"; })(DeflateState || (DeflateState = {})); var Adler32 = /** @class */ (function () { function Adler32() { this.a = 1; this.b = 0; } Adler32.prototype.update = function (data, start, end) { var a = this.a; var b = this.b; for (var i = start; i < end; ++i) { a = (a + (data[i] & 0xff)) % 65521; b = (b + a) % 65521; } this.a = a; this.b = b; }; Adler32.prototype.getChecksum = function () { return (this.b << 16) | this.a; }; return Adler32; }()); export { Adler32 }; var Deflate = /** @class */ (function () { function Deflate(writeZlibHeader) { this._writeZlibHeader = writeZlibHeader; this._state = writeZlibHeader ? DeflateState.ZLIB_HEADER : DeflateState.WRITE; this._adler32 = writeZlibHeader ? new Adler32() : null; } Deflate.prototype.push = function (data) { if (this._state === DeflateState.ZLIB_HEADER) { this.onData(new Uint8Array([0x78, 0x9C])); this._state = DeflateState.WRITE; } // simple non-compressing algorithm for now var len = data.length; var outputSize = len + Math.ceil(len / 0xFFFF) * 5; var output = new Uint8Array(outputSize); var outputPos = 0; var pos = 0; while (len > 0xFFFF) { output.set(new Uint8Array([ 0x00, 0xFF, 0xFF, 0x00, 0x00 ]), outputPos); outputPos += 5; output.set(data.subarray(pos, pos + 0xFFFF), outputPos); pos += 0xFFFF; outputPos += 0xFFFF; len -= 0xFFFF; } output.set(new Uint8Array([ 0x00, (len & 0xff), ((len >> 8) & 0xff), ((~len) & 0xff), (((~len) >> 8) & 0xff) ]), outputPos); outputPos += 5; output.set(data.subarray(pos, len), outputPos); this.onData(output); if (this._adler32) { this._adler32.update(data, 0, len); } }; Deflate.prototype.close = function () { this._state = DeflateState.DONE; this.onData(new Uint8Array([ 0x01, 0x00, 0x00, 0xFF, 0xFF ])); if (this._adler32) { var checksum = this._adler32.getChecksum(); this.onData(new Uint8Array([ checksum & 0xff, (checksum >> 8) & 0xff, (checksum >> 16) & 0xff, (checksum >>> 24) & 0xff ])); } }; return Deflate; }()); export { Deflate };