UNPKG

molstar

Version:

A comprehensive macromolecular library.

393 lines 14.7 kB
/** * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Adapted from CIFTools.js (https://github.com/dsehnal/CIFTools.js; MIT) and MMTF (https://github.com/rcsb/mmtf-javascript/; MIT) * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { ChunkedArray } from '../../../mol-data/util'; import { Encoding } from './encoding'; import { classifyIntArray } from './classifier'; var ArrayEncoderImpl = /** @class */ (function () { function ArrayEncoderImpl(providers) { this.providers = providers; } ArrayEncoderImpl.prototype.and = function (f) { return new ArrayEncoderImpl(this.providers.concat([f])); }; ArrayEncoderImpl.prototype.encode = function (data) { var encoding = []; for (var _i = 0, _a = this.providers; _i < _a.length; _i++) { var p = _a[_i]; var t = p(data); if (!t.encodings.length) { throw new Error('Encodings must be non-empty.'); } data = t.data; for (var _b = 0, _c = t.encodings; _b < _c.length; _b++) { var e = _c[_b]; encoding.push(e); } } if (!(data instanceof Uint8Array)) { throw new Error('The encoding must result in a Uint8Array. Fix your encoding chain.'); } return { encoding: encoding, data: data }; }; return ArrayEncoderImpl; }()); export { ArrayEncoderImpl }; export var ArrayEncoder; (function (ArrayEncoder) { function by(f) { return new ArrayEncoderImpl([f]); } ArrayEncoder.by = by; function fromEncoding(encoding) { var e = by(getProvider(encoding[0])); for (var i = 1; i < encoding.length; i++) e = e.and(getProvider(encoding[i])); return e; } ArrayEncoder.fromEncoding = fromEncoding; function getProvider(e) { switch (e.kind) { case 'ByteArray': return ArrayEncoding.byteArray; case 'FixedPoint': return ArrayEncoding.fixedPoint(e.factor); case 'IntervalQuantization': return ArrayEncoding.intervalQuantizaiton(e.min, e.max, e.numSteps); case 'RunLength': return ArrayEncoding.runLength; case 'Delta': return ArrayEncoding.delta; case 'IntegerPacking': return ArrayEncoding.integerPacking; case 'StringArray': return ArrayEncoding.stringArray; } } })(ArrayEncoder || (ArrayEncoder = {})); export var ArrayEncoding; (function (ArrayEncoding) { var _a, _b; function by(f) { return new ArrayEncoderImpl([f]); } ArrayEncoding.by = by; function uint8(data) { return { encodings: [{ kind: 'ByteArray', type: 4 /* Uint8 */ }], data: data }; } function int8(data) { return { encodings: [{ kind: 'ByteArray', type: 1 /* Int8 */ }], data: new Uint8Array(data.buffer, data.byteOffset) }; } var writers = (_a = {}, _a[2 /* Int16 */] = function (v, i, a) { v.setInt16(2 * i, a, true); }, _a[5 /* Uint16 */] = function (v, i, a) { v.setUint16(2 * i, a, true); }, _a[3 /* Int32 */] = function (v, i, a) { v.setInt32(4 * i, a, true); }, _a[6 /* Uint32 */] = function (v, i, a) { v.setUint32(4 * i, a, true); }, _a[32 /* Float32 */] = function (v, i, a) { v.setFloat32(4 * i, a, true); }, _a[33 /* Float64 */] = function (v, i, a) { v.setFloat64(8 * i, a, true); }, _a); var byteSizes = (_b = {}, _b[2 /* Int16 */] = 2, _b[5 /* Uint16 */] = 2, _b[3 /* Int32 */] = 4, _b[6 /* Uint32 */] = 4, _b[32 /* Float32 */] = 4, _b[33 /* Float64 */] = 8, _b); function byteArray(data) { var type = Encoding.getDataType(data); if (type === 1 /* Int8 */) return int8(data); else if (type === 4 /* Uint8 */) return uint8(data); var result = new Uint8Array(data.length * byteSizes[type]); var w = writers[type]; var view = new DataView(result.buffer); for (var i = 0, n = data.length; i < n; i++) { w(view, i, data[i]); } return { encodings: [{ kind: 'ByteArray', type: type }], data: result }; } ArrayEncoding.byteArray = byteArray; function _fixedPoint(data, factor) { var srcType = Encoding.getDataType(data); var result = new Int32Array(data.length); for (var i = 0, n = data.length; i < n; i++) { result[i] = Math.round(data[i] * factor); } return { encodings: [{ kind: 'FixedPoint', factor: factor, srcType: srcType }], data: result }; } function fixedPoint(factor) { return function (data) { return _fixedPoint(data, factor); }; } ArrayEncoding.fixedPoint = fixedPoint; function _intervalQuantizaiton(data, min, max, numSteps, arrayType) { var srcType = Encoding.getDataType(data); if (!data.length) { return { encodings: [{ kind: 'IntervalQuantization', min: min, max: max, numSteps: numSteps, srcType: srcType }], data: new Int32Array(0) }; } if (max < min) { var t = min; min = max; max = t; } var delta = (max - min) / (numSteps - 1); var output = new arrayType(data.length); for (var i = 0, n = data.length; i < n; i++) { var v = data[i]; if (v <= min) output[i] = 0; else if (v >= max) output[i] = numSteps - 1; else output[i] = (Math.round((v - min) / delta)) | 0; } return { encodings: [{ kind: 'IntervalQuantization', min: min, max: max, numSteps: numSteps, srcType: srcType }], data: output }; } function intervalQuantizaiton(min, max, numSteps, arrayType) { if (arrayType === void 0) { arrayType = Int32Array; } return function (data) { return _intervalQuantizaiton(data, min, max, numSteps, arrayType); }; } ArrayEncoding.intervalQuantizaiton = intervalQuantizaiton; function runLength(data) { var srcType = Encoding.getDataType(data); if (srcType === void 0) { data = new Int32Array(data); srcType = 3 /* Int32 */; } if (!data.length) { return { encodings: [{ kind: 'RunLength', srcType: srcType, srcSize: 0 }], data: new Int32Array(0) }; } // calculate output size var fullLength = 2; for (var i = 1, il = data.length; i < il; i++) { if (data[i - 1] !== data[i]) { fullLength += 2; } } var output = new Int32Array(fullLength); var offset = 0; var runLength = 1; for (var i = 1, il = data.length; i < il; i++) { if (data[i - 1] !== data[i]) { output[offset] = data[i - 1]; output[offset + 1] = runLength; runLength = 1; offset += 2; } else { ++runLength; } } output[offset] = data[data.length - 1]; output[offset + 1] = runLength; return { encodings: [{ kind: 'RunLength', srcType: srcType, srcSize: data.length }], data: output }; } ArrayEncoding.runLength = runLength; function delta(data) { if (!Encoding.isSignedIntegerDataType(data)) { throw new Error('Only signed integer types can be encoded using delta encoding.'); } var srcType = Encoding.getDataType(data); if (srcType === void 0) { data = new Int32Array(data); srcType = 3 /* Int32 */; } if (!data.length) { return { encodings: [{ kind: 'Delta', origin: 0, srcType: srcType }], data: new data.constructor(0) }; } var output = new data.constructor(data.length); var origin = data[0]; output[0] = data[0]; for (var i = 1, n = data.length; i < n; i++) { output[i] = data[i] - data[i - 1]; } output[0] = 0; return { encodings: [{ kind: 'Delta', origin: origin, srcType: srcType }], data: output }; } ArrayEncoding.delta = delta; function isSigned(data) { for (var i = 0, n = data.length; i < n; i++) { if (data[i] < 0) return true; } return false; } function packingSize(data, upperLimit) { var lowerLimit = -upperLimit - 1; var size = 0; for (var i = 0, n = data.length; i < n; i++) { var value = data[i]; if (value === 0) { size += 1; } else if (value > 0) { size += Math.ceil(value / upperLimit); if (value % upperLimit === 0) size += 1; } else { size += Math.ceil(value / lowerLimit); if (value % lowerLimit === 0) size += 1; } } return size; } function determinePacking(data) { var signed = isSigned(data); var size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF); var size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF); if (data.length * 4 < size16 * 2) { // 4 byte packing is the most effective return { isSigned: signed, size: data.length, bytesPerElement: 4 }; } else if (size16 * 2 < size8) { // 2 byte packing is the most effective return { isSigned: signed, size: size16, bytesPerElement: 2 }; } else { // 1 byte packing is the most effective return { isSigned: signed, size: size8, bytesPerElement: 1 }; } ; } function _integerPacking(data, packing) { var upperLimit = packing.isSigned ? (packing.bytesPerElement === 1 ? 0x7F : 0x7FFF) : (packing.bytesPerElement === 1 ? 0xFF : 0xFFFF); var lowerLimit = -upperLimit - 1; var n = data.length; var packed = packing.isSigned ? packing.bytesPerElement === 1 ? new Int8Array(packing.size) : new Int16Array(packing.size) : packing.bytesPerElement === 1 ? new Uint8Array(packing.size) : new Uint16Array(packing.size); var j = 0; for (var i = 0; i < n; i++) { var value = data[i]; if (value >= 0) { while (value >= upperLimit) { packed[j] = upperLimit; ++j; value -= upperLimit; } } else { while (value <= lowerLimit) { packed[j] = lowerLimit; ++j; value -= lowerLimit; } } packed[j] = value; ++j; } var result = byteArray(packed); return { encodings: [{ kind: 'IntegerPacking', byteCount: packing.bytesPerElement, isUnsigned: !packing.isSigned, srcSize: n }, result.encodings[0] ], data: result.data }; } /** * Packs Int32 array. The packing level is determined automatically to either 1-, 2-, or 4-byte words. */ function integerPacking(data) { // if (!(data instanceof Int32Array)) { // throw new Error('Integer packing can only be applied to Int32 data.'); // } var packing = determinePacking(data); if (packing.bytesPerElement === 4) { // no packing done, Int32 encoding will be used return byteArray(data); } return _integerPacking(data, packing); } ArrayEncoding.integerPacking = integerPacking; function stringArray(data) { var map = Object.create(null); var strings = []; var output = new Int32Array(data.length); var offsets = ChunkedArray.create(Int32Array, 1, Math.min(1024, data.length < 32 ? data.length + 1 : Math.round(data.length / 8) + 1)); ChunkedArray.add(offsets, 0); var accLength = 0; var i = 0; for (var _i = 0, data_1 = data; _i < data_1.length; _i++) { var s = data_1[_i]; // handle null strings. if (s === null || s === void 0) { output[i++] = -1; continue; } var index = map[s]; if (index === void 0) { // increment the length accLength += s.length; // store the string and index index = strings.length; strings[index] = s; map[s] = index; // write the offset ChunkedArray.add(offsets, accLength); } output[i++] = index; } var offsetArray = ChunkedArray.compact(offsets); var offsetEncoding = classifyIntArray(offsetArray); var encodedOddsets = offsetEncoding.encode(offsetArray); var dataEncoding = classifyIntArray(output); var encodedData = dataEncoding.encode(output); return { encodings: [{ kind: 'StringArray', dataEncoding: encodedData.encoding, stringData: strings.join(''), offsetEncoding: encodedOddsets.encoding, offsets: encodedOddsets.data }], data: encodedData.data }; } ArrayEncoding.stringArray = stringArray; })(ArrayEncoding || (ArrayEncoding = {})); //# sourceMappingURL=array-encoder.js.map