• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LukaJCB / ts-mls / 19402070633

16 Nov 2025 07:13AM UTC coverage: 94.056% (+0.02%) from 94.037%
19402070633

Pull #141

github

web-flow
Merge 36482b5bd into 633311aca
Pull Request #141: Encoder overhaul

1167 of 1296 branches covered (90.05%)

Branch coverage included in aggregate %.

754 of 764 new or added lines in 49 files covered. (98.69%)

14 existing lines in 4 files now uncovered.

6698 of 7066 relevant lines covered (94.79%)

55535.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.09
/src/codec/variableLength.ts
1
import { CodecError } from "../mlsError.js"
1✔
2
import { Decoder } from "./tlsDecoder.js"
3
import { Enc, Encoder } from "./tlsEncoder.js"
4

5
export const encodeVarLenData: Encoder<Uint8Array> = (data) => {
1✔
6
  const lenBytes: Uint8Array = encodeLength(data.length)
39✔
7

8
  const result = new Uint8Array(lenBytes.length + data.length)
39✔
9
  result.set(lenBytes, 0)
39✔
10
  result.set(data, lenBytes.length)
39✔
11
  return result
39✔
12
}
39✔
13

14
export const encVarLenData: Enc<Uint8Array> = (data) => {
1✔
15
  const [len, write] = encLength(data.length)
138,629✔
16

17
  return [
138,629✔
18
    len + data.length,
138,629✔
19
    (offset, buffer) => {
138,629✔
20
      write(offset, buffer)
1,215,594✔
21
      const view = new Uint8Array(buffer)
1,215,594✔
22
      view.set(data, offset + len)
1,215,594✔
23
    },
1,215,594✔
24
  ]
138,629✔
25
}
138,629✔
26

27
export function encodeLength(len: number): Uint8Array {
1✔
28
  if (len < 64) {
44✔
29
    // 1-byte length: 00xxxxxx
30
    return new Uint8Array([len & 0b00111111])
16✔
31
  } else if (len < 16384) {
44✔
32
    // 2-byte length: 01xxxxxx xxxxxxxx
33
    return new Uint8Array([((len >> 8) & 0b00111111) | 0b01000000, len & 0xff])
26✔
34
  } else if (len < 0x40000000) {
28✔
35
    // 4-byte length: 10xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
36
    return new Uint8Array([((len >> 24) & 0b00111111) | 0b10000000, (len >> 16) & 0xff, (len >> 8) & 0xff, len & 0xff])
1✔
37
  } else {
1✔
38
    throw new CodecError("Length too large to encode (max is 2^30 - 1)")
1✔
39
  }
1✔
40
}
44✔
41

42
export function encLength(len: number): [number, (offset: number, buffer: ArrayBuffer) => void] {
1✔
43
  if (len < 64) {
3,285,518✔
44
    return [
2,606,779✔
45
      1,
2,606,779✔
46
      (offset, buffer) => {
2,606,779✔
47
        // 1-byte length: 00xxxxxx
48
        const view = new DataView(buffer)
2,606,779✔
49
        view.setUint8(offset, len & 0b00111111)
2,606,779✔
50
      },
2,606,779✔
51
    ]
2,606,779✔
52
  } else if (len < 16384) {
3,285,518✔
53
    return [
677,779✔
54
      2,
677,779✔
55
      (offset, buffer) => {
677,779✔
56
        // 2-byte length: 01xxxxxx xxxxxxxx
57
        const view = new DataView(buffer)
677,779✔
58
        view.setUint8(offset, ((len >> 8) & 0b00111111) | 0b01000000)
677,779✔
59
        view.setUint8(offset + 1, len & 0xff)
677,779✔
60
      },
677,779✔
61
    ]
677,779✔
62
  } else if (len < 0x40000000) {
678,739✔
63
    return [
960✔
64
      4,
960✔
65
      (offset, buffer) => {
960✔
66
        // 4-byte length: 10xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
67
        const view = new DataView(buffer)
960✔
68
        view.setUint8(offset, ((len >> 24) & 0b00111111) | 0b10000000)
960✔
69
        view.setUint8(offset + 1, (len >> 16) & 0xff)
960✔
70
        view.setUint8(offset + 2, (len >> 8) & 0xff)
960✔
71
        view.setUint8(offset + 3, len & 0xff)
960✔
72
      },
960✔
73
    ]
960✔
74
  } else {
960!
UNCOV
75
    throw new CodecError("Length too large to encode (max is 2^30 - 1)")
×
UNCOV
76
  }
×
77
}
3,285,518✔
78

