molstar
Version:
A comprehensive macromolecular library.
393 lines • 14.7 kB
JavaScript
/**
* 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