js-dos 7.4.9 → 8.1.0
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/.github/workflows/build.yml +51 -0
- package/.gitmodules +4 -0
- package/README.deployment.md +11 -0
- package/README.md +29 -89
- package/index.html +106 -0
- package/package.json +46 -71
- package/postcss.config.cjs +8 -0
- package/src/app.css +14 -0
- package/src/base.css +10 -0
- package/src/components/checkbox.tsx +41 -0
- package/src/components/close-button.tsx +17 -0
- package/src/components/components.css +28 -0
- package/src/components/dos-option-checkbox.tsx +94 -0
- package/src/components/dos-option-select.tsx +108 -0
- package/src/components/dos-option-slider.tsx +35 -0
- package/src/components/error.tsx +16 -0
- package/src/components/loading.tsx +26 -0
- package/src/components/lock.tsx +10 -0
- package/src/components/select.tsx +33 -0
- package/src/components/slider.tsx +69 -0
- package/src/download-file.ts +18 -0
- package/src/frame/account-frame.tsx +156 -0
- package/src/frame/editor/defaults.ts +419 -0
- package/src/frame/editor/editor-conf-frame.tsx +226 -0
- package/src/frame/editor/editor-frame.css +55 -0
- package/src/frame/editor/editor-fs-frame.tsx +314 -0
- package/src/frame/fat-drives-frame.tsx +135 -0
- package/src/frame/frame.css +119 -0
- package/src/frame/frame.tsx +34 -0
- package/src/frame/image-rendering-frame.tsx +8 -0
- package/src/frame/network-frame.tsx +157 -0
- package/src/frame/quick-save-frame.tsx +96 -0
- package/src/frame/settings-frame.tsx +14 -0
- package/src/frame/stats-frame.tsx +87 -0
- package/src/host/bundle-storage.ts +91 -0
- package/src/host/fullscreen.ts +35 -0
- package/src/host/lcache.ts +203 -0
- package/src/host/lstorage.ts +156 -0
- package/src/i18n.ts +179 -0
- package/src/index.css +7 -0
- package/src/load.ts +132 -0
- package/src/login/login.css +9 -0
- package/src/login/login.tsx +77 -0
- package/src/main.tsx +197 -0
- package/src/public/types.ts +41 -0
- package/src/sidebar/account-button.tsx +33 -0
- package/src/sidebar/cloud-save-button.tsx +89 -0
- package/src/sidebar/diskette-icon.tsx +14 -0
- package/src/sidebar/fullscreen-button.tsx +44 -0
- package/src/sidebar/network-button.tsx +39 -0
- package/src/sidebar/sidebar-button.tsx +244 -0
- package/src/sidebar/sidebar.css +47 -0
- package/src/sidebar/sidebar.tsx +30 -0
- package/src/sockdrive/LICENSE +674 -0
- package/src/sockdrive/README.md +45 -0
- package/src/sockdrive/include/sockdrive.h +23 -0
- package/src/sockdrive/js/.eslintrc.json +43 -0
- package/src/sockdrive/js/package.json +29 -0
- package/src/sockdrive/js/src/fatfs/chains.js +318 -0
- package/src/sockdrive/js/src/fatfs/dir.js +385 -0
- package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
- package/src/sockdrive/js/src/fatfs/index.js +606 -0
- package/src/sockdrive/js/src/fatfs/structs.js +255 -0
- package/src/sockdrive/js/src/fatfs/vol.js +166 -0
- package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
- package/src/sockdrive/js/src/sockdrive/drive.ts +457 -0
- package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
- package/src/sockdrive/js/src/sockdrive/types.ts +14 -0
- package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
- package/src/sockdrive/js/src/sockdrive-fat.ts +226 -0
- package/src/sockdrive/js/src/sockdrive-native.ts +100 -0
- package/src/sockdrive/js/src/test/test.ts +288 -0
- package/src/sockdrive/js/tsconfig.json +11 -0
- package/src/sockdrive/js/webpack.config.js +68 -0
- package/src/sockdrive/js/yarn.lock +1808 -0
- package/src/store/auth.ts +115 -0
- package/src/store/dos.ts +305 -0
- package/src/store/editor.ts +61 -0
- package/src/store/init.ts +48 -0
- package/src/store/storage.ts +32 -0
- package/src/store/ui.ts +186 -0
- package/src/store.tsx +108 -0
- package/src/ui.tsx +104 -0
- package/src/v8/changes.ts +56 -0
- package/src/v8/config.ts +18 -0
- package/src/v8/subscriptions.ts +62 -0
- package/src/vite-env.d.ts +2 -0
- package/src/window/dos/controls/keyboard.ts +32 -0
- package/src/window/dos/controls/keys.ts +331 -0
- package/src/window/dos/controls/mouse/mount.ts +99 -0
- package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
- package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
- package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
- package/src/window/dos/controls/mouse/pointer.ts +85 -0
- package/src/window/dos/controls/mouse.ts +21 -0
- package/src/window/dos/dos-dhry2.tsx +191 -0
- package/src/window/dos/dos-runtime.tsx +214 -0
- package/src/window/dos/dos-window.tsx +84 -0
- package/src/window/dos/render/canvas.ts +65 -0
- package/src/window/dos/render/resize.ts +31 -0
- package/src/window/dos/render/webgl.ts +173 -0
- package/src/window/dos/sound/audio-node.ts +148 -0
- package/src/window/error-window.tsx +9 -0
- package/src/window/loading-window.tsx +54 -0
- package/src/window/prerun-window.tsx +123 -0
- package/src/window/select-window.tsx +118 -0
- package/src/window/window.css +78 -0
- package/src/window/window.tsx +49 -0
- package/src/ws/ws-sockdrive.ts +101 -0
- package/src/ws/ws-transport-layer.ts +455 -0
- package/tailwind.config.cjs +48 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +9 -0
- package/vite.config.ts +22 -0
- package/LICENSE +0 -339
- package/dist/emulators-ui-loader.png +0 -0
- package/dist/js-dos.css +0 -1690
- package/dist/js-dos.js +0 -17
- package/dist/js-dos.js.map +0 -1
- package/dist/types/src/backend/v7/latency.d.ts +0 -8
- package/dist/types/src/backend/v7/personal.d.ts +0 -2
- package/dist/types/src/backend/v7/v7-config.d.ts +0 -16
- package/dist/types/src/components/action-bar.d.ts +0 -2
- package/dist/types/src/components/action-hide.d.ts +0 -4
- package/dist/types/src/components/action-save-or-exit.d.ts +0 -4
- package/dist/types/src/components/client.d.ts +0 -6
- package/dist/types/src/components/controls.d.ts +0 -8
- package/dist/types/src/components/cpu-control.d.ts +0 -2
- package/dist/types/src/components/horizontal-slider.d.ts +0 -13
- package/dist/types/src/components/region.d.ts +0 -6
- package/dist/types/src/components/scale-control.d.ts +0 -3
- package/dist/types/src/components/sensitivity-control.d.ts +0 -3
- package/dist/types/src/components/sidebar/latency-info.d.ts +0 -7
- package/dist/types/src/components/sidebar/main.d.ts +0 -2
- package/dist/types/src/components/sidebar/networking.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-add-time.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token-select.d.ts +0 -2
- package/dist/types/src/components/sidebar/token/token.d.ts +0 -17
- package/dist/types/src/components/sidebar.d.ts +0 -2
- package/dist/types/src/components/sync-control.d.ts +0 -2
- package/dist/types/src/components/tip.d.ts +0 -2
- package/dist/types/src/components/vertical-slider.d.ts +0 -12
- package/dist/types/src/components/volume-control.d.ts +0 -3
- package/dist/types/src/dom.d.ts +0 -8
- package/dist/types/src/hardware-transport-layer.d.ts +0 -18
- package/dist/types/src/icons.d.ts +0 -95
- package/dist/types/src/player-app.d.ts +0 -50
- package/dist/types/src/player.d.ts +0 -29
- package/dist/types/src/request.d.ts +0 -1
- package/dist/types/src/xhr.d.ts +0 -6
- package/dist/types/v7-services/src/personal.d.ts +0 -3
- package/dist/wdosbox.js +0 -55
- package/dist/wdosbox.js.symbols +0 -3174
- package/dist/wdosbox.wasm +0 -0
- package/examples/digger/index.html +0 -71
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { EmModule, Ptr, Stats } from "./types";
|
|
2
|
+
import { BlockCache, Cache } from "./cache";
|
|
3
|
+
|
|
4
|
+
const MAX_FRAME_SIZE = 1 * 1024 * 1024;
|
|
5
|
+
const MEMORY_LIMIT = 64 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
interface Request {
|
|
8
|
+
type: 1 | 2, // read | write
|
|
9
|
+
sectors: number[],
|
|
10
|
+
buffer: Ptr | Uint8Array,
|
|
11
|
+
resolve: (result: number) => void,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class Drive {
|
|
15
|
+
module: EmModule;
|
|
16
|
+
sectorSize: number;
|
|
17
|
+
endpoint: string;
|
|
18
|
+
owner: string;
|
|
19
|
+
drive: string;
|
|
20
|
+
token: string;
|
|
21
|
+
stats: Stats;
|
|
22
|
+
|
|
23
|
+
socket: Promise<WebSocket> = Promise.resolve(null as any);
|
|
24
|
+
request: Request | null;
|
|
25
|
+
pendingRequest: Request | null;
|
|
26
|
+
|
|
27
|
+
aheadRange: number = 0;
|
|
28
|
+
aheadSize: number = 0;
|
|
29
|
+
writeBuffer: Uint8Array;
|
|
30
|
+
|
|
31
|
+
maxRead: number = 0;
|
|
32
|
+
readBuffer: Uint8Array = new Uint8Array(0);
|
|
33
|
+
readAheadBuffer: Uint8Array = new Uint8Array(0);
|
|
34
|
+
readAheadPos = 0;
|
|
35
|
+
readAheadCompressed = 0;
|
|
36
|
+
readStartedAt: number = 0;
|
|
37
|
+
decodeBuffer: Uint8Array = new Uint8Array(0);
|
|
38
|
+
|
|
39
|
+
cache: Cache | null = null;
|
|
40
|
+
cleanup = () => {/**/};
|
|
41
|
+
|
|
42
|
+
openFn = (read: boolean, write: boolean, size: number, preloadQueue: number[]) => {/**/};
|
|
43
|
+
preloadProgressFn = (restBytes: number) => {/**/};
|
|
44
|
+
errorFn = (e: Error) => {/**/};
|
|
45
|
+
|
|
46
|
+
retries: number;
|
|
47
|
+
|
|
48
|
+
preloadSectors: boolean;
|
|
49
|
+
preloadQueue: number[] = [];
|
|
50
|
+
|
|
51
|
+
public constructor(endpoint: string,
|
|
52
|
+
owner: string,
|
|
53
|
+
drive: string,
|
|
54
|
+
token: string,
|
|
55
|
+
stats: Stats,
|
|
56
|
+
module: EmModule,
|
|
57
|
+
preloadSectors = true) {
|
|
58
|
+
this.sectorSize = 512;
|
|
59
|
+
this.module = module;
|
|
60
|
+
this.endpoint = endpoint;
|
|
61
|
+
this.owner = owner;
|
|
62
|
+
this.drive = drive;
|
|
63
|
+
this.token = token;
|
|
64
|
+
this.request = null;
|
|
65
|
+
this.pendingRequest = null;
|
|
66
|
+
this.writeBuffer = new Uint8Array(1 + 4 + this.sectorSize);
|
|
67
|
+
this.stats = stats;
|
|
68
|
+
this.retries = 3;
|
|
69
|
+
this.preloadSectors = preloadSectors;
|
|
70
|
+
|
|
71
|
+
this.reconnect();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public onError(errorFn: (e: Error) => void) {
|
|
75
|
+
this.errorFn = errorFn;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public onOpen(openFn: (read: boolean, write: boolean, imageSize: number, preloadQueue: number[]) => void) {
|
|
79
|
+
this.openFn = openFn;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public onPreloadProgress(progressFn: (restBytes: number) => void) {
|
|
83
|
+
this.preloadProgressFn = progressFn;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public reconnect(): void {
|
|
87
|
+
this.socket = new Promise<WebSocket>((resolve, reject) => {
|
|
88
|
+
const socket = new WebSocket(this.endpoint);
|
|
89
|
+
socket.binaryType = "arraybuffer";
|
|
90
|
+
const onMessage = this.onMessage.bind(this);
|
|
91
|
+
const onOpen = () => {
|
|
92
|
+
const onInit = (event: { data: string }) => {
|
|
93
|
+
socket.removeEventListener("message", onInit);
|
|
94
|
+
if (event.data.startsWith("write") || event.data.startsWith("read")) {
|
|
95
|
+
const [mode, aheadRangeStr, sizeStr] = event.data.split(",");
|
|
96
|
+
const aheadRange = Number.parseInt(aheadRangeStr);
|
|
97
|
+
this.aheadRange = aheadRange;
|
|
98
|
+
this.aheadSize = aheadRange * this.sectorSize;
|
|
99
|
+
this.maxRead = Math.floor(MAX_FRAME_SIZE / this.aheadSize);
|
|
100
|
+
|
|
101
|
+
const decodeBufferSize = this.aheadSize * this.maxRead;
|
|
102
|
+
this.readBuffer = new Uint8Array(1 + 4 + this.maxRead * 4);
|
|
103
|
+
this.readAheadBuffer = new Uint8Array(decodeBufferSize);
|
|
104
|
+
this.decodeBuffer = new Uint8Array(decodeBufferSize);
|
|
105
|
+
this.cache = new BlockCache(this.sectorSize, aheadRange, MEMORY_LIMIT);
|
|
106
|
+
|
|
107
|
+
let preloadLength = 0;
|
|
108
|
+
let preloadPos = 0;
|
|
109
|
+
let preload: Uint8Array = new Uint8Array(0);
|
|
110
|
+
const onPreloadMessage = (event: { data: ArrayBuffer }) => {
|
|
111
|
+
let data = new Uint8Array(event.data);
|
|
112
|
+
if (preloadLength === 0) {
|
|
113
|
+
preloadLength = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
|
|
114
|
+
preload = new Uint8Array(preloadLength - 4);
|
|
115
|
+
data = data.slice(4);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (preloadPos < preloadLength) {
|
|
119
|
+
preload.set(data, preloadPos);
|
|
120
|
+
preloadPos += data.length;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (preloadPos > preload.length) {
|
|
124
|
+
const error = new Error("Preload buffer is bigger then expected");
|
|
125
|
+
this.errorFn(error);
|
|
126
|
+
reject(error);
|
|
127
|
+
} else if (preloadPos == preload.length) {
|
|
128
|
+
this.preloadQueue = [];
|
|
129
|
+
if (this.preloadSectors) {
|
|
130
|
+
for (let i = 0; i < preload.length; i += 4) {
|
|
131
|
+
this.preloadQueue.push(preload[i] + (preload[i + 1] << 8) +
|
|
132
|
+
(preload[i + 2] << 16) + (preload[i + 3] << 24));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
socket.removeEventListener("message", onPreloadMessage);
|
|
136
|
+
socket.addEventListener("message", onMessage);
|
|
137
|
+
this.openFn(true, mode === "write",
|
|
138
|
+
(Number.parseInt(sizeStr) ?? 2 * 1024 * 1024) * 1024,
|
|
139
|
+
this.preloadQueue);
|
|
140
|
+
resolve(socket);
|
|
141
|
+
|
|
142
|
+
if (this.preloadQueue.length > 0) {
|
|
143
|
+
if (this.preloadQueue.length * this.aheadSize > MEMORY_LIMIT) {
|
|
144
|
+
console.log("WARN! preloadQueue size is bigger then allowed",
|
|
145
|
+
this.preloadQueue.length * this.aheadSize / 1024 / 1024, ">",
|
|
146
|
+
MEMORY_LIMIT / 1024 / 1024);
|
|
147
|
+
this.preloadQueue = this.preloadQueue
|
|
148
|
+
.slice(0, Math.floor(MEMORY_LIMIT / this.aheadSize));
|
|
149
|
+
}
|
|
150
|
+
this.request = this.makeReadRequest(this.preloadQueue.shift()!);
|
|
151
|
+
this.executeRequest(this.request);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
socket.addEventListener("message", onPreloadMessage);
|
|
156
|
+
} else {
|
|
157
|
+
const error = new Error(event.data ?? "Unable to establish connection");
|
|
158
|
+
this.errorFn(error);
|
|
159
|
+
reject(error);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
socket.addEventListener("message", onInit);
|
|
163
|
+
socket.send(this.owner + "&" + this.drive + "&" + this.token);
|
|
164
|
+
};
|
|
165
|
+
const onError = (e: Event) => {
|
|
166
|
+
console.error("Network error", e, "will reconnect");
|
|
167
|
+
cleanup();
|
|
168
|
+
socket.close();
|
|
169
|
+
this.retries--;
|
|
170
|
+
if (this.retries === 0) {
|
|
171
|
+
const error = new Error("Network problem");
|
|
172
|
+
this.errorFn(error);
|
|
173
|
+
reject(error);
|
|
174
|
+
} else {
|
|
175
|
+
setTimeout(this.reconnect.bind(this), 300);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
socket.addEventListener("error", onError);
|
|
179
|
+
socket.addEventListener("open", onOpen);
|
|
180
|
+
const cleanup = function() {
|
|
181
|
+
socket.removeEventListener("message", onMessage);
|
|
182
|
+
socket.removeEventListener("open", onOpen);
|
|
183
|
+
socket.removeEventListener("error", onError);
|
|
184
|
+
};
|
|
185
|
+
this.cleanup = cleanup;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (this.request !== null) {
|
|
189
|
+
this.executeRequest(this.request);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private makeReadRequest(sector: number, buffer: number = -1, resolve: (res: number) => void = () => { }): Request {
|
|
194
|
+
const sectors: number[] = [sector];
|
|
195
|
+
if (this.preloadQueue.length > 0) {
|
|
196
|
+
this.preloadProgressFn(this.preloadQueue.length * this.aheadSize);
|
|
197
|
+
while (this.preloadQueue.length > 0 && sectors.length < this.maxRead) {
|
|
198
|
+
const preload = this.preloadQueue.shift()!;
|
|
199
|
+
if (preload !== sector) {
|
|
200
|
+
sectors.push(preload);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (let i = 0; i < this.preloadQueue.length; ++i) {
|
|
204
|
+
if (this.preloadQueue[i] == sector) {
|
|
205
|
+
this.preloadQueue.splice(i, 1);
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
type: 1,
|
|
212
|
+
sectors,
|
|
213
|
+
buffer,
|
|
214
|
+
resolve,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
public read(sector: number, buffer: Ptr, sync: boolean): Promise<number> | number {
|
|
219
|
+
const cached = this.cache!.read(sector);
|
|
220
|
+
if (cached !== null) {
|
|
221
|
+
if (sync) {
|
|
222
|
+
this.stats.cacheHit++;
|
|
223
|
+
}
|
|
224
|
+
this.module.HEAPU8.set(cached, buffer);
|
|
225
|
+
return sync ? 0 : Promise.resolve(0);
|
|
226
|
+
} else if (sync) {
|
|
227
|
+
return 255;
|
|
228
|
+
} else {
|
|
229
|
+
return new Promise<number>((resolve) => {
|
|
230
|
+
this.stats.cacheMiss++;
|
|
231
|
+
|
|
232
|
+
const request: Request = this.makeReadRequest(sector, buffer, resolve);
|
|
233
|
+
if (this.request !== null) {
|
|
234
|
+
if (this.pendingRequest === null) {
|
|
235
|
+
this.pendingRequest = request;
|
|
236
|
+
} else {
|
|
237
|
+
console.error("New read request while old one is still processed");
|
|
238
|
+
resolve(3);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
this.request = request;
|
|
242
|
+
this.executeRequest(this.request);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public write(sector: number, buffer: Ptr): number {
|
|
249
|
+
const request: Request = {
|
|
250
|
+
type: 2,
|
|
251
|
+
sectors: [sector],
|
|
252
|
+
buffer: this.module.HEAPU8.slice(buffer, buffer + this.sectorSize),
|
|
253
|
+
resolve: () => {/**/},
|
|
254
|
+
};
|
|
255
|
+
this.cache!.write(sector, request.buffer as Uint8Array);
|
|
256
|
+
this.executeRequest(request);
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public async close() {
|
|
261
|
+
const socket = await this.socket;
|
|
262
|
+
await new Promise<void>((resolve) => {
|
|
263
|
+
const intervalId = setInterval(() => {
|
|
264
|
+
if (socket.bufferedAmount === 0) {
|
|
265
|
+
clearInterval(intervalId);
|
|
266
|
+
resolve();
|
|
267
|
+
}
|
|
268
|
+
}, 32);
|
|
269
|
+
});
|
|
270
|
+
this.cleanup();
|
|
271
|
+
socket.close();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private executeRequest(request: Request) {
|
|
275
|
+
this.socket.then((socket) => {
|
|
276
|
+
if (request.type === 1) {
|
|
277
|
+
const { sectors } = request;
|
|
278
|
+
this.readStartedAt = Date.now();
|
|
279
|
+
this.readAheadPos = 0;
|
|
280
|
+
this.readAheadCompressed = 0;
|
|
281
|
+
this.readBuffer[0] = 1; // read
|
|
282
|
+
this.readBuffer[1] = sectors.length & 0xFF;
|
|
283
|
+
this.readBuffer[2] = (sectors.length >> 8) & 0xFF;
|
|
284
|
+
this.readBuffer[3] = (sectors.length >> 16) & 0xFF;
|
|
285
|
+
this.readBuffer[4] = (sectors.length >> 24) & 0xFF;
|
|
286
|
+
|
|
287
|
+
for (let i = 0; i < sectors.length; ++i) {
|
|
288
|
+
const origin = this.cache!.getOrigin(sectors[i]);
|
|
289
|
+
|
|
290
|
+
if (i > 0 && origin !== sectors[i]) {
|
|
291
|
+
console.error("Assertion failed orign should equal to sector", origin, sectors[i]);
|
|
292
|
+
request.resolve(5);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.readBuffer[5 + i * 4] = origin & 0xFF;
|
|
297
|
+
this.readBuffer[5 + i * 4 + 1] = (origin >> 8) & 0xFF;
|
|
298
|
+
this.readBuffer[5 + i * 4 + 2] = (origin >> 16) & 0xFF;
|
|
299
|
+
this.readBuffer[5 + i * 4 + 3] = (origin >> 24) & 0xFF;
|
|
300
|
+
}
|
|
301
|
+
socket.send(this.readBuffer.slice(0, sectors.length * 4 + 5).buffer);
|
|
302
|
+
} else {
|
|
303
|
+
const { sectors, buffer, resolve } = request;
|
|
304
|
+
this.stats.write += this.sectorSize;
|
|
305
|
+
this.writeBuffer[0] = 2; // write
|
|
306
|
+
this.writeBuffer[1] = sectors[0] & 0xFF;
|
|
307
|
+
this.writeBuffer[2] = (sectors[0] >> 8) & 0xFF;
|
|
308
|
+
this.writeBuffer[3] = (sectors[0] >> 16) & 0xFF;
|
|
309
|
+
this.writeBuffer[4] = (sectors[0] >> 24) & 0xFF;
|
|
310
|
+
// TBD: maybe do not copy and send just slice ???
|
|
311
|
+
this.writeBuffer.set(buffer as Uint8Array, 5);
|
|
312
|
+
socket.send(this.writeBuffer.buffer);
|
|
313
|
+
resolve(0);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public onMessage(event: { data: ArrayBuffer }) {
|
|
319
|
+
if (this.request === null) {
|
|
320
|
+
console.error("Received message without request");
|
|
321
|
+
this.reconnect();
|
|
322
|
+
} else if (this.request.type === 2) {
|
|
323
|
+
console.error("Received read payload while write request");
|
|
324
|
+
this.reconnect();
|
|
325
|
+
} else if (event.data instanceof ArrayBuffer) {
|
|
326
|
+
let data = new Uint8Array(event.data);
|
|
327
|
+
if (this.readAheadCompressed === 0) {
|
|
328
|
+
this.readAheadCompressed = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
|
|
329
|
+
data = data.slice(4);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const { sectors, buffer, resolve } = this.request;
|
|
333
|
+
const restLength = this.readAheadCompressed - this.readAheadPos;
|
|
334
|
+
if (data.byteLength > restLength || restLength < 0) {
|
|
335
|
+
console.error("wrong read payload length " + data.byteLength + " instead of " + restLength);
|
|
336
|
+
resolve(3);
|
|
337
|
+
} else {
|
|
338
|
+
this.readAheadBuffer.set(data, this.readAheadPos);
|
|
339
|
+
this.readAheadPos += data.byteLength;
|
|
340
|
+
const expectedSize = this.aheadSize * sectors.length;
|
|
341
|
+
|
|
342
|
+
if (this.readAheadPos == this.readAheadCompressed) {
|
|
343
|
+
let decodeResult = expectedSize;
|
|
344
|
+
if (this.readAheadCompressed < expectedSize) {
|
|
345
|
+
const result = decodeLz4(this.readAheadBuffer, this.decodeBuffer, 0, this.readAheadCompressed);
|
|
346
|
+
if (result < 0) {
|
|
347
|
+
decodeResult = result;
|
|
348
|
+
} else {
|
|
349
|
+
this.readAheadBuffer.set(this.decodeBuffer.slice(0, expectedSize), 0);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (decodeResult != expectedSize) {
|
|
354
|
+
console.error("wrong decode result " + decodeResult + " should be " + expectedSize);
|
|
355
|
+
resolve(4);
|
|
356
|
+
} else {
|
|
357
|
+
for (let i = 0; i < sectors.length; ++i) {
|
|
358
|
+
const aheadOffset = i * this.aheadSize;
|
|
359
|
+
const sector = sectors[i];
|
|
360
|
+
const origin = this.cache!.getOrigin(sector);
|
|
361
|
+
this.cache!.create(origin,
|
|
362
|
+
this.readAheadBuffer.slice(aheadOffset, aheadOffset + this.aheadSize));
|
|
363
|
+
if (i == 0 && buffer as number >= 0) {
|
|
364
|
+
const offset = sector - origin;
|
|
365
|
+
// aheadOffset is zero
|
|
366
|
+
this.module.HEAPU8.set(this.readAheadBuffer.slice(offset * this.sectorSize,
|
|
367
|
+
(offset + 1) * this.sectorSize), buffer as number);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
this.stats.cacheUsed = this.cache!.memUsed();
|
|
372
|
+
this.stats.read += this.readAheadCompressed;
|
|
373
|
+
this.stats.readTotalTime += Date.now() - this.readStartedAt;
|
|
374
|
+
this.request = null;
|
|
375
|
+
resolve(0);
|
|
376
|
+
|
|
377
|
+
if (this.pendingRequest !== null) {
|
|
378
|
+
this.request = this.pendingRequest;
|
|
379
|
+
this.pendingRequest = null;
|
|
380
|
+
this.executeRequest(this.request);
|
|
381
|
+
} else if (this.preloadQueue.length > 0) {
|
|
382
|
+
this.request = this.makeReadRequest(this.preloadQueue.shift()!);
|
|
383
|
+
this.executeRequest(this.request);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
console.error("Received non arraybuffer data");
|
|
390
|
+
this.reconnect();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Decode a block. Assumptions: input contains all sequences of a
|
|
397
|
+
* chunk, output is large enough to receive the decoded data.
|
|
398
|
+
* If the output buffer is too small, an error will be thrown.
|
|
399
|
+
* If the returned value is negative, an error occured at the returned offset.
|
|
400
|
+
*
|
|
401
|
+
* @param {ArrayBufferView} input input data
|
|
402
|
+
* @param {ArrayBufferView} output output data
|
|
403
|
+
* @param {number=} sIdx
|
|
404
|
+
* @param {number=} eIdx
|
|
405
|
+
* @return {number} number of decoded bytes
|
|
406
|
+
* @private
|
|
407
|
+
*/
|
|
408
|
+
function decodeLz4(input: Uint8Array, output: Uint8Array, sIdx: number, eIdx: number) {
|
|
409
|
+
sIdx = sIdx || 0;
|
|
410
|
+
eIdx = eIdx || (input.length - sIdx);
|
|
411
|
+
// Process each sequence in the incoming data
|
|
412
|
+
let i; let n; let j;
|
|
413
|
+
for (i = sIdx, n = eIdx, j = 0; i < n;) {
|
|
414
|
+
const token = input[i++];
|
|
415
|
+
|
|
416
|
+
// Literals
|
|
417
|
+
let literalsLength = (token >> 4);
|
|
418
|
+
if (literalsLength > 0) {
|
|
419
|
+
// length of literals
|
|
420
|
+
let l = literalsLength + 240;
|
|
421
|
+
while (l === 255) {
|
|
422
|
+
l = input[i++];
|
|
423
|
+
literalsLength += l;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Copy the literals
|
|
427
|
+
const end = i + literalsLength;
|
|
428
|
+
while (i < end) output[j++] = input[i++];
|
|
429
|
+
|
|
430
|
+
// End of buffer?
|
|
431
|
+
if (i === n) return j;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Match copy
|
|
435
|
+
// 2 bytes offset (little endian)
|
|
436
|
+
const offset = input[i++] | (input[i++] << 8);
|
|
437
|
+
|
|
438
|
+
// XXX 0 is an invalid offset value
|
|
439
|
+
if (offset === 0) return j;
|
|
440
|
+
if (offset > j) return -(i - 2);
|
|
441
|
+
|
|
442
|
+
// length of match copy
|
|
443
|
+
let matchLength = (token & 0xf);
|
|
444
|
+
let l = matchLength + 240;
|
|
445
|
+
while (l === 255) {
|
|
446
|
+
l = input[i++];
|
|
447
|
+
matchLength += l;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Copy the match
|
|
451
|
+
let pos = j - offset; // position of the match copy in the current output
|
|
452
|
+
const end = j + matchLength + 4; // minmatch = 4
|
|
453
|
+
while (j < end) output[j++] = output[pos++];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return j;
|
|
457
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// This code is from https://github.com/rsms/js-lru
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A doubly linked list-based Least Recently Used (LRU) cache. Will keep most
|
|
7
|
+
* recently used items while discarding least recently used items when its limit
|
|
8
|
+
* is reached.
|
|
9
|
+
*
|
|
10
|
+
* Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>
|
|
11
|
+
* See README.md for details.
|
|
12
|
+
*
|
|
13
|
+
* Illustration of the design:
|
|
14
|
+
*
|
|
15
|
+
* entry entry entry entry
|
|
16
|
+
* ______ ______ ______ ______
|
|
17
|
+
* | head |.newer => | |.newer => | |.newer => | tail |
|
|
18
|
+
* | A | | B | | C | | D |
|
|
19
|
+
* |______| <= older.|______| <= older.|______| <= older.|______|
|
|
20
|
+
*
|
|
21
|
+
* removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added
|
|
22
|
+
*/
|
|
23
|
+
const NEWER = Symbol("newer");
|
|
24
|
+
const OLDER = Symbol("older");
|
|
25
|
+
|
|
26
|
+
export class LRUMap {
|
|
27
|
+
size: number;
|
|
28
|
+
limit: number;
|
|
29
|
+
newest: any;
|
|
30
|
+
oldest: any;
|
|
31
|
+
_keymap: Map<any, any>;
|
|
32
|
+
|
|
33
|
+
constructor(limit: number, entries?: any) {
|
|
34
|
+
if (typeof limit !== "number") {
|
|
35
|
+
// called as (entries)
|
|
36
|
+
entries = limit;
|
|
37
|
+
limit = 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.size = 0;
|
|
41
|
+
this.limit = limit;
|
|
42
|
+
this.oldest = this.newest = undefined;
|
|
43
|
+
this._keymap = new Map();
|
|
44
|
+
|
|
45
|
+
if (entries) {
|
|
46
|
+
this.assign(entries);
|
|
47
|
+
if (limit < 1) {
|
|
48
|
+
this.limit = this.size;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_markEntryAsUsed(entry: any) {
|
|
54
|
+
if (entry === this.newest) {
|
|
55
|
+
// Already the most recenlty used entry, so no need to update the list
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// HEAD--------------TAIL
|
|
59
|
+
// <.older .newer>
|
|
60
|
+
// <--- add direction --
|
|
61
|
+
// A B C <D> E
|
|
62
|
+
if (entry[NEWER]) {
|
|
63
|
+
if (entry === this.oldest) {
|
|
64
|
+
this.oldest = entry[NEWER];
|
|
65
|
+
}
|
|
66
|
+
entry[NEWER][OLDER] = entry[OLDER]; // C <-- E.
|
|
67
|
+
}
|
|
68
|
+
if (entry[OLDER]) {
|
|
69
|
+
entry[OLDER][NEWER] = entry[NEWER]; // C. --> E
|
|
70
|
+
}
|
|
71
|
+
entry[NEWER] = undefined; // D --x
|
|
72
|
+
entry[OLDER] = this.newest; // D. --> E
|
|
73
|
+
if (this.newest) {
|
|
74
|
+
this.newest[NEWER] = entry; // E. <-- D
|
|
75
|
+
}
|
|
76
|
+
this.newest = entry;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
assign(entries: any) {
|
|
80
|
+
let entry; let limit = this.limit || Number.MAX_VALUE;
|
|
81
|
+
this._keymap.clear();
|
|
82
|
+
const it = entries[Symbol.iterator]();
|
|
83
|
+
for (let itv = it.next(); !itv.done; itv = it.next()) {
|
|
84
|
+
const e = new (Entry as any)(itv.value[0], itv.value[1]);
|
|
85
|
+
this._keymap.set(e.key, e);
|
|
86
|
+
if (!entry) {
|
|
87
|
+
this.oldest = e;
|
|
88
|
+
} else {
|
|
89
|
+
entry[NEWER] = e;
|
|
90
|
+
e[OLDER] = entry;
|
|
91
|
+
}
|
|
92
|
+
entry = e;
|
|
93
|
+
if (limit-- == 0) {
|
|
94
|
+
throw new Error("overflow");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.newest = entry;
|
|
98
|
+
this.size = this._keymap.size;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get(key: any) {
|
|
102
|
+
// First, find our cache entry
|
|
103
|
+
const entry = this._keymap.get(key);
|
|
104
|
+
if (!entry) return; // Not cached. Sorry.
|
|
105
|
+
// As <key> was found in the cache, register it as being requested recently
|
|
106
|
+
this._markEntryAsUsed(entry);
|
|
107
|
+
return entry.value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
set(key: any, value: any) {
|
|
111
|
+
let entry = this._keymap.get(key);
|
|
112
|
+
|
|
113
|
+
if (entry) {
|
|
114
|
+
// update existing
|
|
115
|
+
entry.value = value;
|
|
116
|
+
this._markEntryAsUsed(entry);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// new entry
|
|
121
|
+
this._keymap.set(key, (entry = new (Entry as any)(key, value)));
|
|
122
|
+
|
|
123
|
+
if (this.newest) {
|
|
124
|
+
// link previous tail to the new tail (entry)
|
|
125
|
+
this.newest[NEWER] = entry;
|
|
126
|
+
entry[OLDER] = this.newest;
|
|
127
|
+
} else {
|
|
128
|
+
// we're first in -- yay
|
|
129
|
+
this.oldest = entry;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// add new entry to the end of the linked list -- it's now the freshest entry.
|
|
133
|
+
this.newest = entry;
|
|
134
|
+
++this.size;
|
|
135
|
+
if (this.size > this.limit) {
|
|
136
|
+
// we hit the limit -- remove the head
|
|
137
|
+
this.shift();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
shift() {
|
|
144
|
+
// todo: handle special case when limit == 1
|
|
145
|
+
const entry = this.oldest;
|
|
146
|
+
if (entry) {
|
|
147
|
+
if (this.oldest[NEWER]) {
|
|
148
|
+
// advance the list
|
|
149
|
+
this.oldest = this.oldest[NEWER];
|
|
150
|
+
this.oldest[OLDER] = undefined;
|
|
151
|
+
} else {
|
|
152
|
+
// the cache is exhausted
|
|
153
|
+
this.oldest = undefined;
|
|
154
|
+
this.newest = undefined;
|
|
155
|
+
}
|
|
156
|
+
// Remove last strong reference to <entry> and remove links from the purged
|
|
157
|
+
// entry being returned:
|
|
158
|
+
entry[NEWER] = entry[OLDER] = undefined;
|
|
159
|
+
this._keymap.delete(entry.key);
|
|
160
|
+
--this.size;
|
|
161
|
+
return [entry.key, entry.value];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function Entry(this: any, key: any, value: any) {
|
|
167
|
+
this.key = key;
|
|
168
|
+
this.value = value;
|
|
169
|
+
this[NEWER] = undefined;
|
|
170
|
+
this[OLDER] = undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
function EntryIterator(this: any, oldestEntry: any) {
|
|
175
|
+
this.entry = oldestEntry;
|
|
176
|
+
}
|
|
177
|
+
EntryIterator.prototype[Symbol.iterator] = function () {
|
|
178
|
+
return this;
|
|
179
|
+
};
|
|
180
|
+
EntryIterator.prototype.next = function () {
|
|
181
|
+
const ent = this.entry;
|
|
182
|
+
if (ent) {
|
|
183
|
+
this.entry = ent[NEWER];
|
|
184
|
+
return { done: false, value: [ent.key, ent.value] };
|
|
185
|
+
} else {
|
|
186
|
+
return { done: true, value: undefined };
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
function KeyIterator(this: any, oldestEntry: any) {
|
|
192
|
+
this.entry = oldestEntry;
|
|
193
|
+
}
|
|
194
|
+
KeyIterator.prototype[Symbol.iterator] = function () {
|
|
195
|
+
return this;
|
|
196
|
+
};
|
|
197
|
+
KeyIterator.prototype.next = function () {
|
|
198
|
+
const ent = this.entry;
|
|
199
|
+
if (ent) {
|
|
200
|
+
this.entry = ent[NEWER];
|
|
201
|
+
return { done: false, value: ent.key };
|
|
202
|
+
} else {
|
|
203
|
+
return { done: true, value: undefined };
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
function ValueIterator(this: any, oldestEntry: any) {
|
|
208
|
+
this.entry = oldestEntry;
|
|
209
|
+
}
|
|
210
|
+
ValueIterator.prototype[Symbol.iterator] = function () {
|
|
211
|
+
return this;
|
|
212
|
+
};
|
|
213
|
+
ValueIterator.prototype.next = function () {
|
|
214
|
+
const ent = this.entry;
|
|
215
|
+
if (ent) {
|
|
216
|
+
this.entry = ent[NEWER];
|
|
217
|
+
return { done: false, value: ent.value };
|
|
218
|
+
} else {
|
|
219
|
+
return { done: true, value: undefined };
|
|
220
|
+
}
|
|
221
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Handle = number;
|
|
2
|
+
export type Ptr = number;
|
|
3
|
+
export interface Stats {
|
|
4
|
+
read: number,
|
|
5
|
+
write: number,
|
|
6
|
+
readTotalTime: number,
|
|
7
|
+
cacheHit: number,
|
|
8
|
+
cacheMiss: number,
|
|
9
|
+
cacheUsed: number,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface EmModule {
|
|
13
|
+
HEAPU8: Uint8Array,
|
|
14
|
+
};
|