@bcts/uniform-resources 1.0.0-alpha.10
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/LICENSE +48 -0
- package/README.md +17 -0
- package/dist/index.cjs +8373 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +761 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +761 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +8377 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +8336 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +74 -0
- package/src/error.ts +88 -0
- package/src/fountain.ts +397 -0
- package/src/index.ts +61 -0
- package/src/multipart-decoder.ts +204 -0
- package/src/multipart-encoder.ts +166 -0
- package/src/ur-codable.ts +48 -0
- package/src/ur-decodable.ts +56 -0
- package/src/ur-encodable.ts +47 -0
- package/src/ur-type.ts +87 -0
- package/src/ur.ts +215 -0
- package/src/utils.ts +802 -0
- package/src/xoshiro.ts +180 -0
package/src/xoshiro.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Xoshiro256** PRNG implementation.
|
|
3
|
+
*
|
|
4
|
+
* This is a high-quality, fast pseudo-random number generator used
|
|
5
|
+
* for deterministic fragment selection in fountain codes.
|
|
6
|
+
*
|
|
7
|
+
* Reference: https://prng.di.unimi.it/
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const MAX_UINT64 = BigInt("0xffffffffffffffff");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Performs a left rotation on a 64-bit BigInt.
|
|
14
|
+
*/
|
|
15
|
+
function rotl(x: bigint, k: number): bigint {
|
|
16
|
+
const kBigInt = BigInt(k);
|
|
17
|
+
return ((x << kBigInt) | (x >> (64n - kBigInt))) & MAX_UINT64;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Xoshiro256** pseudo-random number generator.
|
|
22
|
+
*
|
|
23
|
+
* This PRNG is used for deterministic mixing in fountain codes,
|
|
24
|
+
* allowing both encoder and decoder to agree on which fragments
|
|
25
|
+
* are combined without transmitting that information.
|
|
26
|
+
*/
|
|
27
|
+
export class Xoshiro256 {
|
|
28
|
+
private s: [bigint, bigint, bigint, bigint];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new Xoshiro256** instance from a seed.
|
|
32
|
+
*
|
|
33
|
+
* The seed is hashed using SHA-256 to initialize the state.
|
|
34
|
+
* For consistent results across encoder/decoder, use the same seed.
|
|
35
|
+
*
|
|
36
|
+
* @param seed - The seed bytes (any length)
|
|
37
|
+
*/
|
|
38
|
+
constructor(seed: Uint8Array) {
|
|
39
|
+
// Hash the seed using a simple hash function
|
|
40
|
+
// In production, you'd use SHA-256 here
|
|
41
|
+
const hash = this.hashSeed(seed);
|
|
42
|
+
|
|
43
|
+
// Initialize the 4x64-bit state from the hash
|
|
44
|
+
this.s = [
|
|
45
|
+
this.bytesToBigInt(hash.slice(0, 8)),
|
|
46
|
+
this.bytesToBigInt(hash.slice(8, 16)),
|
|
47
|
+
this.bytesToBigInt(hash.slice(16, 24)),
|
|
48
|
+
this.bytesToBigInt(hash.slice(24, 32)),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a Xoshiro256** instance from raw state values.
|
|
54
|
+
* Useful for seeding with specific values.
|
|
55
|
+
*/
|
|
56
|
+
static fromState(s0: bigint, s1: bigint, s2: bigint, s3: bigint): Xoshiro256 {
|
|
57
|
+
const instance = Object.create(Xoshiro256.prototype) as Xoshiro256;
|
|
58
|
+
instance.s = [s0, s1, s2, s3];
|
|
59
|
+
return instance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Simple hash function for seeding.
|
|
64
|
+
* This is a basic implementation - in production use SHA-256.
|
|
65
|
+
*/
|
|
66
|
+
private hashSeed(seed: Uint8Array): Uint8Array {
|
|
67
|
+
// Simple hash expansion using CRC32-like operations
|
|
68
|
+
const result = new Uint8Array(32);
|
|
69
|
+
|
|
70
|
+
if (seed.length === 0) {
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Expand seed to 32 bytes using a simple mixing function
|
|
75
|
+
for (let i = 0; i < 32; i++) {
|
|
76
|
+
let hash = 0;
|
|
77
|
+
for (const byte of seed) {
|
|
78
|
+
hash = (hash * 31 + byte + i) >>> 0;
|
|
79
|
+
}
|
|
80
|
+
// Mix the hash further
|
|
81
|
+
hash ^= hash >>> 16;
|
|
82
|
+
hash = (hash * 0x85ebca6b) >>> 0;
|
|
83
|
+
hash ^= hash >>> 13;
|
|
84
|
+
hash = (hash * 0xc2b2ae35) >>> 0;
|
|
85
|
+
hash ^= hash >>> 16;
|
|
86
|
+
result[i] = hash & 0xff;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Converts 8 bytes to a 64-bit BigInt (little-endian).
|
|
94
|
+
*/
|
|
95
|
+
private bytesToBigInt(bytes: Uint8Array): bigint {
|
|
96
|
+
let result = 0n;
|
|
97
|
+
for (let i = 7; i >= 0; i--) {
|
|
98
|
+
result = (result << 8n) | BigInt(bytes[i] ?? 0);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generates the next 64-bit random value.
|
|
105
|
+
*/
|
|
106
|
+
next(): bigint {
|
|
107
|
+
const result = (rotl((this.s[1] * 5n) & MAX_UINT64, 7) * 9n) & MAX_UINT64;
|
|
108
|
+
|
|
109
|
+
const t = (this.s[1] << 17n) & MAX_UINT64;
|
|
110
|
+
|
|
111
|
+
this.s[2] ^= this.s[0];
|
|
112
|
+
this.s[3] ^= this.s[1];
|
|
113
|
+
this.s[1] ^= this.s[2];
|
|
114
|
+
this.s[0] ^= this.s[3];
|
|
115
|
+
|
|
116
|
+
this.s[2] ^= t;
|
|
117
|
+
this.s[3] = rotl(this.s[3], 45);
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Generates a random double in [0, 1).
|
|
124
|
+
*/
|
|
125
|
+
nextDouble(): number {
|
|
126
|
+
// Use the upper 53 bits for double precision
|
|
127
|
+
const value = this.next();
|
|
128
|
+
return Number(value >> 11n) / Number(1n << 53n);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generates a random integer in [low, high).
|
|
133
|
+
*/
|
|
134
|
+
nextInt(low: number, high: number): number {
|
|
135
|
+
const range = high - low;
|
|
136
|
+
return low + Math.floor(this.nextDouble() * range);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generates a random byte [0, 255].
|
|
141
|
+
*/
|
|
142
|
+
nextByte(): number {
|
|
143
|
+
return Number(this.next() & 0xffn);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generates an array of random bytes.
|
|
148
|
+
*/
|
|
149
|
+
nextData(count: number): Uint8Array {
|
|
150
|
+
const result = new Uint8Array(count);
|
|
151
|
+
for (let i = 0; i < count; i++) {
|
|
152
|
+
result[i] = this.nextByte();
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Creates a seed for the Xoshiro PRNG from message checksum and sequence number.
|
|
160
|
+
*
|
|
161
|
+
* This ensures that both encoder and decoder produce the same random sequence
|
|
162
|
+
* for a given message and part number.
|
|
163
|
+
*/
|
|
164
|
+
export function createSeed(checksum: number, seqNum: number): Uint8Array {
|
|
165
|
+
const seed = new Uint8Array(8);
|
|
166
|
+
|
|
167
|
+
// Pack checksum (4 bytes, big-endian)
|
|
168
|
+
seed[0] = (checksum >>> 24) & 0xff;
|
|
169
|
+
seed[1] = (checksum >>> 16) & 0xff;
|
|
170
|
+
seed[2] = (checksum >>> 8) & 0xff;
|
|
171
|
+
seed[3] = checksum & 0xff;
|
|
172
|
+
|
|
173
|
+
// Pack seqNum (4 bytes, big-endian)
|
|
174
|
+
seed[4] = (seqNum >>> 24) & 0xff;
|
|
175
|
+
seed[5] = (seqNum >>> 16) & 0xff;
|
|
176
|
+
seed[6] = (seqNum >>> 8) & 0xff;
|
|
177
|
+
seed[7] = seqNum & 0xff;
|
|
178
|
+
|
|
179
|
+
return seed;
|
|
180
|
+
}
|