@threadedstack/wmd-web 0.0.0-stage → 0.1.13
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/README.md +2601 -2
- package/RELEASE.json +123 -0
- package/export-page/coi-sw.js +20 -0
- package/export-page/index.html +17 -0
- package/export-page/run.css +10 -0
- package/export-page/run.js +236 -0
- package/guest/arm64/Image +4 -0
- package/guest/arm64/Image-4k +4 -0
- package/guest/arm64/initramfs.cpio.gz +0 -0
- package/guest/arm64/vm.wasm +0 -0
- package/package.json +36 -4
- package/page/sw.js +381 -0
- package/relay/control.ts +139 -0
- package/relay/engine.ts +787 -0
- package/relay/hosted.ts +660 -0
- package/relay/keys.ts +53 -0
- package/relay/local-tls.ts +121 -0
- package/relay/lock.ts +81 -0
- package/relay/logfile.ts +138 -0
- package/relay/main.ts +642 -0
- package/relay/policy.ts +241 -0
- package/relay/relay.ts +266 -0
- package/relay/token.ts +116 -0
- package/relay/v1.ts +107 -0
- package/relay/wisp.ts +615 -0
- package/src/base.js +325 -0
- package/src/boot.js +2037 -0
- package/src/calib-worker.js +40 -0
- package/src/fs/9p/codec.js +303 -0
- package/src/fs/9p/server.js +521 -0
- package/src/fs/errno.js +43 -0
- package/src/fs/graph.js +380 -0
- package/src/fs/overlay.js +1018 -0
- package/src/fs/pack-write.js +165 -0
- package/src/fs/pack.js +427 -0
- package/src/fs/seed.js +22 -0
- package/src/fs/svc-snapshot.js +65 -0
- package/src/fs/upper-pack.js +112 -0
- package/src/host/base-cache.js +195 -0
- package/src/host/base-file.js +328 -0
- package/src/host/base-io.js +93 -0
- package/src/host/boot-events.js +209 -0
- package/src/host/chan-service.js +300 -0
- package/src/host/chan-services.js +689 -0
- package/src/host/chan.js +655 -0
- package/src/host/ckpt-file.js +395 -0
- package/src/host/ckpt-io.js +207 -0
- package/src/host/ckpt-point.js +160 -0
- package/src/host/ckpt-save.js +64 -0
- package/src/host/ckpt.js +434 -0
- package/src/host/clock.js +111 -0
- package/src/host/ctl.js +778 -0
- package/src/host/errno.js +18 -0
- package/src/host/fs.js +167 -0
- package/src/host/gcnudge.js +83 -0
- package/src/host/i64shim.js +81 -0
- package/src/host/jit-cache.js +341 -0
- package/src/host/jit-hub-core.js +208 -0
- package/src/host/jit-hub.js +93 -0
- package/src/host/jit-snap.js +226 -0
- package/src/host/jit.js +1903 -0
- package/src/host/layout.js +100 -0
- package/src/host/monitor.js +110 -0
- package/src/host/p9mount.js +27 -0
- package/src/host/ram-mem.js +125 -0
- package/src/host/stdio.js +143 -0
- package/src/host/threads.js +109 -0
- package/src/host/wasi.js +335 -0
- package/src/host/wmd-imports.js +71 -0
- package/src/host/yamux.js +387 -0
- package/src/image-files.js +154 -0
- package/src/net/dhcp-dns.js +126 -0
- package/src/net/net-worker.js +172 -0
- package/src/net/packet.js +227 -0
- package/src/net/peer.js +230 -0
- package/src/net/proto.js +189 -0
- package/src/net/relay-client.js +226 -0
- package/src/net/stack.js +867 -0
- package/src/net/switch.js +160 -0
- package/src/net/wisp-client.js +570 -0
- package/src/net/wisp-transport.js +196 -0
- package/src/net/wisp.js +511 -0
- package/src/registry/client.js +176 -0
- package/src/registry/converter.js +68 -0
- package/src/registry/hub.js +221 -0
- package/src/registry/oci.js +76 -0
- package/src/registry/pack-worker.js +70 -0
- package/src/registry/pull.js +258 -0
- package/src/registry/ref.js +110 -0
- package/src/registry/wasi-mem.js +355 -0
- package/src/worker.js +315 -0
- package/wmdpack.wasm +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Throttle detector (boot.js, host/monitor.js createThrottleMeter). Every intervalMs
|
|
2
|
+
// this Worker times a fixed amount of integer work and a 10 ms Atomics.wait, and
|
|
3
|
+
// posts {t:'calib', workMs, waitLateMs}. When the browser deprioritises the page's
|
|
4
|
+
// process (Chromium lowers the priority of a minimised or occluded window's renderer),
|
|
5
|
+
// every Worker of the page, the guest's vCPUs included, runs slower; the page compares
|
|
6
|
+
// workMs with the fastest run it has seen. The work is ~1% of one core.
|
|
7
|
+
|
|
8
|
+
const WORK = 5_000_000
|
|
9
|
+
const cell = new Int32Array(new SharedArrayBuffer(4))
|
|
10
|
+
let sink = 0
|
|
11
|
+
|
|
12
|
+
function work() {
|
|
13
|
+
let x = 0x12345678 ^ sink
|
|
14
|
+
for (let i = 0; i < WORK; i++) {
|
|
15
|
+
x ^= x << 13
|
|
16
|
+
x ^= x >>> 17
|
|
17
|
+
x ^= x << 5
|
|
18
|
+
}
|
|
19
|
+
sink = x
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function measure() {
|
|
23
|
+
const t0 = performance.now()
|
|
24
|
+
work()
|
|
25
|
+
const t1 = performance.now()
|
|
26
|
+
Atomics.wait(cell, 0, 0, 10)
|
|
27
|
+
const t2 = performance.now()
|
|
28
|
+
self.postMessage({ t: 'calib', workMs: t1 - t0, waitLateMs: Math.max(0, t2 - t1 - 10) })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
self.onmessage = e => {
|
|
32
|
+
for (let i = 0; i < 3; i++) work() // warm up the JIT so the first samples are not the slowest
|
|
33
|
+
if (e.data.t === 'once') {
|
|
34
|
+
for (let i = 0; i < e.data.n; i++) measure()
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if (e.data.t !== 'start') return
|
|
38
|
+
measure()
|
|
39
|
+
setInterval(measure, e.data.intervalMs)
|
|
40
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// 9P2000.L message codec: every T and R message the Linux v9fs client and a
|
|
2
|
+
// 9P2000.L server exchange, both directions (a server decodes T and encodes R;
|
|
3
|
+
// tests and clients do the opposite).
|
|
4
|
+
//
|
|
5
|
+
// Frame: size[4] (little endian, including itself) type[1] tag[2] fields.
|
|
6
|
+
// Strings are u16 length + bytes, held as byte strings (one char per byte,
|
|
7
|
+
// see ../pack.js). u64 fields decode to a Number when they fit in 53 bits,
|
|
8
|
+
// else a BigInt, and encode from either.
|
|
9
|
+
|
|
10
|
+
export const NOTAG = 0xffff
|
|
11
|
+
export const NOFID = 0xffffffff
|
|
12
|
+
export const HEADER = 7
|
|
13
|
+
/** Rread/Rwrite/Treaddir overhead: header + count[4] (P9_IOHDRSZ is 24 for Twrite). */
|
|
14
|
+
export const IOHDRSZ = 24
|
|
15
|
+
|
|
16
|
+
// Field kinds.
|
|
17
|
+
const U8 = 'u8', U16 = 'u16', U32 = 'u32', U64 = 'u64', S = 's', QID = 'qid'
|
|
18
|
+
const DATA = 'data', WNAMES = 'wnames', QIDS = 'qids'
|
|
19
|
+
|
|
20
|
+
/** type number -> [name, [[field, kind], ...]] */
|
|
21
|
+
export const MESSAGES = {
|
|
22
|
+
6: ['Tlerror', []],
|
|
23
|
+
7: ['Rlerror', [['ecode', U32]]],
|
|
24
|
+
8: ['Tstatfs', [['fid', U32]]],
|
|
25
|
+
9: ['Rstatfs', [['fstype', U32], ['bsize', U32], ['blocks', U64], ['bfree', U64], ['bavail', U64],
|
|
26
|
+
['files', U64], ['ffree', U64], ['fsid', U64], ['namelen', U32]]],
|
|
27
|
+
12: ['Tlopen', [['fid', U32], ['flags', U32]]],
|
|
28
|
+
13: ['Rlopen', [['qid', QID], ['iounit', U32]]],
|
|
29
|
+
14: ['Tlcreate', [['fid', U32], ['name', S], ['flags', U32], ['mode', U32], ['gid', U32]]],
|
|
30
|
+
15: ['Rlcreate', [['qid', QID], ['iounit', U32]]],
|
|
31
|
+
16: ['Tsymlink', [['fid', U32], ['name', S], ['symtgt', S], ['gid', U32]]],
|
|
32
|
+
17: ['Rsymlink', [['qid', QID]]],
|
|
33
|
+
18: ['Tmknod', [['dfid', U32], ['name', S], ['mode', U32], ['major', U32], ['minor', U32], ['gid', U32]]],
|
|
34
|
+
19: ['Rmknod', [['qid', QID]]],
|
|
35
|
+
20: ['Trename', [['fid', U32], ['dfid', U32], ['name', S]]],
|
|
36
|
+
21: ['Rrename', []],
|
|
37
|
+
22: ['Treadlink', [['fid', U32]]],
|
|
38
|
+
23: ['Rreadlink', [['target', S]]],
|
|
39
|
+
24: ['Tgetattr', [['fid', U32], ['mask', U64]]],
|
|
40
|
+
25: ['Rgetattr', [['valid', U64], ['qid', QID], ['mode', U32], ['uid', U32], ['gid', U32],
|
|
41
|
+
['nlink', U64], ['rdev', U64], ['size', U64], ['blksize', U64], ['blocks', U64],
|
|
42
|
+
['atime_sec', U64], ['atime_nsec', U64], ['mtime_sec', U64], ['mtime_nsec', U64],
|
|
43
|
+
['ctime_sec', U64], ['ctime_nsec', U64], ['btime_sec', U64], ['btime_nsec', U64],
|
|
44
|
+
['gen', U64], ['data_version', U64]]],
|
|
45
|
+
26: ['Tsetattr', [['fid', U32], ['valid', U32], ['mode', U32], ['uid', U32], ['gid', U32], ['size', U64],
|
|
46
|
+
['atime_sec', U64], ['atime_nsec', U64], ['mtime_sec', U64], ['mtime_nsec', U64]]],
|
|
47
|
+
27: ['Rsetattr', []],
|
|
48
|
+
30: ['Txattrwalk', [['fid', U32], ['newfid', U32], ['name', S]]],
|
|
49
|
+
31: ['Rxattrwalk', [['size', U64]]],
|
|
50
|
+
32: ['Txattrcreate', [['fid', U32], ['name', S], ['size', U64], ['flags', U32]]],
|
|
51
|
+
33: ['Rxattrcreate', []],
|
|
52
|
+
40: ['Treaddir', [['fid', U32], ['offset', U64], ['count', U32]]],
|
|
53
|
+
41: ['Rreaddir', [['data', DATA]]],
|
|
54
|
+
50: ['Tfsync', [['fid', U32], ['datasync', U32]]],
|
|
55
|
+
51: ['Rfsync', []],
|
|
56
|
+
52: ['Tlock', [['fid', U32], ['ltype', U8], ['flags', U32], ['start', U64], ['length', U64],
|
|
57
|
+
['proc_id', U32], ['client_id', S]]],
|
|
58
|
+
53: ['Rlock', [['status', U8]]],
|
|
59
|
+
54: ['Tgetlock', [['fid', U32], ['ltype', U8], ['start', U64], ['length', U64], ['proc_id', U32], ['client_id', S]]],
|
|
60
|
+
55: ['Rgetlock', [['ltype', U8], ['start', U64], ['length', U64], ['proc_id', U32], ['client_id', S]]],
|
|
61
|
+
70: ['Tlink', [['dfid', U32], ['fid', U32], ['name', S]]],
|
|
62
|
+
71: ['Rlink', []],
|
|
63
|
+
72: ['Tmkdir', [['dfid', U32], ['name', S], ['mode', U32], ['gid', U32]]],
|
|
64
|
+
73: ['Rmkdir', [['qid', QID]]],
|
|
65
|
+
74: ['Trenameat', [['olddirfid', U32], ['oldname', S], ['newdirfid', U32], ['newname', S]]],
|
|
66
|
+
75: ['Rrenameat', []],
|
|
67
|
+
76: ['Tunlinkat', [['dirfid', U32], ['name', S], ['flags', U32]]],
|
|
68
|
+
77: ['Runlinkat', []],
|
|
69
|
+
100: ['Tversion', [['msize', U32], ['version', S]]],
|
|
70
|
+
101: ['Rversion', [['msize', U32], ['version', S]]],
|
|
71
|
+
102: ['Tauth', [['afid', U32], ['uname', S], ['aname', S], ['n_uname', U32]]],
|
|
72
|
+
103: ['Rauth', [['aqid', QID]]],
|
|
73
|
+
104: ['Tattach', [['fid', U32], ['afid', U32], ['uname', S], ['aname', S], ['n_uname', U32]]],
|
|
74
|
+
105: ['Rattach', [['qid', QID]]],
|
|
75
|
+
108: ['Tflush', [['oldtag', U16]]],
|
|
76
|
+
109: ['Rflush', []],
|
|
77
|
+
110: ['Twalk', [['fid', U32], ['newfid', U32], ['wnames', WNAMES]]],
|
|
78
|
+
111: ['Rwalk', [['qids', QIDS]]],
|
|
79
|
+
116: ['Tread', [['fid', U32], ['offset', U64], ['count', U32]]],
|
|
80
|
+
117: ['Rread', [['data', DATA]]],
|
|
81
|
+
118: ['Twrite', [['fid', U32], ['offset', U64], ['data', DATA]]],
|
|
82
|
+
119: ['Rwrite', [['count', U32]]],
|
|
83
|
+
120: ['Tclunk', [['fid', U32]]],
|
|
84
|
+
121: ['Rclunk', []],
|
|
85
|
+
122: ['Tremove', [['fid', U32]]],
|
|
86
|
+
123: ['Rremove', []],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** name -> type number */
|
|
90
|
+
export const T = {}
|
|
91
|
+
for (const [num, [name]] of Object.entries(MESSAGES)) T[name] = Number(num)
|
|
92
|
+
|
|
93
|
+
export class ProtoError extends Error {}
|
|
94
|
+
|
|
95
|
+
const TWO32 = 2 ** 32
|
|
96
|
+
const MAX_SAFE = Number.MAX_SAFE_INTEGER
|
|
97
|
+
|
|
98
|
+
function strBytesLen(s) {
|
|
99
|
+
return s.length
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function sizeOf(kind, v) {
|
|
103
|
+
switch (kind) {
|
|
104
|
+
case U8: return 1
|
|
105
|
+
case U16: return 2
|
|
106
|
+
case U32: return 4
|
|
107
|
+
case U64: return 8
|
|
108
|
+
case S: return 2 + strBytesLen(v)
|
|
109
|
+
case QID: return 13
|
|
110
|
+
case DATA: return 4 + v.length
|
|
111
|
+
case WNAMES: { let n = 2; for (const s of v) n += 2 + s.length; return n }
|
|
112
|
+
case QIDS: return 2 + 13 * v.length
|
|
113
|
+
}
|
|
114
|
+
throw new ProtoError(`kind ${kind}`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Writes into a DataView at a moving offset. */
|
|
118
|
+
export class Writer {
|
|
119
|
+
constructor(u8) {
|
|
120
|
+
this.u8 = u8
|
|
121
|
+
this.dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength)
|
|
122
|
+
this.p = 0
|
|
123
|
+
}
|
|
124
|
+
u8_(v) { this.dv.setUint8(this.p, v); this.p += 1 }
|
|
125
|
+
u16(v) { this.dv.setUint16(this.p, v, true); this.p += 2 }
|
|
126
|
+
u32(v) { this.dv.setUint32(this.p, v >>> 0, true); this.p += 4 }
|
|
127
|
+
u64(v) {
|
|
128
|
+
if (typeof v === 'number' && v >= 0 && v <= MAX_SAFE && Number.isInteger(v)) {
|
|
129
|
+
this.dv.setUint32(this.p, v % TWO32, true)
|
|
130
|
+
this.dv.setUint32(this.p + 4, Math.floor(v / TWO32), true)
|
|
131
|
+
} else {
|
|
132
|
+
this.dv.setBigUint64(this.p, BigInt.asUintN(64, BigInt(v)), true)
|
|
133
|
+
}
|
|
134
|
+
this.p += 8
|
|
135
|
+
}
|
|
136
|
+
str(s) {
|
|
137
|
+
if (s.length > 0xffff) throw new ProtoError('string too long')
|
|
138
|
+
this.u16(s.length)
|
|
139
|
+
const u8 = this.u8
|
|
140
|
+
for (let i = 0; i < s.length; i++) {
|
|
141
|
+
const c = s.charCodeAt(i)
|
|
142
|
+
if (c > 0xff) throw new ProtoError('not a byte string')
|
|
143
|
+
u8[this.p + i] = c
|
|
144
|
+
}
|
|
145
|
+
this.p += s.length
|
|
146
|
+
}
|
|
147
|
+
qid(q) {
|
|
148
|
+
this.u8_(q.type)
|
|
149
|
+
this.u32(q.version || 0)
|
|
150
|
+
this.u64(q.path)
|
|
151
|
+
}
|
|
152
|
+
bytes(b) {
|
|
153
|
+
this.u8.set(b, this.p)
|
|
154
|
+
this.p += b.length
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Reads from a Uint8Array at a moving offset; ProtoError when short. */
|
|
159
|
+
export class Reader {
|
|
160
|
+
constructor(u8, p = 0, end = u8.length) {
|
|
161
|
+
this.u8 = u8
|
|
162
|
+
this.dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength)
|
|
163
|
+
this.p = p
|
|
164
|
+
this.end = end
|
|
165
|
+
}
|
|
166
|
+
need(n) {
|
|
167
|
+
if (this.p + n > this.end) throw new ProtoError('message too short')
|
|
168
|
+
}
|
|
169
|
+
u8_() { this.need(1); return this.dv.getUint8(this.p++) }
|
|
170
|
+
u16() { this.need(2); const v = this.dv.getUint16(this.p, true); this.p += 2; return v }
|
|
171
|
+
u32() { this.need(4); const v = this.dv.getUint32(this.p, true); this.p += 4; return v }
|
|
172
|
+
u64() {
|
|
173
|
+
this.need(8)
|
|
174
|
+
const lo = this.dv.getUint32(this.p, true)
|
|
175
|
+
const hi = this.dv.getUint32(this.p + 4, true)
|
|
176
|
+
this.p += 8
|
|
177
|
+
if (hi < 0x200000) return hi * TWO32 + lo
|
|
178
|
+
return (BigInt(hi) << 32n) | BigInt(lo)
|
|
179
|
+
}
|
|
180
|
+
str() {
|
|
181
|
+
const n = this.u16()
|
|
182
|
+
this.need(n)
|
|
183
|
+
const s = String.fromCharCode.apply(null, this.u8.subarray(this.p, this.p + n))
|
|
184
|
+
this.p += n
|
|
185
|
+
return s
|
|
186
|
+
}
|
|
187
|
+
qid() {
|
|
188
|
+
const type = this.u8_()
|
|
189
|
+
const version = this.u32()
|
|
190
|
+
const path = this.u64()
|
|
191
|
+
return { type, version, path }
|
|
192
|
+
}
|
|
193
|
+
bytes(n) {
|
|
194
|
+
this.need(n)
|
|
195
|
+
const b = this.u8.subarray(this.p, this.p + n)
|
|
196
|
+
this.p += n
|
|
197
|
+
return b
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function writeField(w, kind, v) {
|
|
202
|
+
switch (kind) {
|
|
203
|
+
case U8: return w.u8_(v)
|
|
204
|
+
case U16: return w.u16(v)
|
|
205
|
+
case U32: return w.u32(v)
|
|
206
|
+
case U64: return w.u64(v)
|
|
207
|
+
case S: return w.str(v)
|
|
208
|
+
case QID: return w.qid(v)
|
|
209
|
+
case DATA: w.u32(v.length); return w.bytes(v)
|
|
210
|
+
case WNAMES: w.u16(v.length); for (const s of v) w.str(s); return
|
|
211
|
+
case QIDS: w.u16(v.length); for (const q of v) w.qid(q); return
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function readField(r, kind) {
|
|
216
|
+
switch (kind) {
|
|
217
|
+
case U8: return r.u8_()
|
|
218
|
+
case U16: return r.u16()
|
|
219
|
+
case U32: return r.u32()
|
|
220
|
+
case U64: return r.u64()
|
|
221
|
+
case S: return r.str()
|
|
222
|
+
case QID: return r.qid()
|
|
223
|
+
case DATA: return r.bytes(r.u32())
|
|
224
|
+
case WNAMES: { const n = r.u16(); const out = new Array(n); for (let i = 0; i < n; i++) out[i] = r.str(); return out }
|
|
225
|
+
case QIDS: { const n = r.u16(); const out = new Array(n); for (let i = 0; i < n; i++) out[i] = r.qid(); return out }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Encodes message type (number or name) with tag and fields into a frame. */
|
|
230
|
+
export function encode(type, tag, fields = {}) {
|
|
231
|
+
if (typeof type === 'string') type = T[type]
|
|
232
|
+
const spec = MESSAGES[type]
|
|
233
|
+
if (!spec) throw new ProtoError(`unknown message type ${type}`)
|
|
234
|
+
let size = HEADER
|
|
235
|
+
for (const [name, kind] of spec[1]) size += sizeOf(kind, fields[name])
|
|
236
|
+
if (size > 0xffffffff) throw new ProtoError('message too large')
|
|
237
|
+
const out = new Uint8Array(size)
|
|
238
|
+
const w = new Writer(out)
|
|
239
|
+
w.u32(size)
|
|
240
|
+
w.u8_(type)
|
|
241
|
+
w.u16(tag)
|
|
242
|
+
for (const [name, kind] of spec[1]) writeField(w, kind, fields[name])
|
|
243
|
+
return out
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** The frame's header: {size, type, tag}. */
|
|
247
|
+
export function header(buf) {
|
|
248
|
+
if (buf.length < HEADER) throw new ProtoError('message too short')
|
|
249
|
+
const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
250
|
+
return { size: dv.getUint32(0, true), type: buf[4], tag: dv.getUint16(5, true) }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Decodes a frame into {type, op (the message's name), tag, ...fields}. Byte fields are views into buf. */
|
|
254
|
+
export function decode(buf) {
|
|
255
|
+
const { size, type, tag } = header(buf)
|
|
256
|
+
if (size < HEADER || size > buf.length) throw new ProtoError('bad frame size')
|
|
257
|
+
const spec = MESSAGES[type]
|
|
258
|
+
if (!spec) {
|
|
259
|
+
const e = new ProtoError(`unknown message type ${type}`)
|
|
260
|
+
e.tag = tag
|
|
261
|
+
e.type = type
|
|
262
|
+
throw e
|
|
263
|
+
}
|
|
264
|
+
const r = new Reader(buf, HEADER, size)
|
|
265
|
+
const m = { type, op: spec[0], tag }
|
|
266
|
+
for (const [name, kind] of spec[1]) m[name] = readField(r, kind)
|
|
267
|
+
if (r.p !== size) throw new ProtoError(`${spec[0]}: ${size - r.p} trailing bytes`)
|
|
268
|
+
return m
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** One Rreaddir entry's encoded size. */
|
|
272
|
+
export function direntSize(name) {
|
|
273
|
+
return 13 + 8 + 1 + 2 + name.length
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Encodes dirents [{qid, offset, type, name}] (Rreaddir's data). */
|
|
277
|
+
export function encodeDirents(list) {
|
|
278
|
+
let n = 0
|
|
279
|
+
for (const d of list) n += direntSize(d.name)
|
|
280
|
+
const out = new Uint8Array(n)
|
|
281
|
+
const w = new Writer(out)
|
|
282
|
+
for (const d of list) {
|
|
283
|
+
w.qid(d.qid)
|
|
284
|
+
w.u64(d.offset)
|
|
285
|
+
w.u8_(d.type)
|
|
286
|
+
w.str(d.name)
|
|
287
|
+
}
|
|
288
|
+
return out
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Decodes Rreaddir's data into [{qid, offset, type, name}]. */
|
|
292
|
+
export function decodeDirents(data) {
|
|
293
|
+
const r = new Reader(data)
|
|
294
|
+
const out = []
|
|
295
|
+
while (r.p < r.end) {
|
|
296
|
+
const qid = r.qid()
|
|
297
|
+
const offset = r.u64()
|
|
298
|
+
const type = r.u8_()
|
|
299
|
+
const name = r.str()
|
|
300
|
+
out.push({ qid, offset, type, name })
|
|
301
|
+
}
|
|
302
|
+
return out
|
|
303
|
+
}
|