@gmod/cram 5.0.6 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/dist/cram-bundle.js +1 -1
- package/dist/cram-bundle.js.LICENSE.txt +0 -6
- package/dist/cramFile/codecs/byteArrayLength.js +8 -4
- package/dist/cramFile/codecs/byteArrayLength.js.map +1 -1
- package/dist/cramFile/codecs/external.d.ts +1 -4
- package/dist/cramFile/codecs/external.js +15 -20
- package/dist/cramFile/codecs/external.js.map +1 -1
- package/dist/cramFile/file.d.ts +2 -7
- package/dist/cramFile/file.js +22 -22
- package/dist/cramFile/file.js.map +1 -1
- package/dist/cramFile/slice/index.js +14 -5
- package/dist/cramFile/slice/index.js.map +1 -1
- package/dist/cramFile/util.js +4 -3
- package/dist/cramFile/util.js.map +1 -1
- package/dist/indexedCramFile.js +15 -4
- package/dist/indexedCramFile.js.map +1 -1
- package/dist/rans/d04.js +42 -16
- package/dist/rans/d04.js.map +1 -1
- package/dist/rans/d14.js +60 -23
- package/dist/rans/d14.js.map +1 -1
- package/dist/rans/decoding.js +3 -4
- package/dist/rans/decoding.js.map +1 -1
- package/dist/rans/frequencies.js +18 -24
- package/dist/rans/frequencies.js.map +1 -1
- package/dist/rans/index.js +9 -14
- package/dist/rans/index.js.map +1 -1
- package/dist/unzip.d.ts +1 -1
- package/dist/unzip.js +2 -36
- package/dist/unzip.js.map +1 -1
- package/dist/xz-decompress/wasm.d.ts +1 -0
- package/dist/xz-decompress/wasm.js +5 -0
- package/dist/xz-decompress/wasm.js.map +1 -0
- package/dist/xz-decompress/xz-decompress.d.ts +1 -0
- package/dist/xz-decompress/xz-decompress.js +123 -0
- package/dist/xz-decompress/xz-decompress.js.map +1 -0
- package/esm/cramFile/codecs/byteArrayLength.js +8 -4
- package/esm/cramFile/codecs/byteArrayLength.js.map +1 -1
- package/esm/cramFile/codecs/external.d.ts +1 -4
- package/esm/cramFile/codecs/external.js +15 -20
- package/esm/cramFile/codecs/external.js.map +1 -1
- package/esm/cramFile/file.d.ts +2 -7
- package/esm/cramFile/file.js +22 -22
- package/esm/cramFile/file.js.map +1 -1
- package/esm/cramFile/slice/index.js +14 -5
- package/esm/cramFile/slice/index.js.map +1 -1
- package/esm/cramFile/util.js +4 -3
- package/esm/cramFile/util.js.map +1 -1
- package/esm/indexedCramFile.js +15 -4
- package/esm/indexedCramFile.js.map +1 -1
- package/esm/rans/d04.js +42 -16
- package/esm/rans/d04.js.map +1 -1
- package/esm/rans/d14.js +60 -20
- package/esm/rans/d14.js.map +1 -1
- package/esm/rans/decoding.js +3 -4
- package/esm/rans/decoding.js.map +1 -1
- package/esm/rans/frequencies.js +18 -24
- package/esm/rans/frequencies.js.map +1 -1
- package/esm/rans/index.js +9 -14
- package/esm/rans/index.js.map +1 -1
- package/esm/unzip.d.ts +1 -1
- package/esm/unzip.js +2 -3
- package/esm/unzip.js.map +1 -1
- package/esm/xz-decompress/wasm.d.ts +1 -0
- package/esm/xz-decompress/wasm.js +2 -0
- package/esm/xz-decompress/wasm.js.map +1 -0
- package/esm/xz-decompress/xz-decompress.d.ts +1 -0
- package/esm/xz-decompress/xz-decompress.js +120 -0
- package/esm/xz-decompress/xz-decompress.js.map +1 -0
- package/package.json +6 -8
- package/src/cramFile/codecs/byteArrayLength.ts +8 -4
- package/src/cramFile/codecs/external.ts +19 -31
- package/src/cramFile/container/compressionScheme.ts +1 -1
- package/src/cramFile/file.ts +27 -27
- package/src/cramFile/slice/index.ts +18 -8
- package/src/cramFile/util.ts +4 -3
- package/src/indexedCramFile.ts +20 -7
- package/src/rans/d04.ts +48 -19
- package/src/rans/d14.ts +69 -23
- package/src/rans/decoding.ts +3 -4
- package/src/rans/frequencies.ts +18 -24
- package/src/rans/index.ts +10 -14
- package/src/unzip.ts +3 -4
- package/src/xz-decompress/wasm.ts +2 -0
- package/src/xz-decompress/xz-decompress.ts +155 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmod/cram",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "read CRAM files with pure Javascript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "GMOD/cram-js",
|
|
@@ -52,26 +52,24 @@
|
|
|
52
52
|
"crc": "^4.3.2",
|
|
53
53
|
"generic-filehandle2": "^2.0.12",
|
|
54
54
|
"md5": "^2.2.1",
|
|
55
|
-
"pako": "^1.0.
|
|
56
|
-
"quick-lru": "^4.0.1"
|
|
57
|
-
"xz-decompress": "^0.2.1"
|
|
55
|
+
"pako-esm2": "^1.0.15",
|
|
56
|
+
"quick-lru": "^4.0.1"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
59
|
"@gmod/indexedfasta": "^4.0.6",
|
|
61
60
|
"@types/md5": "^2.3.2",
|
|
62
|
-
"@
|
|
63
|
-
"@vitest/coverage-v8": "^3.2.3",
|
|
61
|
+
"@vitest/coverage-v8": "^4.0.3",
|
|
64
62
|
"buffer": "^6.0.3",
|
|
65
63
|
"documentation": "^14.0.3",
|
|
66
64
|
"eslint": "^9.29.0",
|
|
67
65
|
"eslint-plugin-import": "^2.31.0",
|
|
68
|
-
"eslint-plugin-unicorn": "^
|
|
66
|
+
"eslint-plugin-unicorn": "^62.0.0",
|
|
69
67
|
"mock-fs": "^5.2.0",
|
|
70
68
|
"prettier": "^3.2.5",
|
|
71
69
|
"rimraf": "^6.0.1",
|
|
72
70
|
"typescript": "^5.7.0",
|
|
73
71
|
"typescript-eslint": "^8.34.0",
|
|
74
|
-
"vitest": "^
|
|
72
|
+
"vitest": "^4.0.3",
|
|
75
73
|
"webpack": "^5.99.9",
|
|
76
74
|
"webpack-cli": "^6.0.1"
|
|
77
75
|
},
|
|
@@ -35,11 +35,15 @@ export default class ByteArrayStopCodec extends CramCodec<
|
|
|
35
35
|
const arrayLength =
|
|
36
36
|
lengthCodec.decode(slice, coreDataBlock, blocksByContentId, cursors) || 0
|
|
37
37
|
|
|
38
|
-
const dataCodec = this._getDataCodec()
|
|
39
38
|
const data = new Uint8Array(arrayLength)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
if (arrayLength > 0) {
|
|
40
|
+
const dataCodec = this._getDataCodec()
|
|
41
|
+
// Call decode directly on codec to avoid repeated lookups
|
|
42
|
+
for (let i = 0; i < arrayLength; i += 1) {
|
|
43
|
+
data[i] =
|
|
44
|
+
dataCodec.decode(slice, coreDataBlock, blocksByContentId, cursors) ||
|
|
45
|
+
0
|
|
46
|
+
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
return data
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import CramCodec, {
|
|
1
|
+
import CramCodec, { Cursors } from './_base.ts'
|
|
2
2
|
import { CramUnimplementedError } from '../../errors.ts'
|
|
3
3
|
import { CramFileBlock } from '../file.ts'
|
|
4
4
|
import CramSlice from '../slice/index.ts'
|
|
@@ -10,21 +10,12 @@ export default class ExternalCodec extends CramCodec<
|
|
|
10
10
|
'int' | 'byte',
|
|
11
11
|
ExternalCramEncoding['parameters']
|
|
12
12
|
> {
|
|
13
|
-
private readonly _decodeData: (
|
|
14
|
-
contentBlock: CramFileBlock,
|
|
15
|
-
cursor: Cursor,
|
|
16
|
-
) => number
|
|
17
|
-
|
|
18
13
|
constructor(
|
|
19
14
|
parameters: ExternalCramEncoding['parameters'],
|
|
20
15
|
dataType: 'int' | 'byte',
|
|
21
16
|
) {
|
|
22
17
|
super(parameters, dataType)
|
|
23
|
-
if (this.dataType
|
|
24
|
-
this._decodeData = this._decodeInt
|
|
25
|
-
} else if (this.dataType === 'byte') {
|
|
26
|
-
this._decodeData = this._decodeByte
|
|
27
|
-
} else {
|
|
18
|
+
if (this.dataType !== 'int' && this.dataType !== 'byte') {
|
|
28
19
|
throw new CramUnimplementedError(
|
|
29
20
|
`${this.dataType} decoding not yet implemented by EXTERNAL codec`,
|
|
30
21
|
)
|
|
@@ -39,29 +30,26 @@ export default class ExternalCodec extends CramCodec<
|
|
|
39
30
|
) {
|
|
40
31
|
const { blockContentId } = this.parameters
|
|
41
32
|
const contentBlock = blocksByContentId[blockContentId]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cursors.externalBlocks.getCursor(blockContentId),
|
|
46
|
-
)
|
|
47
|
-
: undefined
|
|
48
|
-
}
|
|
33
|
+
if (!contentBlock) {
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
const [result, bytesRead] = parseItf8(
|
|
52
|
-
contentBlock.content,
|
|
53
|
-
cursor.bytePosition,
|
|
54
|
-
)
|
|
55
|
-
cursor.bytePosition = cursor.bytePosition + bytesRead
|
|
56
|
-
return result
|
|
57
|
-
}
|
|
37
|
+
const cursor = cursors.externalBlocks.getCursor(blockContentId)
|
|
58
38
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
39
|
+
if (this.dataType === 'int') {
|
|
40
|
+
const [result, bytesRead] = parseItf8(
|
|
41
|
+
contentBlock.content,
|
|
42
|
+
cursor.bytePosition,
|
|
63
43
|
)
|
|
44
|
+
cursor.bytePosition += bytesRead
|
|
45
|
+
return result
|
|
46
|
+
} else {
|
|
47
|
+
if (cursor.bytePosition >= contentBlock.content.length) {
|
|
48
|
+
throw new CramBufferOverrunError(
|
|
49
|
+
'attempted to read beyond end of block. this file seems truncated.',
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
return contentBlock.content[cursor.bytePosition++]!
|
|
64
53
|
}
|
|
65
|
-
return contentBlock.content[cursor.bytePosition++]!
|
|
66
54
|
}
|
|
67
55
|
}
|
|
@@ -100,7 +100,7 @@ export default class CramContainerCompressionScheme {
|
|
|
100
100
|
// interpret some of the preservation map tags for convenient use
|
|
101
101
|
this.readNamesIncluded = content.preservation.RN
|
|
102
102
|
this.APdelta = content.preservation.AP
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
this.referenceRequired = !!content.preservation.RR
|
|
105
105
|
this.tagIdsDictionary = content.preservation.TD
|
|
106
106
|
this.substitutionMatrix = parseSubstitutionMatrix(content.preservation.SM)
|
package/src/cramFile/file.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import crc32 from 'crc/calculators/crc32'
|
|
2
2
|
import QuickLRU from 'quick-lru'
|
|
3
|
-
import { XzReadableStream } from 'xz-decompress'
|
|
4
3
|
|
|
5
4
|
import { CramMalformedError, CramUnimplementedError } from '../errors.ts'
|
|
6
5
|
import * as htscodecs from '../htscodecs/index.ts'
|
|
7
6
|
import { open } from '../io.ts'
|
|
8
7
|
import ransuncompress from '../rans/index.ts'
|
|
9
8
|
import { parseHeaderText } from '../sam.ts'
|
|
9
|
+
import { parseItem, tinyMemoize } from './util.ts'
|
|
10
10
|
import { decode } from '../seek-bzip/index.ts'
|
|
11
11
|
import { unzip } from '../unzip.ts'
|
|
12
12
|
import CramContainer from './container/index.ts'
|
|
@@ -17,19 +17,10 @@ import {
|
|
|
17
17
|
cramFileDefinition,
|
|
18
18
|
getSectionParsers,
|
|
19
19
|
} from './sectionParsers.ts'
|
|
20
|
-
import {
|
|
20
|
+
import { xzDecompress } from '../xz-decompress/xz-decompress.ts'
|
|
21
21
|
|
|
22
22
|
import type { GenericFilehandle } from 'generic-filehandle2'
|
|
23
23
|
|
|
24
|
-
function bufferToStream(buf: Uint8Array) {
|
|
25
|
-
return new ReadableStream({
|
|
26
|
-
start(controller) {
|
|
27
|
-
controller.enqueue(buf)
|
|
28
|
-
controller.close()
|
|
29
|
-
},
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
24
|
// source: https://abdulapopoola.com/2019/01/20/check-endianness-with-javascript/
|
|
34
25
|
function getEndianness() {
|
|
35
26
|
const uInt32 = new Uint32Array([0x11223344])
|
|
@@ -80,6 +71,7 @@ export default class CramFile {
|
|
|
80
71
|
}
|
|
81
72
|
public featureCache: QuickLRU<string, Promise<CramRecord[]>>
|
|
82
73
|
private header: string | undefined
|
|
74
|
+
private _sectionParsers?: ReturnType<typeof getSectionParsers>
|
|
83
75
|
|
|
84
76
|
constructor(args: CramFileArgs) {
|
|
85
77
|
this.file = open(args.url, args.path, args.filehandle)
|
|
@@ -148,9 +140,11 @@ export default class CramFile {
|
|
|
148
140
|
}
|
|
149
141
|
|
|
150
142
|
async getContainerById(containerNumber: number) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
143
|
+
if (!this._sectionParsers) {
|
|
144
|
+
const { majorVersion } = await this.getDefinition()
|
|
145
|
+
this._sectionParsers = getSectionParsers(majorVersion)
|
|
146
|
+
}
|
|
147
|
+
let position = this._sectionParsers.cramFileDefinition.maxLength
|
|
154
148
|
|
|
155
149
|
// skip with a series of reads to the proper container
|
|
156
150
|
let currentContainer: CramContainer | undefined
|
|
@@ -208,11 +202,13 @@ export default class CramFile {
|
|
|
208
202
|
* length check, relies on a try catch to read return an error to break
|
|
209
203
|
*/
|
|
210
204
|
async containerCount(): Promise<number | undefined> {
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
if (!this._sectionParsers) {
|
|
206
|
+
const { majorVersion } = await this.getDefinition()
|
|
207
|
+
this._sectionParsers = getSectionParsers(majorVersion)
|
|
208
|
+
}
|
|
213
209
|
|
|
214
210
|
let containerCount = 0
|
|
215
|
-
let position =
|
|
211
|
+
let position = this._sectionParsers.cramFileDefinition.maxLength
|
|
216
212
|
try {
|
|
217
213
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
218
214
|
while (true) {
|
|
@@ -247,10 +243,14 @@ export default class CramFile {
|
|
|
247
243
|
return new CramContainer(this, position)
|
|
248
244
|
}
|
|
249
245
|
|
|
250
|
-
async readBlockHeader(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
async readBlockHeader(
|
|
247
|
+
position: number,
|
|
248
|
+
): Promise<BlockHeader & { _endPosition: number; _size: number }> {
|
|
249
|
+
if (!this._sectionParsers) {
|
|
250
|
+
const { majorVersion } = await this.getDefinition()
|
|
251
|
+
this._sectionParsers = getSectionParsers(majorVersion)
|
|
252
|
+
}
|
|
253
|
+
const { cramBlockHeader } = this._sectionParsers
|
|
254
254
|
|
|
255
255
|
const buffer = await this.file.read(cramBlockHeader.maxLength, position)
|
|
256
256
|
return parseItem(buffer, cramBlockHeader.parser, 0, position)
|
|
@@ -267,7 +267,7 @@ export default class CramFile {
|
|
|
267
267
|
position: number,
|
|
268
268
|
size = section.maxLength,
|
|
269
269
|
preReadBuffer?: Uint8Array,
|
|
270
|
-
) {
|
|
270
|
+
): Promise<T & { _endPosition: number; _size: number }> {
|
|
271
271
|
const buffer = preReadBuffer ?? (await this.file.read(size, position))
|
|
272
272
|
const data = parseItem(buffer, section.parser, 0, position)
|
|
273
273
|
if (data._size !== size) {
|
|
@@ -289,10 +289,7 @@ export default class CramFile {
|
|
|
289
289
|
} else if (compressionMethod === 'bzip2') {
|
|
290
290
|
return decode(inputBuffer)
|
|
291
291
|
} else if (compressionMethod === 'lzma') {
|
|
292
|
-
|
|
293
|
-
new XzReadableStream(bufferToStream(inputBuffer)),
|
|
294
|
-
)
|
|
295
|
-
return new Uint8Array(await decompressedResponse.arrayBuffer())
|
|
292
|
+
return xzDecompress(inputBuffer)
|
|
296
293
|
} else if (compressionMethod === 'rans') {
|
|
297
294
|
const outputBuffer = new Uint8Array(uncompressedSize)
|
|
298
295
|
ransuncompress(inputBuffer, outputBuffer)
|
|
@@ -332,7 +329,10 @@ export default class CramFile {
|
|
|
332
329
|
|
|
333
330
|
async readBlock(position: number) {
|
|
334
331
|
const { majorVersion } = await this.getDefinition()
|
|
335
|
-
|
|
332
|
+
if (!this._sectionParsers) {
|
|
333
|
+
this._sectionParsers = getSectionParsers(majorVersion)
|
|
334
|
+
}
|
|
335
|
+
const sectionParsers = this._sectionParsers
|
|
336
336
|
const blockHeader = await this.readBlockHeader(position)
|
|
337
337
|
const blockContentPosition = blockHeader._endPosition
|
|
338
338
|
|
|
@@ -392,16 +392,23 @@ export default class CramSlice {
|
|
|
392
392
|
},
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
// Pre-resolve all codecs to avoid repeated lookups
|
|
396
|
+
const codecCache = new Map<DataSeriesEncodingKey, any>()
|
|
397
|
+
|
|
395
398
|
const decodeDataSeries: DataSeriesDecoder = <
|
|
396
399
|
T extends DataSeriesEncodingKey,
|
|
397
400
|
>(
|
|
398
401
|
dataSeriesName: T,
|
|
399
402
|
): DataTypeMapping[DataSeriesTypes[T]] | undefined => {
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
403
|
+
let codec = codecCache.get(dataSeriesName)
|
|
404
|
+
if (codec === undefined) {
|
|
405
|
+
codec = compressionScheme.getCodecForDataSeries(dataSeriesName)
|
|
406
|
+
if (!codec) {
|
|
407
|
+
throw new CramMalformedError(
|
|
408
|
+
`no codec defined for ${dataSeriesName} data series`,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
codecCache.set(dataSeriesName, codec)
|
|
405
412
|
}
|
|
406
413
|
return codec.decode(this, coreDataBlock, blocksByContentId, cursors)
|
|
407
414
|
}
|
|
@@ -431,10 +438,13 @@ export default class CramSlice {
|
|
|
431
438
|
})
|
|
432
439
|
} catch (e) {
|
|
433
440
|
if (e instanceof CramBufferOverrunError) {
|
|
434
|
-
|
|
435
|
-
|
|
441
|
+
const recordsDecoded = i
|
|
442
|
+
const recordsExpected = sliceHeader.parsedContent.numRecords
|
|
443
|
+
throw new CramMalformedError(
|
|
444
|
+
`Failed to decode all records in slice. Decoded ${recordsDecoded} of ${recordsExpected} expected records. ` +
|
|
445
|
+
`Buffer overrun suggests either: (1) file is truncated/corrupted, (2) compression scheme is incorrect, ` +
|
|
446
|
+
`or (3) there's a bug in the decoder. Original error: ${e.message}`,
|
|
436
447
|
)
|
|
437
|
-
break
|
|
438
448
|
} else {
|
|
439
449
|
throw e
|
|
440
450
|
}
|
package/src/cramFile/util.ts
CHANGED
|
@@ -183,14 +183,15 @@ export function tinyMemoize(_class: any, methodName: any) {
|
|
|
183
183
|
const method = _class.prototype[methodName]
|
|
184
184
|
const memoAttrName = `_memo_${methodName}`
|
|
185
185
|
_class.prototype[methodName] = function _tinyMemoized() {
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
let res = this[memoAttrName]
|
|
187
|
+
if (res === undefined) {
|
|
188
|
+
res = method.call(this)
|
|
188
189
|
this[memoAttrName] = res
|
|
189
190
|
Promise.resolve(res).catch(() => {
|
|
190
191
|
delete this[memoAttrName]
|
|
191
192
|
})
|
|
192
193
|
}
|
|
193
|
-
return
|
|
194
|
+
return res
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
package/src/indexedCramFile.ts
CHANGED
|
@@ -104,14 +104,27 @@ export default class IndexedCramFile {
|
|
|
104
104
|
// fetch all the slices and parse the feature data
|
|
105
105
|
const sliceResults = await Promise.all(
|
|
106
106
|
slices.map(slice =>
|
|
107
|
-
this.getRecordsInSlice(
|
|
108
|
-
|
|
109
|
-
feature
|
|
110
|
-
|
|
107
|
+
this.getRecordsInSlice(slice, feature => {
|
|
108
|
+
// Check if feature belongs to this sequence
|
|
109
|
+
if (feature.sequenceId !== seq) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// For unmapped reads (lengthOnRef is undefined), they are placed at their
|
|
114
|
+
// mate's position. Include them if that position is within the range.
|
|
115
|
+
if (feature.lengthOnRef === undefined) {
|
|
116
|
+
return (
|
|
117
|
+
feature.alignmentStart >= start && feature.alignmentStart <= end
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// For mapped reads, check if they overlap the requested range
|
|
122
|
+
// Use > instead of >= for start boundary to match samtools behavior
|
|
123
|
+
return (
|
|
111
124
|
feature.alignmentStart <= end &&
|
|
112
|
-
feature.lengthOnRef
|
|
113
|
-
|
|
114
|
-
),
|
|
125
|
+
feature.alignmentStart + feature.lengthOnRef - 1 > start
|
|
126
|
+
)
|
|
127
|
+
}),
|
|
115
128
|
),
|
|
116
129
|
)
|
|
117
130
|
|
package/src/rans/d04.ts
CHANGED
|
@@ -3,6 +3,10 @@ import { CramMalformedError } from '../errors.ts'
|
|
|
3
3
|
import { TF_SHIFT } from './constants.ts'
|
|
4
4
|
import Decoding from './decoding.ts'
|
|
5
5
|
|
|
6
|
+
// Inline constants for performance
|
|
7
|
+
const RANS_BYTE_L = 8388608 // 1 << 23
|
|
8
|
+
const MASK = 4095 // (1 << 12) - 1
|
|
9
|
+
|
|
6
10
|
export default function uncompress(
|
|
7
11
|
/* ByteBuffer */ input,
|
|
8
12
|
/* Decoding.AriDecoder */ D,
|
|
@@ -16,26 +20,51 @@ export default function uncompress(
|
|
|
16
20
|
|
|
17
21
|
const /* int */ outputSize = out.remaining()
|
|
18
22
|
const /* int */ outputEnd = outputSize & ~3
|
|
23
|
+
const D_R = D.R
|
|
24
|
+
|
|
19
25
|
for (let i = 0; i < outputEnd; i += 4) {
|
|
20
|
-
const /* byte */ c0 =
|
|
21
|
-
const /* byte */ c1 =
|
|
22
|
-
const /* byte */ c2 =
|
|
23
|
-
const /* byte */ c3 =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
out.
|
|
27
|
-
out.
|
|
28
|
-
out.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
const /* byte */ c0 = D_R[rans0 & MASK]
|
|
27
|
+
const /* byte */ c1 = D_R[rans1 & MASK]
|
|
28
|
+
const /* byte */ c2 = D_R[rans2 & MASK]
|
|
29
|
+
const /* byte */ c3 = D_R[rans3 & MASK]
|
|
30
|
+
|
|
31
|
+
// Inline putAt to avoid function call overhead
|
|
32
|
+
out._buffer[i] = c0
|
|
33
|
+
out._buffer[i + 1] = c1
|
|
34
|
+
out._buffer[i + 2] = c2
|
|
35
|
+
out._buffer[i + 3] = c3
|
|
36
|
+
|
|
37
|
+
const sym0 = syms[0xff & c0]
|
|
38
|
+
const sym1 = syms[0xff & c1]
|
|
39
|
+
const sym2 = syms[0xff & c2]
|
|
40
|
+
const sym3 = syms[0xff & c3]
|
|
41
|
+
|
|
42
|
+
rans0 = sym0.freq * (rans0 >> TF_SHIFT) + (rans0 & MASK) - sym0.start
|
|
43
|
+
rans1 = sym1.freq * (rans1 >> TF_SHIFT) + (rans1 & MASK) - sym1.start
|
|
44
|
+
rans2 = sym2.freq * (rans2 >> TF_SHIFT) + (rans2 & MASK) - sym2.start
|
|
45
|
+
rans3 = sym3.freq * (rans3 >> TF_SHIFT) + (rans3 & MASK) - sym3.start
|
|
46
|
+
|
|
47
|
+
// Inline renormalize to avoid function call overhead
|
|
48
|
+
if (rans0 < RANS_BYTE_L) {
|
|
49
|
+
do {
|
|
50
|
+
rans0 = (rans0 << 8) | (0xff & input.get())
|
|
51
|
+
} while (rans0 < RANS_BYTE_L)
|
|
52
|
+
}
|
|
53
|
+
if (rans1 < RANS_BYTE_L) {
|
|
54
|
+
do {
|
|
55
|
+
rans1 = (rans1 << 8) | (0xff & input.get())
|
|
56
|
+
} while (rans1 < RANS_BYTE_L)
|
|
57
|
+
}
|
|
58
|
+
if (rans2 < RANS_BYTE_L) {
|
|
59
|
+
do {
|
|
60
|
+
rans2 = (rans2 << 8) | (0xff & input.get())
|
|
61
|
+
} while (rans2 < RANS_BYTE_L)
|
|
62
|
+
}
|
|
63
|
+
if (rans3 < RANS_BYTE_L) {
|
|
64
|
+
do {
|
|
65
|
+
rans3 = (rans3 << 8) | (0xff & input.get())
|
|
66
|
+
} while (rans3 < RANS_BYTE_L)
|
|
67
|
+
}
|
|
39
68
|
}
|
|
40
69
|
|
|
41
70
|
out.setPosition(outputEnd)
|
package/src/rans/d14.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { TF_SHIFT } from './constants.ts'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
// Inline constants for performance
|
|
5
|
+
const RANS_BYTE_L = 8388608 // 1 << 23
|
|
6
|
+
const MASK = 4095 // (1 << 12) - 1
|
|
4
7
|
|
|
5
8
|
export default function uncompress(
|
|
6
9
|
/* ByteBuffer */ input,
|
|
@@ -23,26 +26,59 @@ export default function uncompress(
|
|
|
23
26
|
let /* int */ l1 = 0
|
|
24
27
|
let /* int */ l2 = 0
|
|
25
28
|
let /* int */ l7 = 0
|
|
29
|
+
|
|
26
30
|
for (; i0 < isz4; i0 += 1, i1 += 1, i2 += 1, i7 += 1) {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
const D_l0_R = D[l0].R
|
|
32
|
+
const D_l1_R = D[l1].R
|
|
33
|
+
const D_l2_R = D[l2].R
|
|
34
|
+
const D_l7_R = D[l7].R
|
|
35
|
+
|
|
36
|
+
const /* int */ c0 = 0xff & D_l0_R[rans0 & MASK]
|
|
37
|
+
const /* int */ c1 = 0xff & D_l1_R[rans1 & MASK]
|
|
38
|
+
const /* int */ c2 = 0xff & D_l2_R[rans2 & MASK]
|
|
39
|
+
const /* int */ c7 = 0xff & D_l7_R[rans7 & MASK]
|
|
40
|
+
|
|
41
|
+
// Inline putAt to avoid function call overhead
|
|
42
|
+
output._buffer[i0] = c0
|
|
43
|
+
output._buffer[i1] = c1
|
|
44
|
+
output._buffer[i2] = c2
|
|
45
|
+
output._buffer[i7] = c7
|
|
46
|
+
|
|
47
|
+
const sym_l0_c0 = syms[l0][c0]
|
|
48
|
+
const sym_l1_c1 = syms[l1][c1]
|
|
49
|
+
const sym_l2_c2 = syms[l2][c2]
|
|
50
|
+
const sym_l7_c7 = syms[l7][c7]
|
|
51
|
+
|
|
52
|
+
rans0 =
|
|
53
|
+
sym_l0_c0.freq * (rans0 >> TF_SHIFT) + (rans0 & MASK) - sym_l0_c0.start
|
|
54
|
+
rans1 =
|
|
55
|
+
sym_l1_c1.freq * (rans1 >> TF_SHIFT) + (rans1 & MASK) - sym_l1_c1.start
|
|
56
|
+
rans2 =
|
|
57
|
+
sym_l2_c2.freq * (rans2 >> TF_SHIFT) + (rans2 & MASK) - sym_l2_c2.start
|
|
58
|
+
rans7 =
|
|
59
|
+
sym_l7_c7.freq * (rans7 >> TF_SHIFT) + (rans7 & MASK) - sym_l7_c7.start
|
|
60
|
+
|
|
61
|
+
// Inline renormalize to avoid function call overhead
|
|
62
|
+
if (rans0 < RANS_BYTE_L) {
|
|
63
|
+
do {
|
|
64
|
+
rans0 = (rans0 << 8) | (0xff & input.get())
|
|
65
|
+
} while (rans0 < RANS_BYTE_L)
|
|
66
|
+
}
|
|
67
|
+
if (rans1 < RANS_BYTE_L) {
|
|
68
|
+
do {
|
|
69
|
+
rans1 = (rans1 << 8) | (0xff & input.get())
|
|
70
|
+
} while (rans1 < RANS_BYTE_L)
|
|
71
|
+
}
|
|
72
|
+
if (rans2 < RANS_BYTE_L) {
|
|
73
|
+
do {
|
|
74
|
+
rans2 = (rans2 << 8) | (0xff & input.get())
|
|
75
|
+
} while (rans2 < RANS_BYTE_L)
|
|
76
|
+
}
|
|
77
|
+
if (rans7 < RANS_BYTE_L) {
|
|
78
|
+
do {
|
|
79
|
+
rans7 = (rans7 << 8) | (0xff & input.get())
|
|
80
|
+
} while (rans7 < RANS_BYTE_L)
|
|
81
|
+
}
|
|
46
82
|
|
|
47
83
|
l0 = c0
|
|
48
84
|
l1 = c1
|
|
@@ -52,9 +88,19 @@ export default function uncompress(
|
|
|
52
88
|
|
|
53
89
|
// Remainder
|
|
54
90
|
for (; i7 < outputSize; i7 += 1) {
|
|
55
|
-
const /* int */ c7 = 0xff & D[l7].R[
|
|
56
|
-
|
|
57
|
-
|
|
91
|
+
const /* int */ c7 = 0xff & D[l7].R[rans7 & MASK]
|
|
92
|
+
// Inline putAt to avoid function call overhead
|
|
93
|
+
output._buffer[i7] = c7
|
|
94
|
+
|
|
95
|
+
// Inline advanceSymbol to avoid function call overhead
|
|
96
|
+
const sym = syms[l7][c7]
|
|
97
|
+
rans7 = sym.freq * (rans7 >> TF_SHIFT) + (rans7 & MASK) - sym.start
|
|
98
|
+
if (rans7 < RANS_BYTE_L) {
|
|
99
|
+
do {
|
|
100
|
+
rans7 = (rans7 << 8) | (0xff & input.get())
|
|
101
|
+
} while (rans7 < RANS_BYTE_L)
|
|
102
|
+
}
|
|
103
|
+
|
|
58
104
|
l7 = c7
|
|
59
105
|
}
|
|
60
106
|
}
|
package/src/rans/decoding.ts
CHANGED
|
@@ -15,10 +15,9 @@ class AriDecoder {
|
|
|
15
15
|
// byte[] R;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
18
|
+
// Use two parallel arrays instead of array of objects for better cache efficiency
|
|
19
|
+
this.fcF = new Array(256)
|
|
20
|
+
this.fcC = new Array(256)
|
|
22
21
|
this.R = null
|
|
23
22
|
}
|
|
24
23
|
}
|
package/src/rans/frequencies.ts
CHANGED
|
@@ -19,25 +19,22 @@ export function readStatsO0(
|
|
|
19
19
|
let x = 0
|
|
20
20
|
let j = cp.get() & 0xff
|
|
21
21
|
do {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
decoder.fcF[j] = cp.get() & 0xff
|
|
23
|
+
if (decoder.fcF[j] >= 128) {
|
|
24
|
+
decoder.fcF[j] &= ~128
|
|
25
|
+
decoder.fcF[j] = ((decoder.fcF[j] & 127) << 8) | (cp.get() & 0xff)
|
|
24
26
|
}
|
|
25
|
-
decoder.
|
|
26
|
-
if (decoder.fc[j].F >= 128) {
|
|
27
|
-
decoder.fc[j].F &= ~128
|
|
28
|
-
decoder.fc[j].F = ((decoder.fc[j].F & 127) << 8) | (cp.get() & 0xff)
|
|
29
|
-
}
|
|
30
|
-
decoder.fc[j].C = x
|
|
27
|
+
decoder.fcC[j] = x
|
|
31
28
|
|
|
32
|
-
Decoding.symbolInit(syms[j], decoder.
|
|
29
|
+
Decoding.symbolInit(syms[j], decoder.fcC[j], decoder.fcF[j])
|
|
33
30
|
|
|
34
31
|
/* Build reverse lookup table */
|
|
35
32
|
if (!decoder.R) {
|
|
36
33
|
decoder.R = new Array(TOTFREQ)
|
|
37
34
|
}
|
|
38
|
-
decoder.R.fill(j, x, x + decoder.
|
|
35
|
+
decoder.R.fill(j, x, x + decoder.fcF[j])
|
|
39
36
|
|
|
40
|
-
x += decoder.
|
|
37
|
+
x += decoder.fcF[j]
|
|
41
38
|
|
|
42
39
|
if (rle === 0 && j + 1 === (0xff & cp.getByteAt(cp.position()))) {
|
|
43
40
|
j = cp.get() & 0xff
|
|
@@ -68,33 +65,30 @@ export function readStatsO1(
|
|
|
68
65
|
D[i] = new Decoding.AriDecoder()
|
|
69
66
|
}
|
|
70
67
|
do {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (D[i].fc[j].F >= 128) {
|
|
76
|
-
D[i].fc[j].F &= ~128
|
|
77
|
-
D[i].fc[j].F = ((D[i].fc[j].F & 127) << 8) | (0xff & cp.get())
|
|
68
|
+
D[i].fcF[j] = 0xff & cp.get()
|
|
69
|
+
if (D[i].fcF[j] >= 128) {
|
|
70
|
+
D[i].fcF[j] &= ~128
|
|
71
|
+
D[i].fcF[j] = ((D[i].fcF[j] & 127) << 8) | (0xff & cp.get())
|
|
78
72
|
}
|
|
79
|
-
D[i].
|
|
73
|
+
D[i].fcC[j] = x
|
|
80
74
|
|
|
81
|
-
if (D[i].
|
|
82
|
-
D[i].
|
|
75
|
+
if (D[i].fcF[j] === 0) {
|
|
76
|
+
D[i].fcF[j] = TOTFREQ
|
|
83
77
|
}
|
|
84
78
|
|
|
85
79
|
if (syms[i][j] == null) {
|
|
86
80
|
syms[i][j] = new Decoding.RansDecSymbol()
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
Decoding.symbolInit(syms[i][j], D[i].
|
|
83
|
+
Decoding.symbolInit(syms[i][j], D[i].fcC[j], D[i].fcF[j])
|
|
90
84
|
|
|
91
85
|
/* Build reverse lookup table */
|
|
92
86
|
if (D[i].R == null) {
|
|
93
87
|
D[i].R = new Array(TOTFREQ)
|
|
94
88
|
}
|
|
95
|
-
D[i].R.fill(j, x, x + D[i].
|
|
89
|
+
D[i].R.fill(j, x, x + D[i].fcF[j])
|
|
96
90
|
|
|
97
|
-
x += D[i].
|
|
91
|
+
x += D[i].fcF[j]
|
|
98
92
|
assert(x <= TOTFREQ)
|
|
99
93
|
|
|
100
94
|
if (rlej === 0 && j + 1 === (0xff & cp.getByteAt(cp.position()))) {
|