@gmod/cram
Version:
read CRAM files with pure Javascript
200 lines (189 loc) • 5.22 kB
text/typescript
import md5 from 'md5'
export const TWO_PWR_16_DBL = 1 << 16
export const TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL
export const TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL
export const TWO_PWR_24_DBL = 1 << 24
export const TWO_PWR_56_DBL = TWO_PWR_24_DBL * TWO_PWR_32_DBL
export function itf8Size(v: number) {
if (!(v & ~0x7f)) {
return 1
}
if (!(v & ~0x3fff)) {
return 2
}
if (!(v & ~0x1fffff)) {
return 3
}
if (!(v & ~0xfffffff)) {
return 4
}
return 5
}
export function parseItf8(buffer: Uint8Array, initialOffset: number) {
let offset = initialOffset
const countFlags = buffer[offset]!
let result: number
// Single byte value (0xxxxxxx)
if (countFlags < 0x80) {
result = countFlags
offset += 1
}
// Two byte value (10xxxxxx)
else if (countFlags < 0xc0) {
result = ((countFlags & 0x3f) << 8) | buffer[offset + 1]!
offset += 2
}
// Three byte value (110xxxxx)
else if (countFlags < 0xe0) {
result =
((countFlags & 0x1f) << 16) |
(buffer[offset + 1]! << 8) |
buffer[offset + 2]!
offset += 3
}
// Four byte value (1110xxxx)
else if (countFlags < 0xf0) {
result =
((countFlags & 0x0f) << 24) |
(buffer[offset + 1]! << 16) |
(buffer[offset + 2]! << 8) |
buffer[offset + 3]!
offset += 4
}
// Five byte value (11110xxx)
else {
result =
((countFlags & 0x0f) << 28) |
(buffer[offset + 1]! << 20) |
(buffer[offset + 2]! << 12) |
(buffer[offset + 3]! << 4) |
(buffer[offset + 4]! & 0x0f)
offset += 5
}
return [result, offset - initialOffset] as const
}
export function parseLtf8(buffer: Uint8Array, initialOffset: number) {
let offset = initialOffset
const countFlags = buffer[offset]!
let value: number
// Single byte value < 0x80
if (countFlags < 0x80) {
value = countFlags
offset += 1
}
// Two byte value < 0xC0
else if (countFlags < 0xc0) {
value = ((countFlags << 8) | buffer[offset + 1]!) & 0x3fff
offset += 2
}
// Three byte value < 0xE0
else if (countFlags < 0xe0) {
value =
((countFlags & 0x3f) << 16) |
(buffer[offset + 1]! << 8) |
buffer[offset + 2]!
offset += 3
}
// Four byte value < 0xF0
else if (countFlags < 0xf0) {
value =
((countFlags & 0x1f) << 24) |
(buffer[offset + 1]! << 16) |
(buffer[offset + 2]! << 8) |
buffer[offset + 3]!
offset += 4
}
// Five byte value < 0xF8
else if (countFlags < 0xf8) {
value =
(buffer[offset]! & 0x0f) * TWO_PWR_32_DBL +
((buffer[offset + 1]! << 24) |
(buffer[offset + 2]! << 16) |
(buffer[offset + 3]! << 8) |
buffer[offset + 4]!)
offset += 5
}
// Six byte value < 0xFC
else if (countFlags < 0xfc) {
value =
(((buffer[offset]! & 0x07) << 8) | buffer[offset + 1]!) * TWO_PWR_32_DBL +
((buffer[offset + 2]! << 24) |
(buffer[offset + 3]! << 16) |
(buffer[offset + 4]! << 8) |
buffer[offset + 5]!)
offset += 6
}
// Seven byte value < 0xFE
else if (countFlags < 0xfe) {
value =
(((buffer[offset]! & 0x03) << 16) |
(buffer[offset + 1]! << 8) |
buffer[offset + 2]!) *
TWO_PWR_32_DBL +
((buffer[offset + 3]! << 24) |
(buffer[offset + 4]! << 16) |
(buffer[offset + 5]! << 8) |
buffer[offset + 6]!)
offset += 7
}
// Eight byte value < 0xFF
else if (countFlags < 0xff) {
value =
((buffer[offset + 1]! << 24) |
(buffer[offset + 2]! << 16) |
(buffer[offset + 3]! << 8) |
buffer[offset + 4]!) *
TWO_PWR_32_DBL +
((buffer[offset + 5]! << 24) |
(buffer[offset + 6]! << 16) |
(buffer[offset + 7]! << 8) |
buffer[offset + 8]!)
offset += 8
}
// Nine byte value
else {
value =
buffer[offset + 1]! * TWO_PWR_56_DBL +
((buffer[offset + 2]! << 24) |
(buffer[offset + 3]! << 16) |
(buffer[offset + 4]! << 8) |
buffer[offset + 5]!) *
TWO_PWR_32_DBL +
((buffer[offset + 6]! << 24) |
(buffer[offset + 7]! << 16) |
(buffer[offset + 8]! << 8) |
buffer[offset + 9]!)
offset += 9
}
return [value, offset - initialOffset] as const
}
export function parseItem<T>(
buffer: Uint8Array,
parser: (buffer: Uint8Array, offset: number) => { offset: number; value: T },
startBufferPosition = 0,
startFilePosition = 0,
) {
const { offset, value } = parser(buffer, startBufferPosition)
return {
...value,
_endPosition: offset + startFilePosition,
_size: offset - startBufferPosition,
}
}
export function tinyMemoize(_class: any, methodName: any) {
const method = _class.prototype[methodName]
const memoAttrName = `_memo_${methodName}`
_class.prototype[methodName] = function _tinyMemoized() {
if (!(memoAttrName in this)) {
const res = method.call(this)
this[memoAttrName] = res
Promise.resolve(res).catch(() => {
delete this[memoAttrName]
})
}
return this[memoAttrName]
}
}
export function sequenceMD5(seq: string) {
return md5(seq.toUpperCase().replaceAll(/[^\u0021-\u007e]/g, ''))
}