@helios-lang/effect 0.1.5 → 0.1.6
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/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 +530 -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,219 +0,0 @@
|
|
|
1
|
-
import { Effect, Either } from "effect"
|
|
2
|
-
import { describe, it, expect } from "bun:test"
|
|
3
|
-
import * as Base32 from "./Base32.js"
|
|
4
|
-
import * as Utf8 from "./Utf8.js"
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Some test vectors taken from https://chromium.googlesource.com/chromium/src/+/lkgr/components/base32/base32_unittest.cc
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
describe(`Base32.make()`, () => {
|
|
11
|
-
it("fails for non-32 char alphabet", () => {
|
|
12
|
-
expect(() => Base32.make({ alphabet: "abcdefg" })).toThrow()
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it("fails for non-unique 32 char alphabet", () => {
|
|
16
|
-
expect(() =>
|
|
17
|
-
Base32.make({ alphabet: "aacdefghijklmnopqrstuvwxyz234567" })
|
|
18
|
-
).toThrow()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it("fails for non-single char padding (0 chars)", () => {
|
|
22
|
-
expect(() =>
|
|
23
|
-
Base32.make({
|
|
24
|
-
alphabet: Base32.DEFAULT_ALPHABET,
|
|
25
|
-
padChar: ""
|
|
26
|
-
})
|
|
27
|
-
).toThrow()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it("fails for non-single char padding (more than 1 chars)", () => {
|
|
31
|
-
expect(() =>
|
|
32
|
-
Base32.make({
|
|
33
|
-
alphabet: Base32.DEFAULT_ALPHABET,
|
|
34
|
-
padChar: "=="
|
|
35
|
-
})
|
|
36
|
-
).toThrow()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it("fails if padding char is part of alphabet", () => {
|
|
40
|
-
expect(() =>
|
|
41
|
-
Base32.make({
|
|
42
|
-
alphabet: "abcdefghijklmnopqrstuvwxyz23456=",
|
|
43
|
-
padChar: "="
|
|
44
|
-
})
|
|
45
|
-
).toThrow()
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
describe("Base32.DEFAULT.isValid()", () => {
|
|
50
|
-
it("returns true for an empty string", () => {
|
|
51
|
-
expect(Base32.DEFAULT.isValid("")).toBe(true)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('returns true for "my"', () => {
|
|
55
|
-
expect(Base32.DEFAULT.isValid("my")).toBe(true)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('returns false for "f0" (invalid char)', () => {
|
|
59
|
-
expect(Base32.DEFAULT.isValid("f0")).toBe(false)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('returns false for "fo=" (bad alignment with padding)', () => {
|
|
63
|
-
expect(Base32.DEFAULT.isValid("fo=")).toBe(false)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('returns false for "fo=o====" (interrupted padding)', () => {
|
|
67
|
-
expect(Base32.DEFAULT.isValid("fo=o====")).toBe(false)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('returns false for "foo=====" (invalid padding length)', () => {
|
|
71
|
-
expect(Base32.DEFAULT.isValid("foo=====")).toBe(false)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('returns false for "fooo====" (bad terminating char)', () => {
|
|
75
|
-
expect(Base32.DEFAULT.isValid("fooo====")).toBe(false)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('returns true for "fooa===="', () => {
|
|
79
|
-
expect(Base32.DEFAULT.isValid("fooa====")).toBe(true)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe("Base32.encode() without padding", () => {
|
|
84
|
-
const codec = Base32.make({})
|
|
85
|
-
|
|
86
|
-
it("returns an empty string for []", () => {
|
|
87
|
-
expect(codec.encode([])).toBe("")
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('returns "my" for the utf-8 bytes of "f"', () => {
|
|
91
|
-
expect(codec.encode(Utf8.encode("f"))).toBe("my")
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('returns "mzxq" for the utf-8 bytes of "fo"', () => {
|
|
95
|
-
expect(codec.encode(Utf8.encode("fo"))).toBe("mzxq")
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('returns "mzxw6" for the utf-8 bytes of "foo"', () => {
|
|
99
|
-
expect(codec.encode(Utf8.encode("foo"))).toBe("mzxw6")
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('returns "mzxw6yq" for the utf-8 bytes of "foob"', () => {
|
|
103
|
-
expect(codec.encode(Utf8.encode("foob"))).toBe("mzxw6yq")
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('returns "mzxw6ytb" for the utf-8 bytes of "fooba"', () => {
|
|
107
|
-
expect(codec.encode(Utf8.encode("fooba"))).toBe("mzxw6ytb")
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('returns "mzxw6ytboi" for the utf-8 bytes of "foobar"', () => {
|
|
111
|
-
expect(codec.encode(Utf8.encode("foobar"))).toBe("mzxw6ytboi")
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
describe("Base32.decode()", () => {
|
|
116
|
-
const paddingLessCodec = Base32.make({ alphabet: Base32.DEFAULT_ALPHABET })
|
|
117
|
-
const paddingCodec = Base32.make({
|
|
118
|
-
...Base32.DEFAULT_PROPS,
|
|
119
|
-
strict: true
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it("returns [] for an empty string", () => {
|
|
123
|
-
expect(Base32.DEFAULT.decode("")).toEqual(Either.right(new Uint8Array([])))
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('returns the utf-8 bytes of "f" for "my"', () => {
|
|
127
|
-
expect(paddingLessCodec.decode("my")).toEqual(
|
|
128
|
-
Either.right(Utf8.encode("f"))
|
|
129
|
-
)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('returns the utf-8 bytes of "fo" for "mzxq"', () => {
|
|
133
|
-
expect(Base32.DEFAULT.decode("mzxq")).toEqual(
|
|
134
|
-
Either.right(Utf8.encode("fo"))
|
|
135
|
-
)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('fails for "mzxq" if strict', () => {
|
|
139
|
-
expect(paddingCodec.decode("mzxq")._tag).toBe("Left")
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('returns the utf-8 btyes of "foo" for "mzxw6"', () => {
|
|
143
|
-
expect(Base32.DEFAULT.decode("mzxw6")).toEqual(
|
|
144
|
-
Either.right(Utf8.encode("foo"))
|
|
145
|
-
)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('returns the utf-8 bytes of "foob" for "mzxw6yq"', () => {
|
|
149
|
-
expect(Base32.DEFAULT.decode("mzxw6yq")).toEqual(
|
|
150
|
-
Either.right(Utf8.encode("foob"))
|
|
151
|
-
)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('returns the utf-8 bytes of "fooba" for "mzxw6ytb"', () => {
|
|
155
|
-
expect(Base32.DEFAULT.decode("mzxw6ytb")).toEqual(
|
|
156
|
-
Either.right(Utf8.encode("fooba"))
|
|
157
|
-
)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('returns the utf-8 bytes of "foobar" for "mzxw6ytboi"', () => {
|
|
161
|
-
expect(Base32.DEFAULT.decode("mzxw6ytboi")).toEqual(
|
|
162
|
-
Either.right(Utf8.encode("foobar"))
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('fails for "0" (invalid char)', () => {
|
|
167
|
-
expect(Base32.DEFAULT.decode("0")._tag).toBe("Left")
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('fails for "1" (invalid char)', () => {
|
|
171
|
-
expect(Base32.DEFAULT.decode("1")._tag).toBe("Left")
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('fails for "8" (invalid char)', () => {
|
|
175
|
-
expect(Base32.DEFAULT.decode("8")._tag).toBe("Left")
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('fails for "9" (invalid char)', () => {
|
|
179
|
-
expect(Base32.DEFAULT.decode("9")._tag).toBe("Left")
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('fails for "$" (invalid char)', () => {
|
|
183
|
-
expect(Base32.DEFAULT.decode("$")._tag).toBe("Left")
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('returns the same for "mzxw6ytboi" as for "MZXW6YTBOI" (case insensitive)', () => {
|
|
187
|
-
const s = "mzxw6ytboi"
|
|
188
|
-
|
|
189
|
-
expect(Base32.DEFAULT.decode(s)).toEqual(
|
|
190
|
-
Base32.DEFAULT.decode(s.toUpperCase())
|
|
191
|
-
)
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
describe("Base32.decode()/Base32.encode() roundtrip", () => {
|
|
196
|
-
function roundtrip(encoded: string): string {
|
|
197
|
-
return Base32.DEFAULT.encode(Effect.runSync(Base32.DEFAULT.decode(encoded)))
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
it("fails for foo=====", () => {
|
|
201
|
-
expect(() => roundtrip("foo=====")).toThrow()
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it("fails for foo====", () => {
|
|
205
|
-
expect(() => roundtrip("foo====")).toThrow()
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it("fails for foo=b", () => {
|
|
209
|
-
expect(() => roundtrip("foo=b")).toThrow()
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it("ok for fooa====", () => {
|
|
213
|
-
expect(roundtrip("fooa====")).toBe("fooa====")
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it("fails for for fooo====", () => {
|
|
217
|
-
expect(() => roundtrip("fooo====")).toThrow()
|
|
218
|
-
})
|
|
219
|
-
})
|
package/src/internal/Base32.ts
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
import { Encoding, Either } from "effect"
|
|
2
|
-
import * as Bits from "./Bits.js"
|
|
3
|
-
|
|
4
|
-
export interface Base32 {
|
|
5
|
-
readonly alphabet: string
|
|
6
|
-
readonly padChar: string
|
|
7
|
-
readonly strict: boolean
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param encoded
|
|
11
|
-
* @returns array of bytes
|
|
12
|
-
*/
|
|
13
|
-
decode(encoded: string): Either.Either<Uint8Array, Encoding.DecodeException>
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param encoded
|
|
17
|
-
* @returns array of numbers in range [0,32)
|
|
18
|
-
*/
|
|
19
|
-
decodeRaw(encoded: string): Either.Either<number[], Encoding.DecodeException>
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @param bytes
|
|
23
|
-
* @returns base32 encoded string
|
|
24
|
-
*/
|
|
25
|
-
encode(bytes: string | Uint8Array | number[]): string
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
* @param bytes
|
|
30
|
-
* @returns array of numbers in range [0,32)
|
|
31
|
-
*/
|
|
32
|
-
encodeRaw(bytes: string | Uint8Array | number[]): number[]
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Checks if encoded bytes are valid base32
|
|
36
|
-
* @param encoded
|
|
37
|
-
*/
|
|
38
|
-
isValid(encoded: string): boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type Props =
|
|
42
|
-
| {
|
|
43
|
-
alphabet?: string
|
|
44
|
-
}
|
|
45
|
-
| {
|
|
46
|
-
alphabet?: string
|
|
47
|
-
padChar: string
|
|
48
|
-
strict?: boolean
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export const DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567" as const
|
|
52
|
-
|
|
53
|
-
export const DEFAULT_PAD_CHAR = "=" as const
|
|
54
|
-
|
|
55
|
-
export const DEFAULT_PROPS: Props = {
|
|
56
|
-
alphabet: DEFAULT_ALPHABET,
|
|
57
|
-
padChar: DEFAULT_PAD_CHAR,
|
|
58
|
-
strict: false
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const DEFAULT: Base32 = /* @__PURE__ */ make()
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @param props
|
|
65
|
-
* @returns
|
|
66
|
-
*/
|
|
67
|
-
export function make(props: Props = DEFAULT_PROPS): Base32 {
|
|
68
|
-
const alphabet = props.alphabet ?? DEFAULT_ALPHABET
|
|
69
|
-
const padChar = "padChar" in props ? props.padChar : ""
|
|
70
|
-
const strict = "strict" in props ? (props.strict ?? false) : false
|
|
71
|
-
|
|
72
|
-
if (alphabet.length != 32) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Expected base32 alphabet with 32 characters, got ${alphabet.length} characters`
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (new Set(alphabet.split("")).size != 32) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
"Invalid base32 alphabet, doesn't consist 32 unique characters"
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if ("padChar" in props && padChar.length != 1) {
|
|
85
|
-
throw new Error("Expected single base32 padChar")
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if ("padChar" in props && alphabet.indexOf(padChar) != -1) {
|
|
89
|
-
throw new Error("Base32 padChar can't be part of alphabet")
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return new Base32Impl(alphabet, padChar, strict)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
class Base32Impl implements Base32 {
|
|
96
|
-
readonly alphabet: string
|
|
97
|
-
readonly padChar: string
|
|
98
|
-
readonly strict: boolean
|
|
99
|
-
|
|
100
|
-
constructor(alphabet: string, padChar: string, strict: boolean) {
|
|
101
|
-
this.alphabet = alphabet
|
|
102
|
-
this.padChar = padChar
|
|
103
|
-
this.strict = strict
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Decodes a Base32 string into bytes.
|
|
108
|
-
* @param encoded
|
|
109
|
-
* @returns
|
|
110
|
-
*/
|
|
111
|
-
decode(encoded: string): Either.Either<Uint8Array, Encoding.DecodeException> {
|
|
112
|
-
const writer = Bits.makeWriter()
|
|
113
|
-
|
|
114
|
-
const rawResult = this.decodeRaw(encoded)
|
|
115
|
-
|
|
116
|
-
if (rawResult._tag == "Left") {
|
|
117
|
-
return Either.left(rawResult.left)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const raw = rawResult.right
|
|
121
|
-
|
|
122
|
-
const n = raw.length
|
|
123
|
-
|
|
124
|
-
raw.forEach((code, i) => {
|
|
125
|
-
if (i == n - 1) {
|
|
126
|
-
// last, make sure we align to byte
|
|
127
|
-
|
|
128
|
-
const nCut = n * 5 - 8 * Math.floor((n * 5) / 8)
|
|
129
|
-
|
|
130
|
-
const bits = Bits.pad(code.toString(2), 5)
|
|
131
|
-
|
|
132
|
-
writer.writeBits(bits.slice(0, 5 - nCut))
|
|
133
|
-
} else {
|
|
134
|
-
const bits = Bits.pad(code.toString(2), 5)
|
|
135
|
-
|
|
136
|
-
writer.writeBits(bits)
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
const result = writer.finalize(false)
|
|
141
|
-
|
|
142
|
-
return Either.right(new Uint8Array(result))
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @param encoded
|
|
147
|
-
* @returns array with numbers in range [0,32)
|
|
148
|
-
*/
|
|
149
|
-
decodeRaw(
|
|
150
|
-
encoded: string
|
|
151
|
-
): Either.Either<number[], Encoding.DecodeException> {
|
|
152
|
-
const trimResult = trim(encoded, this.padChar, this.strict)
|
|
153
|
-
|
|
154
|
-
if (trimResult._tag == "Left") {
|
|
155
|
-
return Either.left(trimResult.left)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
encoded = trimResult.right
|
|
159
|
-
|
|
160
|
-
const n = encoded.length
|
|
161
|
-
|
|
162
|
-
const res: number[] = []
|
|
163
|
-
|
|
164
|
-
for (let i = 0; i < n; i++) {
|
|
165
|
-
const c = encoded[i]
|
|
166
|
-
|
|
167
|
-
if (c == this.padChar) {
|
|
168
|
-
// TODO: yield with Effect
|
|
169
|
-
return Either.left(
|
|
170
|
-
Encoding.DecodeException(
|
|
171
|
-
encoded,
|
|
172
|
-
`Unexpected padding character '${c}' at position ${i}`
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const code = this.alphabet.indexOf(c.toLowerCase())
|
|
178
|
-
|
|
179
|
-
if (code < 0) {
|
|
180
|
-
return Either.left(
|
|
181
|
-
Encoding.DecodeException(
|
|
182
|
-
encoded,
|
|
183
|
-
`Invalid base32 character '${c}' at position ${i}`
|
|
184
|
-
)
|
|
185
|
-
)
|
|
186
|
-
} else if (i == n - 1) {
|
|
187
|
-
const nBitsExtra = n * 5 - Math.floor((n * 5) / 8) * 8
|
|
188
|
-
|
|
189
|
-
if ((((1 << nBitsExtra) - 1) & code) != 0) {
|
|
190
|
-
return Either.left(
|
|
191
|
-
Encoding.DecodeException(
|
|
192
|
-
encoded,
|
|
193
|
-
`Invalid base32 final character '${c}'`
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
res.push(code)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return Either.right(res)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Encodes bytes in using Base32.
|
|
207
|
-
* @param bytes hex encoded or list of uint8 numbers
|
|
208
|
-
* @returns
|
|
209
|
-
*/
|
|
210
|
-
encode(bytes: string | number[] | Uint8Array): string {
|
|
211
|
-
const s = this.encodeRaw(bytes)
|
|
212
|
-
.map((c) => this.alphabet[c])
|
|
213
|
-
.join("")
|
|
214
|
-
|
|
215
|
-
const n = s.length
|
|
216
|
-
|
|
217
|
-
if (n % 8 != 0 && this.padChar.length != 0) {
|
|
218
|
-
return s + new Array(8 - (n % 8)).fill(this.padChar).join("")
|
|
219
|
-
} else {
|
|
220
|
-
return s
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @param bytes
|
|
226
|
-
* @returns {number[]} list of numbers between 0 and 32
|
|
227
|
-
*/
|
|
228
|
-
encodeRaw(bytes: string | number[] | Uint8Array): number[] {
|
|
229
|
-
const result: number[] = []
|
|
230
|
-
|
|
231
|
-
const reader = Bits.makeReader(bytes, false)
|
|
232
|
-
|
|
233
|
-
while (!reader.isAtEnd()) {
|
|
234
|
-
result.push(reader.readBits(5))
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return result
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Checks if all the characters in `encoded` are in the given base32 alphabet.
|
|
242
|
-
* Checks lengths if their pad characters at the end
|
|
243
|
-
* @param encoded
|
|
244
|
-
* @returns
|
|
245
|
-
*/
|
|
246
|
-
isValid(encoded: string): boolean {
|
|
247
|
-
let n = encoded.length
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
this.padChar.length == 1 &&
|
|
251
|
-
(this.strict || encoded.endsWith(this.padChar))
|
|
252
|
-
) {
|
|
253
|
-
if (encoded.length % 8 != 0) {
|
|
254
|
-
return false
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const iPad = encoded.indexOf(this.padChar)
|
|
258
|
-
|
|
259
|
-
for (let i = iPad + 1; i < n; i++) {
|
|
260
|
-
if (encoded.at(i) != this.padChar) {
|
|
261
|
-
return false
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const nPad = n - iPad
|
|
266
|
-
|
|
267
|
-
if (nPad != 6 && nPad != 4 && nPad != 3 && nPad != 1) {
|
|
268
|
-
return false
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
encoded = encoded.slice(0, iPad)
|
|
272
|
-
|
|
273
|
-
n = iPad
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// the last char can't be any possible number
|
|
277
|
-
|
|
278
|
-
return encoded.split("").every((c, i) => {
|
|
279
|
-
const code = this.alphabet.indexOf(c.toLowerCase())
|
|
280
|
-
|
|
281
|
-
if (code < 0) {
|
|
282
|
-
return false
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (i == n - 1) {
|
|
286
|
-
const nBitsExtra = n * 5 - Math.floor((n * 5) / 8) * 8
|
|
287
|
-
|
|
288
|
-
return (((1 << nBitsExtra) - 1) & code) == 0
|
|
289
|
-
} else {
|
|
290
|
-
return true
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Trims the padding, asserting it is correctly formed
|
|
298
|
-
* @param encoded
|
|
299
|
-
* @param padChar
|
|
300
|
-
* @returns
|
|
301
|
-
*/
|
|
302
|
-
function trim(
|
|
303
|
-
encoded: string,
|
|
304
|
-
padChar: string,
|
|
305
|
-
strict: boolean
|
|
306
|
-
): Either.Either<string, Encoding.DecodeException> {
|
|
307
|
-
if (padChar.length == 1) {
|
|
308
|
-
let n = encoded.length
|
|
309
|
-
|
|
310
|
-
while (n >= 0 && encoded.at(n - 1) == padChar) {
|
|
311
|
-
n -= 1
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// length alignment is only checked if there are some padding characters at the end
|
|
315
|
-
if ((strict || n < encoded.length) && encoded.length % 8 != 0) {
|
|
316
|
-
return Either.left(
|
|
317
|
-
Encoding.DecodeException(
|
|
318
|
-
encoded,
|
|
319
|
-
"Invalid length (expected multiple of 8)"
|
|
320
|
-
)
|
|
321
|
-
)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const nPad = encoded.length - n
|
|
325
|
-
|
|
326
|
-
if (nPad != 0) {
|
|
327
|
-
if (nPad != 6 && nPad != 4 && nPad != 3 && nPad != 1) {
|
|
328
|
-
return Either.left(
|
|
329
|
-
Encoding.DecodeException(
|
|
330
|
-
encoded,
|
|
331
|
-
"Invalid number of base32 padding characters"
|
|
332
|
-
)
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return Either.right(encoded.slice(0, n))
|
|
338
|
-
} else {
|
|
339
|
-
return Either.right(encoded)
|
|
340
|
-
}
|
|
341
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test"
|
|
2
|
-
import { Either } from "effect"
|
|
3
|
-
import * as BigEndian from "./BigEndian.js"
|
|
4
|
-
import { toHex } from "./Bytes.js"
|
|
5
|
-
|
|
6
|
-
describe("BigEndian.decode", () => {
|
|
7
|
-
it("returns 255n for [255]", () => {
|
|
8
|
-
expect(BigEndian.decode([255])).toEqual(Either.right(255n))
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it("returns 255n for [0, 0, 0, 255]", () => {
|
|
12
|
-
expect(BigEndian.decode([0, 0, 0, 255])).toEqual(Either.right(255n))
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it("fails for [256] (invalid byte)", () => {
|
|
16
|
-
expect(BigEndian.decode([256])._tag).toBe("Left")
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it("fails for [3.14] (invalid byte)", () => {
|
|
20
|
-
expect(BigEndian.decode([3.14])._tag).toBe("Left")
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it("fails for [-1] (invalid byte)", () => {
|
|
24
|
-
expect(BigEndian.decode([-1])._tag).toBe("Left")
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it("fails for empty bytes", () => {
|
|
28
|
-
expect(BigEndian.decode([])._tag).toBe("Left")
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
describe("BigEndian.decode compared to alt formula", () => {
|
|
32
|
-
const alt = (bytes: number[]): bigint => {
|
|
33
|
-
let sum = 0n
|
|
34
|
-
bytes = bytes.slice().reverse()
|
|
35
|
-
bytes.forEach((b, i) => {
|
|
36
|
-
sum += BigInt(b) * (1n << BigInt(i * 8))
|
|
37
|
-
})
|
|
38
|
-
return sum
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const testVector: number[][] = [
|
|
42
|
-
[0x00],
|
|
43
|
-
[0x80],
|
|
44
|
-
[0xff],
|
|
45
|
-
[0xff, 0xff],
|
|
46
|
-
[0xfe, 0xfe],
|
|
47
|
-
[0x80, 0x80],
|
|
48
|
-
[0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89]
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
testVector.forEach((t) => {
|
|
52
|
-
it(`ok for #${toHex(t)}`, () => {
|
|
53
|
-
expect(BigEndian.decode(t)).toEqual(Either.right(alt(t)))
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
describe("BigEndian.encode", () => {
|
|
60
|
-
it("returns [1, 0] for 256", () => {
|
|
61
|
-
expect(BigEndian.encode(256)).toEqual([1, 0])
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it("returns [0] for 0", () => {
|
|
65
|
-
expect(BigEndian.encode(0)).toEqual([0])
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it("returns [0] for 0n", () => {
|
|
69
|
-
expect(BigEndian.encode(0n)).toEqual([0])
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it("fails for a non-whole number", () => {
|
|
73
|
-
expect(() => BigEndian.encode(0.5)).toThrow()
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it("fails for a negative number", () => {
|
|
77
|
-
expect(() => BigEndian.encode(-1n)).toThrow()
|
|
78
|
-
})
|
|
79
|
-
})
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Either, Encoding } from "effect"
|
|
2
|
-
import { DecodeException } from "./Bytes.js"
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Converts a list of big endian uint8 numbers into an unbounded int
|
|
6
|
-
* @param bytes
|
|
7
|
-
* @returns
|
|
8
|
-
* @throws
|
|
9
|
-
* If `bytes` is empty
|
|
10
|
-
* @throws
|
|
11
|
-
* If any input number is out of range [0,256) or not a whole number
|
|
12
|
-
*/
|
|
13
|
-
export function decode(
|
|
14
|
-
bytes: number[] | Uint8Array
|
|
15
|
-
): Either.Either<bigint, Encoding.DecodeException> {
|
|
16
|
-
if (bytes.length == 0) {
|
|
17
|
-
return Either.left(DecodeException(bytes, "Empty bytes"))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let p = 1n
|
|
21
|
-
let total = 0n
|
|
22
|
-
|
|
23
|
-
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
24
|
-
const b = bytes[i]
|
|
25
|
-
|
|
26
|
-
if (b < 0 || b > 255 || b % 1.0 != 0.0) {
|
|
27
|
-
return Either.left(
|
|
28
|
-
DecodeException(bytes, `Invalide bytes '${b}' at position ${i}`)
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
total += BigInt(b) * p
|
|
33
|
-
|
|
34
|
-
p *= 256n
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return Either.right(total)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Converts an unbounded integer into a list of big endian uint8 numbers.
|
|
42
|
-
* @param x
|
|
43
|
-
* @returns
|
|
44
|
-
* @throws
|
|
45
|
-
* If `x` isn't a whole number
|
|
46
|
-
* @throws
|
|
47
|
-
* If `x` is negative.
|
|
48
|
-
*/
|
|
49
|
-
export function encode(x: number | bigint): number[] {
|
|
50
|
-
if (typeof x == "number") {
|
|
51
|
-
return encode(BigInt(x))
|
|
52
|
-
} else if (x < 0n) {
|
|
53
|
-
throw new RangeError(`Unexpected negative number: ${x}`)
|
|
54
|
-
} else if (x == 0n) {
|
|
55
|
-
return [0]
|
|
56
|
-
} else {
|
|
57
|
-
const res: number[] = []
|
|
58
|
-
|
|
59
|
-
while (x > 0n) {
|
|
60
|
-
res.unshift(Number(x % 256n))
|
|
61
|
-
|
|
62
|
-
x = x / 256n
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return res
|
|
66
|
-
}
|
|
67
|
-
}
|