/
byte-array.ts
145 lines (125 loc) · 3.37 KB
/
byte-array.ts
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { Options } from "../options.js"
// Stat
export function bufferAllocatorStat() {
return stat
}
const stat: Stat = {
copied: 0,
maxAllocatedSize: 0,
maxOutputSize: 0,
}
interface Stat {
copied: number
maxAllocatedSize: number
maxOutputSize: number
}
// Options
export function applyOptions(opt: Options) {
bufferAllocator.base = opt.encoder.byteArray.base
}
/**
* Control buffer allocation for more than one ByteArray
*/
const bufferAllocator = {
base: 1024,
size(exponent = 0) {
const val = this.base * 2 ** exponent
if (val > stat.maxAllocatedSize) {
stat.maxAllocatedSize = val
}
return val
},
}
/**
* For easier memory operation
*/
export default class ByteArray {
private array: Uint8Array
private view: DataView
private pos: number
private newBufferExponent = 0
constructor() {
this.array = new Uint8Array(bufferAllocator.size())
this.view = new DataView(this.array.buffer)
this.pos = 0
}
private ensureEnoughSpace(reqSize: number): void {
const totalReqSize = this.pos + reqSize
if (totalReqSize >= this.array.byteLength) {
// Decide how many buffer will be allocated, normally one iteration is enough
let newSize = totalReqSize
while (newSize <= totalReqSize) {
newSize += bufferAllocator.size(this.newBufferExponent++) // exponential
}
// Create new buffer and copy content from original buffer
const newArray = new Uint8Array(newSize)
newArray.set(this.array)
this.view = new DataView(newArray.buffer)
this.array = newArray
// Increment stat
stat.copied++
}
}
writeUint8(number: number): void {
this.ensureEnoughSpace(1)
this.view.setUint8(this.pos, number)
this.pos += 1
}
writeUint16(number: number): void {
this.ensureEnoughSpace(2)
this.view.setUint16(this.pos, number, false)
this.pos += 2
}
writeUint32(number: number): void {
this.ensureEnoughSpace(4)
this.view.setUint32(this.pos, number, false)
this.pos += 4
}
writeUint64(number: bigint): void {
this.ensureEnoughSpace(8)
this.view.setBigUint64(this.pos, number, false)
this.pos += 8
}
writeInt8(number: number): void {
this.ensureEnoughSpace(1)
this.view.setInt8(this.pos, number)
this.pos += 1
}
writeInt16(number: number): void {
this.ensureEnoughSpace(2)
this.view.setInt16(this.pos, number, false)
this.pos += 2
}
writeInt32(number: number): void {
this.ensureEnoughSpace(4)
this.view.setInt32(this.pos, number, false)
this.pos += 4
}
writeInt64(number: bigint): void {
this.ensureEnoughSpace(8)
this.view.setBigInt64(this.pos, number, false)
this.pos += 8
}
writeFloat64(number: number): void {
this.ensureEnoughSpace(8)
this.view.setFloat64(this.pos, number, false)
this.pos += 8
}
append(array: Uint8Array): void {
this.ensureEnoughSpace(array.byteLength)
this.array.set(array, this.pos)
this.pos += array.byteLength
}
subarrayBackward(byteLength: number): Uint8Array {
return this.array.subarray(this.pos - byteLength, this.pos)
}
/**
* After writing all encoded data, use this function to create reference to those written bytes in buffer.
*/
getWrittenBytes(): Uint8Array {
if (this.pos > stat.maxOutputSize) {
stat.maxOutputSize = this.pos
}
return this.array.subarray(0, this.pos)
}
}