79
export function determineLength(data: Uint8Array, offset: number = 0): { length: number; lengthFieldSize: number } {
1✔
80
  if (offset >= data.length) {
220,735✔
81
    throw new CodecError("Offset beyond buffer")
1✔
82
  }
1✔
83

84
  const firstByte = data[offset] as number
220,734✔
85
  const prefix = firstByte >> 6
220,734✔
86

87
  if (prefix === 0) {
220,735✔
88
    return { length: firstByte & 0b00111111, lengthFieldSize: 1 }
146,418✔
89
  } else if (prefix === 1) {
220,735✔
90
    if (offset + 2 > data.length) throw new CodecError("Incomplete 2-byte length")
74,128!
91
    return { length: ((firstByte & 0b00111111) << 8) | (data[offset + 1] as number), lengthFieldSize: 2 }
74,128✔
92
  } else if (prefix === 2) {
74,316✔
93
    if (offset + 4 > data.length) throw new CodecError("Incomplete 4-byte length")
187!
94
    return {
187✔
95
      length:
187✔
96
        ((firstByte & 0b00111111) << 24) |
187✔
97
        ((data[offset + 1] as number) << 16) |
187✔
98
        ((data[offset + 2] as number) << 8) |
187✔
99
        (data[offset + 3] as number),
187✔
100
      lengthFieldSize: 4,
187✔
101
    }
187✔
102
  } else {
188✔
103
    throw new CodecError("8-byte length not supported in this implementation")
1✔
104
  }
1✔
105
}
220,735✔
106

107
export const decodeVarLenData: Decoder<Uint8Array> = (buf, offset) => {
1✔
108
  if (offset >= buf.length) {
220,720✔
109
    throw new CodecError("Offset beyond buffer")
1✔
110
  }
1✔
111

112
  const { length, lengthFieldSize } = determineLength(buf, offset)
220,719✔
113

114
  const totalBytes = lengthFieldSize + length
220,719✔
115
  if (offset + totalBytes > buf.length) {
220,720✔
116
    throw new CodecError("Data length exceeds buffer")
1✔
117
  }
1✔
118

119
  const data = buf.subarray(offset + lengthFieldSize, offset + totalBytes)
220,718✔
120
  return [data, totalBytes]
220,718✔
121
}
220,718✔
122

123
export function encodeVarLenType<T>(enc: Encoder<T>): Encoder<T[]> {
1✔
124
  return (data) => {
4✔
125
    const encodedParts = new Array<Uint8Array>(data.length)
4✔
126
    let dataLength = 0
4✔
127

128
    for (let i = 0; i < data.length; i++) {
4✔
129
      const encoded = enc(data[i]!)
22✔
130
      dataLength += encoded.byteLength
22✔
131
      encodedParts[i] = encoded
22✔
132
    }
22✔
133

134
    const lengthHeader: Uint8Array = encodeLength(dataLength)
4✔
135

136
    const result = new Uint8Array(lengthHeader.length + dataLength)
4✔
137
    result.set(lengthHeader, 0)
4✔
138
    let offset = lengthHeader.length
4✔
139

140
    for (const arr of encodedParts) {
4✔
141
      result.set(arr, offset)
22✔
142
      offset += arr.length
22✔
143
    }
22✔
144
    return result
4✔
145
  }
4✔
146
}
4✔
147

148
export function encVarLenType<T>(enc: Enc<T>): Enc<T[]> {
1✔
149
  return (data) => {
66✔
150
    let totalLength = 0
993,805✔
151
    let writeTotal = (_offset: number, _buffer: ArrayBuffer) => {}
993,805✔
152
    for (let i = 0; i < data.length; i++) {
993,805✔
153
      const [len, write] = enc(data[i]!)
2,792,304✔
154
      const oldFunc = writeTotal
2,792,304✔
155
      const currentLen = totalLength
2,792,304✔
156
      writeTotal = (offset: number, buffer: ArrayBuffer) => {
2,792,304✔
157
        oldFunc(offset, buffer)
2,792,323✔
158
        write(offset + currentLen, buffer)
2,792,323✔
159
      }
2,792,323✔
160
      totalLength += len
2,792,304✔
161
    }
2,792,304✔
162
    const [headerLength, writeLength] = encLength(totalLength)
2,792,304✔
163
    return [
2,792,304✔
164
      headerLength + totalLength,
2,792,304✔
165
      (offset, buffer) => {
2,792,304✔
166
        writeLength(offset, buffer)
993,805✔
167
        writeTotal(offset + headerLength, buffer)
993,805✔
168
      },
993,805✔
169
    ]
2,792,304✔
170
  }
993,805✔
171
}
66✔
172

173
export function decodeVarLenType<T>(dec: Decoder<T>): Decoder<T[]> {
1✔
174
  return (b, offset) => {
71✔
175
    const d = decodeVarLenData(b, offset)
89,556✔
176
    if (d === undefined) return
89,556!
177

178
    const [totalBytes, totalLength] = d
89,556✔
179

180
    let cursor = 0
89,556✔
181
    const result: T[] = []
89,556✔
182

183
    while (cursor < totalBytes.length) {
89,556✔
184
      const item = dec(totalBytes, cursor)
180,351✔
185
      if (item === undefined) return undefined
180,351✔
186

187
      const [value, len] = item
180,331✔
188
      result.push(value)
180,331✔
189
      cursor += len
180,331✔
190
    }
180,331✔
191

192
    return [result, totalLength]
89,536✔
193
  }
89,556✔
194
}
71✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc