All files decoder.ts

72.72% Statements 32/44
35.71% Branches 5/14
100% Functions 9/9
72.72% Lines 32/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 921x 30x         30x 22x 22x   8x 8x         126x       116x             62x 62x         8x 8x 8x   8x         4x 4x   4x 4x                         8x 8x       46x 46x 46x 46x       4x       24x 24x     24x 24x       24x       8x      
export class Decoder {
    private pos = 0
    private data: DataView
    private array: Uint8Array
 
    constructor(data: Uint8Array | ArrayBuffer) {
        if (data instanceof Uint8Array) {
            this.data = new DataView(data.buffer, data.byteOffset, data.byteLength)
            this.array = data
        } else {
            this.data = new DataView(data)
            this.array = new Uint8Array(data)
        }
    }
 
    canRead(bytes = 1): boolean {
        return !(this.pos + bytes > this.array.byteLength)
    }
 
    private ensure(bytes: number) {
        Iif (!this.canRead(bytes)) {
            throw new Error('Read past end of buffer')
        }
    }
 
    /** Read one byte. */
    readByte(): number {
        this.ensure(1)
        return this.array[this.pos++]
    }
 
    /** Read integer as JavaScript number, up to 32 bits. */
    readNum(byteWidth: number, littleEndian = false) {
        this.ensure(byteWidth)
        const d = this.data,
            p = this.pos
        let rv: number
        switch (byteWidth) {
            case 1:
                rv = d.getUint8(p)
                break
            case 2:
                rv = d.getUint16(p, littleEndian)
                break
            case 4:
                rv = d.getUint32(p, littleEndian)
                break
            case -1:
                rv = d.getInt8(p)
                break
            case -2:
                rv = d.getInt16(p, littleEndian)
                break
            case -4:
                rv = d.getInt32(p, littleEndian)
                break
            default:
                throw new Error('Invalid integer width')
        }
        this.pos += byteWidth
        return rv
    }
 
    readArray(length: number) {
        this.ensure(length)
        const rv = this.array.subarray(this.pos, this.pos + length)
        this.pos += length
        return rv
    }
 
    remainder() {
        return this.array.subarray(this.pos)
    }
 
    readDer(tag: number) {
        const t = this.readByte()
        Iif (t !== tag) {
            throw new Error(`Unexpected DER tag: ${t}`)
        }
        const len = this.readByte()
        Iif (len < 0) {
            throw new Error('Invalid DER length')
        }
 
        return this.readArray(len)
    }
 
    derDecoder(tag: number) {
        return new Decoder(this.readDer(tag))
    }
}