@helios-lang/effect 0.1.5 → 0.1.7
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/bun.lock +60 -0
- package/dist/Uplc/Data.js +20 -0
- package/dist/Uplc/Data.js.map +1 -1
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.check.tsbuildinfo +1 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/types/Address.d.ts +5 -0
- package/types/Address.d.ts.map +1 -0
- package/types/Bech32.d.ts +30 -0
- package/types/Bech32.d.ts.map +1 -0
- package/types/Cbor.d.ts +430 -0
- package/types/Cbor.d.ts.map +1 -0
- package/types/Ledger/Address.d.ts +109 -0
- package/types/Ledger/Address.d.ts.map +1 -0
- package/types/Ledger/AssetClass.d.ts +101 -0
- package/types/Ledger/AssetClass.d.ts.map +1 -0
- package/types/Ledger/Assets.d.ts +70 -0
- package/types/Ledger/Assets.d.ts.map +1 -0
- package/types/Ledger/Credential.d.ts +26 -0
- package/types/Ledger/Credential.d.ts.map +1 -0
- package/types/Ledger/DatumHash.d.ts +40 -0
- package/types/Ledger/DatumHash.d.ts.map +1 -0
- package/types/Ledger/IsMainnet.d.ts +6 -0
- package/types/Ledger/IsMainnet.d.ts.map +1 -0
- package/types/Ledger/MintingPolicy.d.ts +39 -0
- package/types/Ledger/MintingPolicy.d.ts.map +1 -0
- package/{src/Ledger/NetworkParams.ts → types/Ledger/NetworkParams.d.ts} +24 -26
- package/types/Ledger/NetworkParams.d.ts.map +1 -0
- package/types/Ledger/PubKeyHash.d.ts +40 -0
- package/types/Ledger/PubKeyHash.d.ts.map +1 -0
- package/types/Ledger/TxId.d.ts +10 -0
- package/types/Ledger/TxId.d.ts.map +1 -0
- package/types/Ledger/TxInput.d.ts +55 -0
- package/types/Ledger/TxInput.d.ts.map +1 -0
- package/types/Ledger/TxOutput.d.ts +63 -0
- package/types/Ledger/TxOutput.d.ts.map +1 -0
- package/types/Ledger/TxOutputDatum.d.ts +41 -0
- package/types/Ledger/TxOutputDatum.d.ts.map +1 -0
- package/types/Ledger/TxOutputId.d.ts +14 -0
- package/types/Ledger/TxOutputId.d.ts.map +1 -0
- package/types/Ledger/ValidatorHash.d.ts +40 -0
- package/types/Ledger/ValidatorHash.d.ts.map +1 -0
- package/types/Ledger/index.d.ts +16 -0
- package/types/Ledger/index.d.ts.map +1 -0
- package/types/Uplc/Cek.d.ts +72 -0
- package/types/Uplc/Cek.d.ts.map +1 -0
- package/types/Uplc/Data.d.ts +559 -0
- package/types/Uplc/Data.d.ts.map +1 -0
- package/types/Uplc/DataSchema.d.ts +227 -0
- package/types/Uplc/DataSchema.d.ts.map +1 -0
- package/types/Uplc/Primitive.d.ts +26 -0
- package/types/Uplc/Primitive.d.ts.map +1 -0
- package/types/Uplc/index.d.ts +3 -0
- package/types/Uplc/index.d.ts.map +1 -0
- package/types/index.d.ts +5 -0
- package/types/index.d.ts.map +1 -0
- package/types/internal/Base32.d.ts +49 -0
- package/types/internal/Base32.d.ts.map +1 -0
- package/types/internal/BigEndian.d.ts +22 -0
- package/types/internal/BigEndian.d.ts.map +1 -0
- package/types/internal/Bits.d.ts +123 -0
- package/types/internal/Bits.d.ts.map +1 -0
- package/types/internal/Bytes.d.ts +88 -0
- package/types/internal/Bytes.d.ts.map +1 -0
- package/types/internal/Flat.d.ts +71 -0
- package/types/internal/Flat.d.ts.map +1 -0
- package/types/internal/Float.d.ts +38 -0
- package/types/internal/Float.d.ts.map +1 -0
- package/types/internal/Utf8.d.ts +24 -0
- package/types/internal/Utf8.d.ts.map +1 -0
- package/src/Bech32.test.ts +0 -117
- package/src/Bech32.ts +0 -198
- package/src/Cbor.test.ts +0 -1610
- package/src/Cbor.ts +0 -1704
- package/src/Ledger/Address.ts +0 -248
- package/src/Ledger/AssetClass.ts +0 -90
- package/src/Ledger/Assets.ts +0 -164
- package/src/Ledger/Credential.ts +0 -29
- package/src/Ledger/DatumHash.ts +0 -36
- package/src/Ledger/IsMainnet.ts +0 -6
- package/src/Ledger/MintingPolicy.ts +0 -57
- package/src/Ledger/PubKeyHash.ts +0 -36
- package/src/Ledger/TxId.ts +0 -31
- package/src/Ledger/TxInput.test.ts +0 -21
- package/src/Ledger/TxInput.ts +0 -66
- package/src/Ledger/TxOutput.ts +0 -166
- package/src/Ledger/TxOutputDatum.ts +0 -64
- package/src/Ledger/TxOutputId.ts +0 -63
- package/src/Ledger/ValidatorHash.ts +0 -36
- package/src/Ledger/index.ts +0 -15
- package/src/Uplc/Cek.ts +0 -92
- package/src/Uplc/Data.test.ts +0 -321
- package/src/Uplc/Data.ts +0 -657
- package/src/Uplc/Primitive.ts +0 -56
- package/src/Uplc/index.ts +0 -2
- package/src/index.ts +0 -4
- package/src/internal/Base32.test.ts +0 -219
- package/src/internal/Base32.ts +0 -341
- package/src/internal/BigEndian.test.ts +0 -79
- package/src/internal/BigEndian.ts +0 -67
- package/src/internal/Bits.test.ts +0 -300
- package/src/internal/Bits.ts +0 -398
- package/src/internal/Bytes.test.ts +0 -369
- package/src/internal/Bytes.ts +0 -343
- package/src/internal/Flat.test.ts +0 -29
- package/src/internal/Flat.ts +0 -387
- package/src/internal/Float.test.ts +0 -51
- package/src/internal/Float.ts +0 -190
- package/src/internal/Utf8.test.ts +0 -69
- package/src/internal/Utf8.ts +0 -58
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test"
|
|
2
|
-
import { Either } from "effect"
|
|
3
|
-
import * as Bits from "./Bits.js"
|
|
4
|
-
|
|
5
|
-
describe("Bits.Reader", () => {
|
|
6
|
-
describe("initialized with [255]", () => {
|
|
7
|
-
const bytes = [255]
|
|
8
|
-
|
|
9
|
-
it("returns 7 when reading the first 3 bits", () => {
|
|
10
|
-
const r = Bits.makeReader(bytes)
|
|
11
|
-
expect(r.readBits(3)).toBe(7)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it("is NOT at EOF when reading the first 7 bits", () => {
|
|
15
|
-
const r = Bits.makeReader(bytes)
|
|
16
|
-
r.readBits(7)
|
|
17
|
-
expect(r.isAtEnd()).toBe(false)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it("is at EOF when reading the first 8 bits", () => {
|
|
21
|
-
const r = Bits.makeReader(bytes)
|
|
22
|
-
r.readBits(8)
|
|
23
|
-
expect(r.isAtEnd()).toBe(true)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it("fails when reading 9 bits", () => {
|
|
27
|
-
const r = Bits.makeReader(bytes)
|
|
28
|
-
expect(() => r.readBits(9)).toThrow()
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
describe("initialized with an empty Uint8Array", () => {
|
|
33
|
-
it(`fails when reading a single bit`, () => {
|
|
34
|
-
const r = Bits.makeReader(new Uint8Array(0))
|
|
35
|
-
expect(() => r.readBits(1)).toThrow()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it(`fails when reading a single byte`, () => {
|
|
39
|
-
const r = Bits.makeReader(new Uint8Array(0))
|
|
40
|
-
expect(() => r.readByte()).toThrow()
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
describe("initialized with [255, 255] and truncate set to false", () => {
|
|
45
|
-
const bytes = [255, 255]
|
|
46
|
-
|
|
47
|
-
describe("discard 14 bits", () => {
|
|
48
|
-
it("returns 3 when reading 2 bits", () => {
|
|
49
|
-
const r = Bits.makeReader(bytes, false)
|
|
50
|
-
r.readBits(7)
|
|
51
|
-
r.readBits(7)
|
|
52
|
-
expect(r.readBits(2)).toBe(3)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it("returns 0b11000000 when reading 8 bits", () => {
|
|
56
|
-
const r = Bits.makeReader(bytes, false)
|
|
57
|
-
r.readBits(7)
|
|
58
|
-
r.readBits(7)
|
|
59
|
-
expect(r.readBits(8)).toBe(0b11000000)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe("initialized with [0, 1, 2, ...] so that never at end", () => {
|
|
65
|
-
const bytes: number[] = []
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < 1000; i++) {
|
|
68
|
-
bytes.push(i % 256)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
it("fails when reading more than 8 bits at a time", () => {
|
|
72
|
-
const r = Bits.makeReader(bytes)
|
|
73
|
-
expect(() => r.readBits(9)).toThrow()
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it("after reading 8 bits and moving to byte boundary (which has no effect), returns 1 when reading a byte ", () => {
|
|
77
|
-
const r = Bits.makeReader(bytes)
|
|
78
|
-
r.readBits(8)
|
|
79
|
-
r.moveToByteBoundary()
|
|
80
|
-
expect(r.readByte()).toBe(1)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it("after reading 7 bits and moving to byte boundary, returns 1 when reading a byte", () => {
|
|
84
|
-
const r = Bits.makeReader(bytes)
|
|
85
|
-
r.readBits(7)
|
|
86
|
-
r.moveToByteBoundary()
|
|
87
|
-
expect(r.readByte()).toBe(1)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it("after forcing a move to byte boundary from start, returns 1 when reading a byte", () => {
|
|
91
|
-
const r = Bits.makeReader(bytes)
|
|
92
|
-
r.moveToByteBoundary(true)
|
|
93
|
-
expect(r.readByte()).toBe(1)
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
describe("Bits.Writer", () => {
|
|
99
|
-
describe("initialized without writing any bits", () => {
|
|
100
|
-
it("finalizes as []", () => {
|
|
101
|
-
expect(Bits.makeWriter().finalize(false)).toEqual([])
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it("finalizes as [] after writing an empty bit-string", () => {
|
|
105
|
-
expect(Bits.makeWriter().writeBits("").finalize(false)).toEqual([])
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('fails when writing a bit-string not consisting of only "0"s and "1"s', () => {
|
|
109
|
-
const w = Bits.makeWriter()
|
|
110
|
-
expect(() => {
|
|
111
|
-
w.writeBits("2")
|
|
112
|
-
}).toThrow()
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it("fails when writing -1 as a byte", () => {
|
|
116
|
-
const w = Bits.makeWriter()
|
|
117
|
-
expect(() => w.writeByte(-1)).toThrow()
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it("fails when writing 256 as a byte", () => {
|
|
121
|
-
const w = Bits.makeWriter()
|
|
122
|
-
expect(() => w.writeByte(256)).toThrow()
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
describe('initialized by writing "0", "" (empty string), and then "1"', () => {
|
|
127
|
-
it("finalizes as [0b01000001]", () => {
|
|
128
|
-
expect(
|
|
129
|
-
Bits.makeWriter().writeBits("0").writeBits("1").finalize(false)
|
|
130
|
-
).toEqual([0b01000001])
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it("finalizes as [] after popping 2 bits", () => {
|
|
134
|
-
const w = Bits.makeWriter()
|
|
135
|
-
w.writeBits("0").writeBits("").writeBits("1").pop(2)
|
|
136
|
-
expect(w.finalize(false)).toEqual([])
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it("fails when popping 3 bits", () => {
|
|
140
|
-
const w = Bits.makeWriter()
|
|
141
|
-
expect(() =>
|
|
142
|
-
w.writeBits("0").writeBits("").writeBits("1").pop(3)
|
|
143
|
-
).toThrow()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it("finalizes as a bit-string with length divisible by 8", () => {
|
|
147
|
-
const w = Bits.makeWriter()
|
|
148
|
-
w.writeBits("0").writeBits("1").finalize(false)
|
|
149
|
-
expect(w.length % 8).toBe(0)
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
describe("initialized by writing 7 as a single byte", () => {
|
|
154
|
-
it("finalizes as [7]", () => {
|
|
155
|
-
const w = Bits.makeWriter()
|
|
156
|
-
expect(w.writeByte(7).finalize(false)).toEqual([7])
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it("finalizes as [7, 1] if force is set to true", () => {
|
|
160
|
-
const w = Bits.makeWriter()
|
|
161
|
-
expect(w.writeByte(7).finalize(true)).toEqual([7, 1])
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('returns "111" when popping 3 bits', () => {
|
|
165
|
-
const w = Bits.makeWriter()
|
|
166
|
-
w.writeByte(7)
|
|
167
|
-
expect(w.pop(3)).toBe("111")
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it("fails when popping a negative number of bits", () => {
|
|
171
|
-
const w = Bits.makeWriter()
|
|
172
|
-
w.writeByte(7)
|
|
173
|
-
expect(() => w.pop(-1)).toThrow()
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it("returns an empty string when when popping 0 bits", () => {
|
|
177
|
-
const w = Bits.makeWriter()
|
|
178
|
-
w.writeByte(7)
|
|
179
|
-
expect(w.pop(0)).toBe("")
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it("after popping 3 bits, finalizes as [1]", () => {
|
|
183
|
-
const w = Bits.makeWriter()
|
|
184
|
-
w.writeByte(7).pop(3)
|
|
185
|
-
expect(w.finalize(false)).toEqual([1])
|
|
186
|
-
})
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
describe("Bits.fromByte", () => {
|
|
191
|
-
describe("calling with 7 as the input byte", () => {
|
|
192
|
-
it('returns "0b00000111"', () => {
|
|
193
|
-
expect(Bits.fromByte(7)).toEqual(Either.right("0b00000111"))
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('returns "00000111" if prefix=false', () => {
|
|
197
|
-
expect(Bits.fromByte(7, 8, false)).toEqual(Either.right("00000111"))
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('returns "111" if n=3 and prefix=false', () => {
|
|
201
|
-
expect(Bits.fromByte(7, 3, false)).toEqual(Either.right("111"))
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it("fails if n=2", () => {
|
|
205
|
-
expect(Bits.fromByte(7, 2)._tag).toBe("Left")
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
it("fails for 0 if n=0", () => {
|
|
210
|
-
expect(Bits.fromByte(0, 0, false)._tag).toBe("Left")
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it("fails for a negative number", () => {
|
|
214
|
-
expect(Bits.fromByte(-1)._tag).toBe("Left")
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it("fails for a non-whole number", () => {
|
|
218
|
-
expect(Bits.fromByte(3.14)._tag).toBe("Left")
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
it("fails for a number larger than 255", () => {
|
|
222
|
-
expect(Bits.fromByte(256)._tag).toBe("Left")
|
|
223
|
-
})
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
describe("Bits.getBit", () => {
|
|
227
|
-
it("get first bit of #00ff returns 0", () => {
|
|
228
|
-
expect(Bits.getBit([0x00, 0xff], 0)).toBe(0)
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it("get last bit of #00ff returns 1", () => {
|
|
232
|
-
expect(Bits.getBit([0x00, 0xff], 15)).toBe(1)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it("returns 0 when getting indexing past bytes", () => {
|
|
236
|
-
expect(Bits.getBit([0x00, 0xff], 16)).toBe(0)
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
it("returns 0 when calling with negative index", () => {
|
|
240
|
-
expect(Bits.getBit([0x00, 0xff], -1)).toBe(0)
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
describe("Bits.mask", () => {
|
|
245
|
-
describe("calling with 0b11111111 as the input byte", () => {
|
|
246
|
-
it("returns 0b0111 if range=[1, 4)", () => {
|
|
247
|
-
expect(Bits.mask(0b11111111, 1, 4)).toBe(0b0111)
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
it("returns 0b11111111 if range=[0, 8)", () => {
|
|
251
|
-
const bits = 0b11111111
|
|
252
|
-
expect(Bits.mask(bits, 0, 8)).toBe(bits)
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
it("fails for range=[1, 1)", () => {
|
|
256
|
-
expect(() => Bits.mask(0b11111111, 1, 1)).toThrow()
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it("fails for a range starting with a negative number", () => {
|
|
260
|
-
expect(() => Bits.mask(0b11111111, -1, 8)).toThrow()
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
it("fails for a range starting after 7", () => {
|
|
264
|
-
expect(() => Bits.mask(0b11111111, 8, 9)).toThrow()
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
it("fails for a range ending after 8", () => {
|
|
268
|
-
expect(() => Bits.mask(0b11111111, 0, 9)).toThrow()
|
|
269
|
-
})
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
it("fails for a negative input number", () => {
|
|
273
|
-
expect(() => Bits.mask(-1, 0, 8)).toThrow()
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
it("fails for an input number larger than 255", () => {
|
|
277
|
-
expect(() => Bits.mask(256, 0, 8)).toThrow()
|
|
278
|
-
})
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
describe("Bits.pad", () => {
|
|
282
|
-
describe('calling with "1111" as a bit-string', () => {
|
|
283
|
-
it('returns "00001111" if n=8', () => {
|
|
284
|
-
expect(Bits.pad("1111", 8)).toBe("00001111")
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
it('returns "001111" if n=3 (pads to next multiple of n if n is less than the number of bits)', () => {
|
|
288
|
-
expect(Bits.pad("1111", 3)).toBe("001111")
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
it('returns "1111" if n=4 (does nothing if n is equal to the input number of bits)', () => {
|
|
292
|
-
const bits = "1111"
|
|
293
|
-
expect(Bits.pad(bits, bits.length)).toBe(bits)
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
it("fails for negative n", () => {
|
|
297
|
-
expect(() => Bits.pad("1111", -1)).toThrow()
|
|
298
|
-
})
|
|
299
|
-
})
|
|
300
|
-
})
|
package/src/internal/Bits.ts
DELETED
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import { Either } from "effect"
|
|
2
|
-
import * as Bytes from "./Bytes.js"
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Read non-byte aligned numbers
|
|
6
|
-
*/
|
|
7
|
-
export interface Reader {
|
|
8
|
-
isAtEnd(): boolean
|
|
9
|
-
moveToByteBoundary(force?: boolean): void
|
|
10
|
-
readBits(n: number): number
|
|
11
|
-
readByte(): number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param bytes
|
|
16
|
-
* @param truncate defaults to true
|
|
17
|
-
* @returns {BitReader}
|
|
18
|
-
*/
|
|
19
|
-
export function makeReader(
|
|
20
|
-
bytes: string | number[] | Uint8Array,
|
|
21
|
-
truncate: boolean = true
|
|
22
|
-
): Reader {
|
|
23
|
-
return new ReaderImpl(bytes, truncate ?? true)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
class ReaderImpl implements Reader {
|
|
27
|
-
private readonly view: Uint8Array
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* bit position, not byte position
|
|
31
|
-
*/
|
|
32
|
-
private pos: number
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* If true then read last bits as low part of number, if false pad with zero bits (only applies when trying to read more bits than there are left )
|
|
36
|
-
*/
|
|
37
|
-
private readonly truncate: boolean
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @param bytes
|
|
41
|
-
* @param truncate determines behavior when reading too many bits
|
|
42
|
-
*/
|
|
43
|
-
constructor(bytes: string | number[] | Uint8Array, truncate: boolean = true) {
|
|
44
|
-
this.view = Bytes.toUint8Array(bytes)
|
|
45
|
-
|
|
46
|
-
this.pos = 0
|
|
47
|
-
this.truncate = truncate
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
isAtEnd(): boolean {
|
|
51
|
-
return Math.trunc(this.pos / 8) >= this.view.length
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Moves position to next byte boundary
|
|
56
|
-
* @param force
|
|
57
|
-
* If true then move to next byte boundary if already at byte boundary
|
|
58
|
-
*/
|
|
59
|
-
moveToByteBoundary(force: boolean = false): void {
|
|
60
|
-
if (this.pos % 8 != 0) {
|
|
61
|
-
const n = 8 - (this.pos % 8)
|
|
62
|
-
|
|
63
|
-
void this.readBits(n)
|
|
64
|
-
} else if (force) {
|
|
65
|
-
this.readBits(8)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Reads a number of bits (<= 8) and returns the result as an unsigned number
|
|
71
|
-
* @param n number of bits to read
|
|
72
|
-
* @returns
|
|
73
|
-
* @throws
|
|
74
|
-
* If at end
|
|
75
|
-
* @throws
|
|
76
|
-
* If n is larger than 8
|
|
77
|
-
*/
|
|
78
|
-
readBits(n: number): number {
|
|
79
|
-
if (n > 8) {
|
|
80
|
-
throw new RangeError(
|
|
81
|
-
`Reading more than 1 byte (trying to read ${n} bits)`
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let leftShift = 0
|
|
86
|
-
if (this.pos + n > this.view.length * 8) {
|
|
87
|
-
const newN = this.view.length * 8 - this.pos
|
|
88
|
-
|
|
89
|
-
if (!this.truncate) {
|
|
90
|
-
leftShift = n - newN
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
n = newN
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (n == 0) {
|
|
97
|
-
throw new Error("Bits.Reader is at end")
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// it is assumed we don't need to be at the byte boundary
|
|
101
|
-
|
|
102
|
-
let res = 0
|
|
103
|
-
let i0 = this.pos
|
|
104
|
-
|
|
105
|
-
for (let i = this.pos + 1; i <= this.pos + n; i++) {
|
|
106
|
-
if (i % 8 == 0) {
|
|
107
|
-
const nPart = i - i0
|
|
108
|
-
|
|
109
|
-
res += mask(this.view[Math.trunc(i / 8) - 1], i0 % 8, 8) << (n - nPart)
|
|
110
|
-
|
|
111
|
-
i0 = i
|
|
112
|
-
} else if (i == this.pos + n) {
|
|
113
|
-
res += mask(this.view[Math.trunc(i / 8)], i0 % 8, i % 8)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
this.pos += n
|
|
118
|
-
return res << leftShift
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Reads 8 bits
|
|
123
|
-
* @returns
|
|
124
|
-
*/
|
|
125
|
-
readByte(): number {
|
|
126
|
-
return this.readBits(8)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* BitWriter turns a string of '0's and '1's into a list of bytes.
|
|
132
|
-
* Finalization pads the bits using '0*1' if not yet aligned with the byte boundary.
|
|
133
|
-
*/
|
|
134
|
-
export interface Writer {
|
|
135
|
-
readonly length: number
|
|
136
|
-
finalize(force?: boolean): number[]
|
|
137
|
-
padToByteBoundary(force?: boolean): void
|
|
138
|
-
pop(n: number): string
|
|
139
|
-
writeBits(bitChars: string): Writer
|
|
140
|
-
writeByte(byte: number): Writer
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* @returns
|
|
145
|
-
* Writer instance
|
|
146
|
-
*/
|
|
147
|
-
export function makeWriter() {
|
|
148
|
-
return new WriterImpl()
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
class WriterImpl implements Writer {
|
|
152
|
-
/**
|
|
153
|
-
* Concatenated and padded upon finalization
|
|
154
|
-
*/
|
|
155
|
-
private readonly parts: string[]
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Number of bits written so far
|
|
159
|
-
*/
|
|
160
|
-
private n: number
|
|
161
|
-
|
|
162
|
-
constructor() {
|
|
163
|
-
this.parts = []
|
|
164
|
-
this.n = 0
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
get length(): number {
|
|
168
|
-
return this.n
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Pads the Bits.Writer to align with the byte boundary and returns the resulting bytes.
|
|
173
|
-
* @param force force padding (will add one byte if already aligned)
|
|
174
|
-
* @returns
|
|
175
|
-
*/
|
|
176
|
-
finalize(force: boolean = true): number[] {
|
|
177
|
-
this.padToByteBoundary(force)
|
|
178
|
-
|
|
179
|
-
const chars = this.parts.join("")
|
|
180
|
-
|
|
181
|
-
const bytes = []
|
|
182
|
-
|
|
183
|
-
for (let i = 0; i < chars.length; i += 8) {
|
|
184
|
-
const byteChars = chars.slice(i, i + 8)
|
|
185
|
-
const byte = parseInt(byteChars, 2)
|
|
186
|
-
|
|
187
|
-
bytes.push(byte)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return bytes
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Add padding to the BitWriter in order to align with the byte boundary.
|
|
195
|
-
* @param force
|
|
196
|
-
* If 'force == true' then 8 bits are added if the Writer is already aligned.
|
|
197
|
-
*/
|
|
198
|
-
padToByteBoundary(force: boolean = false): void {
|
|
199
|
-
let nPad = 0
|
|
200
|
-
if (this.n % 8 != 0) {
|
|
201
|
-
nPad = 8 - (this.n % 8)
|
|
202
|
-
} else if (force) {
|
|
203
|
-
nPad = 8
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (nPad != 0) {
|
|
207
|
-
const padding = new Array(nPad).fill("0")
|
|
208
|
-
padding[nPad - 1] = "1"
|
|
209
|
-
|
|
210
|
-
this.parts.push(padding.join(""))
|
|
211
|
-
|
|
212
|
-
this.n += nPad
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Pop n bits of the end
|
|
218
|
-
* @param n
|
|
219
|
-
* @returns
|
|
220
|
-
*/
|
|
221
|
-
pop(n: number): string {
|
|
222
|
-
if (n > this.n) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Too many bits to pop, only have ${this.n} bits, but want n=${n}`
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const n0 = n
|
|
229
|
-
|
|
230
|
-
const parts: string[] = []
|
|
231
|
-
|
|
232
|
-
while (n > 0) {
|
|
233
|
-
const last = this.parts.pop()
|
|
234
|
-
|
|
235
|
-
if (last !== undefined) {
|
|
236
|
-
if (last.length <= n) {
|
|
237
|
-
parts.unshift(last)
|
|
238
|
-
n -= last.length
|
|
239
|
-
} else {
|
|
240
|
-
parts.unshift(last.slice(last.length - n))
|
|
241
|
-
this.parts.push(last.slice(0, last.length - n))
|
|
242
|
-
n = 0
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
this.n -= n0
|
|
248
|
-
|
|
249
|
-
const bits = parts.join("")
|
|
250
|
-
|
|
251
|
-
if (bits.length != n0) {
|
|
252
|
-
throw new Error(
|
|
253
|
-
`Internal error: expected ${n0} bits popped, but popped ${bits.length}`
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return bits
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Write a string of '0's and '1's to the BitWriter.
|
|
262
|
-
* Returns the BitWriter to enable chaining
|
|
263
|
-
* @param bitChars
|
|
264
|
-
* @returns
|
|
265
|
-
* Self so these calls can be chain
|
|
266
|
-
*/
|
|
267
|
-
writeBits(bitChars: string): Writer {
|
|
268
|
-
for (const c of bitChars) {
|
|
269
|
-
if (c != "0" && c != "1") {
|
|
270
|
-
throw new Error(`Bit string contains invalid chars: ${bitChars}`)
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
this.parts.push(bitChars)
|
|
275
|
-
this.n += bitChars.length
|
|
276
|
-
|
|
277
|
-
return this
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Returns the BitWriter to enable chaining
|
|
282
|
-
* @param byte
|
|
283
|
-
* @returns
|
|
284
|
-
* Self so these calls can be chain
|
|
285
|
-
*/
|
|
286
|
-
writeByte(byte: number): Writer {
|
|
287
|
-
if (byte < 0 || byte > 255) {
|
|
288
|
-
throw new Error(`Invalid byte: ${byte}`)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
this.writeBits(pad(byte.toString(2), 8))
|
|
292
|
-
|
|
293
|
-
return this
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Converts a 8 bit integer number into a bit string with an optional "0b" prefix.
|
|
299
|
-
* The result is padded with leading zeroes to become 'n' chars long ('2 + n' chars long if you count the "0b" prefix).
|
|
300
|
-
* @example
|
|
301
|
-
* byteToBits(7) == "0b00000111"
|
|
302
|
-
* @param {number} b
|
|
303
|
-
* @param {number} n
|
|
304
|
-
* @param {boolean} prefix
|
|
305
|
-
* @returns {string}
|
|
306
|
-
*/
|
|
307
|
-
export function fromByte(
|
|
308
|
-
b: number,
|
|
309
|
-
n: number = 8,
|
|
310
|
-
prefix: boolean = true
|
|
311
|
-
): Either.Either<string, RangeError> {
|
|
312
|
-
if (b < 0 || b > 255) {
|
|
313
|
-
return Either.left(new RangeError(`Invalid byte: ${b}`))
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const bits = b.toString(2)
|
|
317
|
-
|
|
318
|
-
if (n < bits.length) {
|
|
319
|
-
return Either.left(
|
|
320
|
-
new RangeError(
|
|
321
|
-
`n is smaller than the number of bits: ${n} < ${bits.length}`
|
|
322
|
-
)
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const s = pad(bits, n)
|
|
327
|
-
|
|
328
|
-
if (prefix) {
|
|
329
|
-
return Either.right("0b" + s)
|
|
330
|
-
} else {
|
|
331
|
-
return Either.right(s)
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* @param bytes
|
|
337
|
-
* @param i
|
|
338
|
-
* bit index
|
|
339
|
-
* @returns
|
|
340
|
-
* 0 or 1
|
|
341
|
-
*/
|
|
342
|
-
export function getBit(bytes: number[], i: number): 0 | 1 {
|
|
343
|
-
return ((bytes[Math.floor(i / 8)] >> (i % 8)) & 1) as 0 | 1
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const BIT_MASKS = [
|
|
347
|
-
0b11111111, 0b01111111, 0b00111111, 0b00011111, 0b00001111, 0b00000111,
|
|
348
|
-
0b00000011, 0b00000001
|
|
349
|
-
]
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Masks bits of `b` by setting bits outside the range `[i0, i1)` to 0.
|
|
353
|
-
* `b` is an 8 bit integer (i.e. number between 0 and 255).
|
|
354
|
-
* The return value is also an 8 bit integer, shifted right by `i1`.
|
|
355
|
-
* @example
|
|
356
|
-
* maskBits(0b11111111, 1, 4) == 0b0111 // (i.e. 7)
|
|
357
|
-
* @param b
|
|
358
|
-
* @param i0
|
|
359
|
-
* @param i1
|
|
360
|
-
* @returns
|
|
361
|
-
*/
|
|
362
|
-
export function mask(b: number, i0: number, i1: number): number {
|
|
363
|
-
if (i0 >= i1 || i0 < 0 || i0 > 7 || i1 > 8 || b < 0 || b > 255) {
|
|
364
|
-
throw new RangeError(
|
|
365
|
-
`Invalid Bits.mask arguments: b=${b}, i0=${i0}, i1=${i1}`
|
|
366
|
-
)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return (b & BIT_MASKS[i0]) >> (8 - i1)
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Prepends zeroes to a bit-string so that 'result.length == n'.
|
|
374
|
-
* If `n < nCurrent`, pad to next multiple of `n`.
|
|
375
|
-
* @example
|
|
376
|
-
* padBits("1111", 8) == "00001111"
|
|
377
|
-
* @param bits
|
|
378
|
-
* @param n
|
|
379
|
-
* @returns
|
|
380
|
-
* @throws
|
|
381
|
-
* If n is zero or negative
|
|
382
|
-
*/
|
|
383
|
-
export function pad(bits: string, n: number): string {
|
|
384
|
-
const nBits = bits.length
|
|
385
|
-
|
|
386
|
-
if (nBits == n) {
|
|
387
|
-
return bits
|
|
388
|
-
} else if (n <= 0) {
|
|
389
|
-
throw new RangeError(`Expected pad length n to be > 0, got n=${n}`)
|
|
390
|
-
} else if (nBits % n != 0) {
|
|
391
|
-
// padded to multiple of n
|
|
392
|
-
const nPad = n - (nBits % n)
|
|
393
|
-
|
|
394
|
-
bits = new Array(nPad).fill("0").join("") + bits
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return bits
|
|
398
|
-
}
|