UNPKG

@gmod/cram

Version:

read CRAM files with pure Javascript

99 lines 4.12 kB
import CramCodec from "./_base.js"; import { CramBufferOverrunError, CramMalformedError, CramUnimplementedError, } from "../../errors.js"; import { parseItf8 } from "../util.js"; export { parseItf8 } from "../util.js"; // Decode an entire buffer of ITF8 (variable-length int) values at once into // an Int32Array. ITF8 uses the high bits of the first byte to encode length: // 0xxxxxxx (1 byte, 7 bits), 10xxxxxx (2 bytes, 14 bits), 110xxxxx (3 bytes, // 21 bits), 1110xxxx (4 bytes, 28 bits), 1111xxxx (5 bytes, 32 bits). export function batchDecodeItf8(buffer) { const result = new Int32Array(buffer.length); let count = 0; let pos = 0; const len = buffer.length; while (pos < len) { const b0 = buffer[pos]; if (b0 < 0x80) { result[count++] = b0; pos += 1; } else if (b0 < 0xc0) { result[count++] = ((b0 & 0x3f) << 8) | buffer[pos + 1]; pos += 2; } else if (b0 < 0xe0) { result[count++] = ((b0 & 0x1f) << 16) | (buffer[pos + 1] << 8) | buffer[pos + 2]; pos += 3; } else if (b0 < 0xf0) { result[count++] = ((b0 & 0x0f) << 24) | (buffer[pos + 1] << 16) | (buffer[pos + 2] << 8) | buffer[pos + 3]; pos += 4; } else { result[count++] = ((b0 & 0x0f) << 28) | (buffer[pos + 1] << 20) | (buffer[pos + 2] << 12) | (buffer[pos + 3] << 4) | (buffer[pos + 4] & 0x0f); pos += 5; } } return result.subarray(0, count); } export default class ExternalCodec extends CramCodec { blockContentId; constructor(parameters, dataType) { super(parameters, dataType); this.blockContentId = parameters.blockContentId; if (this.dataType !== 'int' && this.dataType !== 'byte') { throw new CramUnimplementedError(`${this.dataType} decoding not yet implemented by EXTERNAL codec`); } } decode(_slice, _coreDataBlock, blocksByContentId, cursors) { if (this.dataType === 'int') { const preDecoded = cursors.preDecodedIntBlocks?.get(this.blockContentId); if (preDecoded) { return preDecoded.values[preDecoded.index++]; } const contentBlock = blocksByContentId[this.blockContentId]; if (!contentBlock) { throw new CramMalformedError(`no block found with content ID ${this.blockContentId}`); } const cursor = cursors.externalBlocks.getCursor(this.blockContentId); return parseItf8(contentBlock.content, cursor); } else { const contentBlock = blocksByContentId[this.blockContentId]; if (!contentBlock) { throw new CramMalformedError(`no block found with content ID ${this.blockContentId}`); } const cursor = cursors.externalBlocks.getCursor(this.blockContentId); if (cursor.bytePosition >= contentBlock.content.length) { throw new CramBufferOverrunError('attempted to read beyond end of block. this file seems truncated.'); } return contentBlock.content[cursor.bytePosition++]; } } getBytesSubarray(blocksByContentId, cursors, length) { const { blockContentId } = this.parameters; const contentBlock = blocksByContentId[blockContentId]; if (!contentBlock) { return undefined; } const cursor = cursors.externalBlocks.getCursor(blockContentId); const start = cursor.bytePosition; const end = start + length; if (end > contentBlock.content.length) { throw new CramBufferOverrunError('attempted to read beyond end of block. this file seems truncated.'); } cursor.bytePosition = end; return contentBlock.content.subarray(start, end); } } //# sourceMappingURL=external.js.map