sol-trade-sdk 0.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/README.md +390 -0
- package/dist/chunk-MMQAMIKR.mjs +3735 -0
- package/dist/chunk-NEZDFAYA.mjs +7744 -0
- package/dist/clients-VITWK7B6.mjs +1370 -0
- package/dist/index-1BK_FXsW.d.mts +2327 -0
- package/dist/index-1BK_FXsW.d.ts +2327 -0
- package/dist/index.d.mts +2659 -0
- package/dist/index.d.ts +2659 -0
- package/dist/index.js +13265 -0
- package/dist/index.mjs +562 -0
- package/dist/perf/index.d.mts +2 -0
- package/dist/perf/index.d.ts +2 -0
- package/dist/perf/index.js +3742 -0
- package/dist/perf/index.mjs +214 -0
- package/package.json +101 -0
- package/src/__tests__/complete_sdk.test.ts +354 -0
- package/src/__tests__/hotpath.test.ts +486 -0
- package/src/__tests__/nonce.test.ts +45 -0
- package/src/__tests__/sdk.test.ts +425 -0
- package/src/address-lookup/index.ts +197 -0
- package/src/cache/cache.ts +308 -0
- package/src/calc/index.ts +1058 -0
- package/src/calc/pumpfun.ts +124 -0
- package/src/common/bonding_curve.ts +272 -0
- package/src/common/compute-budget.ts +148 -0
- package/src/common/confirm-any-signature.ts +184 -0
- package/src/common/fast-timing.ts +481 -0
- package/src/common/fast_fn.ts +150 -0
- package/src/common/gas-fee-strategy.ts +253 -0
- package/src/common/map-pool.ts +23 -0
- package/src/common/nonce.ts +40 -0
- package/src/common/sdk-log.ts +460 -0
- package/src/common/seed.ts +381 -0
- package/src/common/spl-token.ts +578 -0
- package/src/common/subscription-handle.ts +644 -0
- package/src/common/trading-utils.ts +239 -0
- package/src/common/wsol-manager.ts +325 -0
- package/src/compute/compute_budget_manager.ts +187 -0
- package/src/compute/index.ts +21 -0
- package/src/constants/index.ts +96 -0
- package/src/execution/execution.ts +532 -0
- package/src/execution/index.ts +42 -0
- package/src/hotpath/executor.ts +464 -0
- package/src/hotpath/index.ts +64 -0
- package/src/hotpath/state.ts +435 -0
- package/src/index.ts +2117 -0
- package/src/instruction/bonk_builder.ts +730 -0
- package/src/instruction/index.ts +24 -0
- package/src/instruction/meteora_damm_v2_builder.ts +509 -0
- package/src/instruction/pumpfun_builder.ts +1183 -0
- package/src/instruction/pumpswap.ts +1123 -0
- package/src/instruction/raydium_amm_v4_builder.ts +692 -0
- package/src/instruction/raydium_cpmm_builder.ts +795 -0
- package/src/middleware/traits.ts +407 -0
- package/src/params/index.ts +483 -0
- package/src/perf/compiler-optimization.ts +529 -0
- package/src/perf/hardware.ts +631 -0
- package/src/perf/index.ts +9 -0
- package/src/perf/kernel-bypass.ts +656 -0
- package/src/perf/protocol.ts +682 -0
- package/src/perf/realtime.ts +592 -0
- package/src/perf/simd.ts +668 -0
- package/src/perf/syscall-bypass.ts +331 -0
- package/src/perf/ultra-low-latency.ts +505 -0
- package/src/perf/zero-copy.ts +589 -0
- package/src/pool/pool.ts +294 -0
- package/src/rpc/client.ts +345 -0
- package/src/sdk-errors.ts +13 -0
- package/src/security/index.ts +26 -0
- package/src/security/secure-key.ts +303 -0
- package/src/security/validators.ts +281 -0
- package/src/seed/pda.ts +262 -0
- package/src/serialization/index.ts +28 -0
- package/src/serialization/serialization.ts +288 -0
- package/src/swqos/clients.ts +1754 -0
- package/src/swqos/index.ts +50 -0
- package/src/swqos/providers.ts +1707 -0
- package/src/trading/core/async-executor.ts +702 -0
- package/src/trading/core/confirmation-monitor.ts +711 -0
- package/src/trading/core/index.ts +82 -0
- package/src/trading/core/retry-handler.ts +683 -0
- package/src/trading/core/transaction-pool.ts +780 -0
- package/src/trading/executor.ts +385 -0
- package/src/trading/factory.ts +282 -0
- package/src/trading/index.ts +30 -0
- package/src/types.ts +8 -0
- package/src/utils/index.ts +155 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel bypass optimizations for ultra-low latency I/O.
|
|
3
|
+
* Provides io_uring-like support for async I/O without kernel copies,
|
|
4
|
+
* along with fallback implementations for different platforms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ===== Types =====
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* I/O operation types
|
|
11
|
+
*/
|
|
12
|
+
export enum IOOperation {
|
|
13
|
+
READ = 'READ',
|
|
14
|
+
WRITE = 'WRITE',
|
|
15
|
+
FSYNC = 'FSYNC',
|
|
16
|
+
POLL = 'POLL',
|
|
17
|
+
TIMEOUT = 'TIMEOUT',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* I/O request for kernel bypass operations
|
|
22
|
+
*/
|
|
23
|
+
export interface IORequest {
|
|
24
|
+
op: IOOperation;
|
|
25
|
+
fd: number;
|
|
26
|
+
buffer?: Buffer;
|
|
27
|
+
offset: number;
|
|
28
|
+
size: number;
|
|
29
|
+
callback?: (id: number, data: Buffer | null, bytesTransferred: number) => void;
|
|
30
|
+
userData?: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* I/O result
|
|
35
|
+
*/
|
|
36
|
+
export interface IOResult {
|
|
37
|
+
requestId: number;
|
|
38
|
+
bytesTransferred: number;
|
|
39
|
+
buffer?: Buffer;
|
|
40
|
+
error?: Error;
|
|
41
|
+
userData?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* io_uring configuration
|
|
46
|
+
*/
|
|
47
|
+
export interface IOUringConfig {
|
|
48
|
+
queueDepth: number;
|
|
49
|
+
sqThreadIdle: number; // ms before SQ thread sleeps
|
|
50
|
+
sqThreadCpu: number; // CPU for SQ thread (-1 = any)
|
|
51
|
+
cqSize: number; // 0 = same as queueDepth
|
|
52
|
+
flags: number;
|
|
53
|
+
features: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Default io_uring configuration
|
|
58
|
+
*/
|
|
59
|
+
export function defaultIOUringConfig(): IOUringConfig {
|
|
60
|
+
return {
|
|
61
|
+
queueDepth: 256,
|
|
62
|
+
sqThreadIdle: 2000,
|
|
63
|
+
sqThreadCpu: -1,
|
|
64
|
+
cqSize: 0,
|
|
65
|
+
flags: 0,
|
|
66
|
+
features: [],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ===== Kernel Bypass Manager =====
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Manager for kernel bypass I/O operations.
|
|
74
|
+
* Uses optimized async I/O when available, falls back to standard async I/O.
|
|
75
|
+
*/
|
|
76
|
+
export class KernelBypassManager {
|
|
77
|
+
private config: IOUringConfig;
|
|
78
|
+
private uringAvailable: boolean = false;
|
|
79
|
+
private requestCounter: number = 0;
|
|
80
|
+
private pendingRequests: Map<number, IORequest> = new Map();
|
|
81
|
+
private running: boolean = false;
|
|
82
|
+
private processTimer: NodeJS.Timeout | null = null;
|
|
83
|
+
|
|
84
|
+
constructor(config?: IOUringConfig) {
|
|
85
|
+
this.config = config || defaultIOUringConfig();
|
|
86
|
+
this.checkUringAvailability();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if io_uring-like functionality is available
|
|
91
|
+
*/
|
|
92
|
+
private checkUringAvailability(): boolean {
|
|
93
|
+
// In Node.js, we don't have direct io_uring access,
|
|
94
|
+
// but we can use libuv's async I/O which is highly optimized
|
|
95
|
+
// Check for Linux platform
|
|
96
|
+
if (process.platform !== 'linux') {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// io_uring support would require native bindings
|
|
101
|
+
// For now, use optimized libuv fallback
|
|
102
|
+
this.uringAvailable = false;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if io_uring is available
|
|
108
|
+
*/
|
|
109
|
+
isUringAvailable(): boolean {
|
|
110
|
+
return this.uringAvailable;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Start the I/O processing loop
|
|
115
|
+
*/
|
|
116
|
+
start(): void {
|
|
117
|
+
if (this.running) return;
|
|
118
|
+
this.running = true;
|
|
119
|
+
|
|
120
|
+
// Start processing timer
|
|
121
|
+
this.processTimer = setInterval(() => {
|
|
122
|
+
this.processPendingRequests();
|
|
123
|
+
}, 0.1); // 100 microseconds
|
|
124
|
+
|
|
125
|
+
// Use setImmediate for faster processing
|
|
126
|
+
setImmediate(() => this.processLoop());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Stop the I/O processing loop
|
|
131
|
+
*/
|
|
132
|
+
stop(): void {
|
|
133
|
+
this.running = false;
|
|
134
|
+
if (this.processTimer) {
|
|
135
|
+
clearInterval(this.processTimer);
|
|
136
|
+
this.processTimer = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Main processing loop
|
|
142
|
+
*/
|
|
143
|
+
private processLoop(): void {
|
|
144
|
+
if (!this.running) return;
|
|
145
|
+
|
|
146
|
+
this.processPendingRequests();
|
|
147
|
+
|
|
148
|
+
if (this.running) {
|
|
149
|
+
setImmediate(() => this.processLoop());
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Process pending I/O requests
|
|
155
|
+
*/
|
|
156
|
+
private processPendingRequests(): void {
|
|
157
|
+
const toDelete: number[] = [];
|
|
158
|
+
this.pendingRequests.forEach((request, id) => {
|
|
159
|
+
switch (request.op) {
|
|
160
|
+
case IOOperation.READ:
|
|
161
|
+
this.processRead(id, request);
|
|
162
|
+
break;
|
|
163
|
+
case IOOperation.WRITE:
|
|
164
|
+
this.processWrite(id, request);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
toDelete.push(id);
|
|
168
|
+
});
|
|
169
|
+
toDelete.forEach((id) => this.pendingRequests.delete(id));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Process a read request
|
|
174
|
+
*/
|
|
175
|
+
private processRead(id: number, request: IORequest): void {
|
|
176
|
+
try {
|
|
177
|
+
const fs = require('fs');
|
|
178
|
+
const buffer = Buffer.alloc(request.size);
|
|
179
|
+
fs.read(request.fd, buffer, 0, request.size, request.offset, (err: Error | null, bytesRead: number) => {
|
|
180
|
+
if (request.callback) {
|
|
181
|
+
request.callback(id, err ? null : buffer.slice(0, bytesRead), bytesRead);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (request.callback) {
|
|
186
|
+
request.callback(id, null, 0);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Process a write request
|
|
193
|
+
*/
|
|
194
|
+
private processWrite(id: number, request: IORequest): void {
|
|
195
|
+
try {
|
|
196
|
+
const fs = require('fs');
|
|
197
|
+
fs.write(request.fd, request.buffer!, 0, request.buffer!.length, request.offset, (err: Error | null, bytesWritten: number) => {
|
|
198
|
+
if (request.callback) {
|
|
199
|
+
request.callback(id, null, bytesWritten);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (request.callback) {
|
|
204
|
+
request.callback(id, null, 0);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Submit an async read request
|
|
211
|
+
*/
|
|
212
|
+
submitRead(
|
|
213
|
+
fd: number,
|
|
214
|
+
size: number,
|
|
215
|
+
offset: number = 0,
|
|
216
|
+
callback?: (id: number, data: Buffer | null, bytesTransferred: number) => void,
|
|
217
|
+
userData?: unknown
|
|
218
|
+
): number {
|
|
219
|
+
const id = ++this.requestCounter;
|
|
220
|
+
|
|
221
|
+
const request: IORequest = {
|
|
222
|
+
op: IOOperation.READ,
|
|
223
|
+
fd,
|
|
224
|
+
offset,
|
|
225
|
+
size,
|
|
226
|
+
callback,
|
|
227
|
+
userData,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
this.pendingRequests.set(id, request);
|
|
231
|
+
return id;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Submit an async write request
|
|
236
|
+
*/
|
|
237
|
+
submitWrite(
|
|
238
|
+
fd: number,
|
|
239
|
+
buffer: Buffer,
|
|
240
|
+
offset: number = 0,
|
|
241
|
+
callback?: (id: number, data: Buffer | null, bytesTransferred: number) => void,
|
|
242
|
+
userData?: unknown
|
|
243
|
+
): number {
|
|
244
|
+
const id = ++this.requestCounter;
|
|
245
|
+
|
|
246
|
+
const request: IORequest = {
|
|
247
|
+
op: IOOperation.WRITE,
|
|
248
|
+
fd,
|
|
249
|
+
buffer,
|
|
250
|
+
offset,
|
|
251
|
+
size: buffer.length,
|
|
252
|
+
callback,
|
|
253
|
+
userData,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
this.pendingRequests.set(id, request);
|
|
257
|
+
return id;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ===== Direct I/O File =====
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* File with direct I/O support (bypassing page cache)
|
|
265
|
+
*/
|
|
266
|
+
export class DirectIOFile {
|
|
267
|
+
private path: string;
|
|
268
|
+
private fd: number | null = null;
|
|
269
|
+
private directIO: boolean = false;
|
|
270
|
+
|
|
271
|
+
constructor(path: string) {
|
|
272
|
+
this.path = path;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Open file with direct I/O if available
|
|
277
|
+
*/
|
|
278
|
+
async open(): Promise<boolean> {
|
|
279
|
+
const fs = require('fs').promises;
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
// Try to open with direct I/O flag (O_DIRECT on Linux)
|
|
283
|
+
// Note: Node.js doesn't directly support O_DIRECT, this is a placeholder
|
|
284
|
+
this.fd = await fs.open(this.path, 'r+');
|
|
285
|
+
this.directIO = process.platform === 'linux'; // Placeholder
|
|
286
|
+
return true;
|
|
287
|
+
} catch (error) {
|
|
288
|
+
// Fallback to normal open
|
|
289
|
+
try {
|
|
290
|
+
this.fd = await fs.open(this.path, 'w+');
|
|
291
|
+
return true;
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Close the file
|
|
300
|
+
*/
|
|
301
|
+
async close(): Promise<void> {
|
|
302
|
+
if (this.fd !== null) {
|
|
303
|
+
const fs = require('fs').promises;
|
|
304
|
+
await fs.close(this.fd);
|
|
305
|
+
this.fd = null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Read from file
|
|
311
|
+
*/
|
|
312
|
+
async read(size: number, offset: number = 0): Promise<Buffer> {
|
|
313
|
+
if (this.fd === null) {
|
|
314
|
+
throw new Error('File not open');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (this.directIO) {
|
|
318
|
+
return this.readDirect(size, offset);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const fs = require('fs').promises;
|
|
322
|
+
const buffer = Buffer.alloc(size);
|
|
323
|
+
await fs.read(this.fd, buffer, 0, size, offset);
|
|
324
|
+
return buffer;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Read using direct I/O with aligned buffer
|
|
329
|
+
*/
|
|
330
|
+
private async readDirect(size: number, offset: number): Promise<Buffer> {
|
|
331
|
+
const align = 512;
|
|
332
|
+
const alignedOffset = Math.floor(offset / align) * align;
|
|
333
|
+
const offsetDiff = offset - alignedOffset;
|
|
334
|
+
const alignedSize = Math.ceil((size + offsetDiff + align - 1) / align) * align;
|
|
335
|
+
|
|
336
|
+
// Allocate aligned buffer
|
|
337
|
+
const buffer = Buffer.alloc(alignedSize);
|
|
338
|
+
const fs = require('fs').promises;
|
|
339
|
+
await fs.read(this.fd!, buffer, 0, alignedSize, alignedOffset);
|
|
340
|
+
|
|
341
|
+
return buffer.slice(offsetDiff, offsetDiff + size);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Write to file
|
|
346
|
+
*/
|
|
347
|
+
async write(data: Buffer, offset: number = 0): Promise<number> {
|
|
348
|
+
if (this.fd === null) {
|
|
349
|
+
throw new Error('File not open');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const fs = require('fs').promises;
|
|
353
|
+
const { bytesWritten } = await fs.write(this.fd, data, 0, data.length, offset);
|
|
354
|
+
return bytesWritten;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Sync file to disk
|
|
359
|
+
*/
|
|
360
|
+
async fsync(): Promise<void> {
|
|
361
|
+
if (this.fd !== null) {
|
|
362
|
+
const fs = require('fs').promises;
|
|
363
|
+
await fs.fsync(this.fd);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ===== Memory Mapped File =====
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Memory-mapped file for zero-copy access
|
|
372
|
+
*/
|
|
373
|
+
export class MemoryMappedFile {
|
|
374
|
+
private path: string;
|
|
375
|
+
private fd: number | null = null;
|
|
376
|
+
private buffer: Buffer | null = null;
|
|
377
|
+
|
|
378
|
+
constructor(path: string) {
|
|
379
|
+
this.path = path;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Open and memory-map the file
|
|
384
|
+
*/
|
|
385
|
+
async open(size: number = 0): Promise<boolean> {
|
|
386
|
+
const fs = require('fs');
|
|
387
|
+
const fsPromises = fs.promises;
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const fileHandle = await fsPromises.open(this.path, 'r+');
|
|
391
|
+
this.fd = fileHandle.fd;
|
|
392
|
+
|
|
393
|
+
const stats = await fileHandle.stat();
|
|
394
|
+
let fileSize = stats.size;
|
|
395
|
+
|
|
396
|
+
if (size > 0 && size > fileSize) {
|
|
397
|
+
// Extend file
|
|
398
|
+
await fileHandle.truncate(size);
|
|
399
|
+
fileSize = size;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (fileSize === 0) {
|
|
403
|
+
fileSize = 4096; // Default size
|
|
404
|
+
await fileHandle.truncate(fileSize);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Memory map using Buffer (Node.js doesn't have true mmap, but we can simulate)
|
|
408
|
+
this.buffer = Buffer.alloc(fileSize);
|
|
409
|
+
await fileHandle.read(this.buffer, 0, fileSize, 0);
|
|
410
|
+
|
|
411
|
+
return true;
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Close the memory-mapped file
|
|
419
|
+
*/
|
|
420
|
+
async close(): Promise<void> {
|
|
421
|
+
if (this.buffer) {
|
|
422
|
+
// Flush changes
|
|
423
|
+
const fs = require('fs').promises;
|
|
424
|
+
if (this.fd !== null) {
|
|
425
|
+
await fs.write(this.fd, this.buffer, 0, this.buffer.length, 0);
|
|
426
|
+
}
|
|
427
|
+
this.buffer = null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (this.fd !== null) {
|
|
431
|
+
const fs = require('fs').promises;
|
|
432
|
+
await fs.close(this.fd);
|
|
433
|
+
this.fd = null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Read from memory-mapped file
|
|
439
|
+
*/
|
|
440
|
+
read(offset: number, size: number): Buffer {
|
|
441
|
+
if (!this.buffer) {
|
|
442
|
+
throw new Error('File not mapped');
|
|
443
|
+
}
|
|
444
|
+
return this.buffer.slice(offset, offset + size);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Write to memory-mapped file
|
|
449
|
+
*/
|
|
450
|
+
write(offset: number, data: Buffer): void {
|
|
451
|
+
if (!this.buffer) {
|
|
452
|
+
throw new Error('File not mapped');
|
|
453
|
+
}
|
|
454
|
+
data.copy(this.buffer, offset);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Flush changes to disk
|
|
459
|
+
*/
|
|
460
|
+
async flush(): Promise<void> {
|
|
461
|
+
if (this.buffer && this.fd !== null) {
|
|
462
|
+
const fs = require('fs').promises;
|
|
463
|
+
await fs.write(this.fd, this.buffer, 0, this.buffer.length, 0);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ===== Async Socket =====
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Async socket with kernel bypass optimizations
|
|
472
|
+
*/
|
|
473
|
+
export class AsyncSocket {
|
|
474
|
+
private socket: any = null;
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Set the underlying socket
|
|
478
|
+
*/
|
|
479
|
+
setSocket(socket: any): void {
|
|
480
|
+
this.socket = socket;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Enable kernel bypass optimizations for this socket
|
|
485
|
+
*/
|
|
486
|
+
enableKernelBypass(): boolean {
|
|
487
|
+
if (!this.socket) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
// Set TCP_NODELAY
|
|
493
|
+
this.socket.setNoDelay(true);
|
|
494
|
+
|
|
495
|
+
// Set keepalive
|
|
496
|
+
this.socket.setKeepAlive(true, 1000);
|
|
497
|
+
|
|
498
|
+
return true;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Async receive with optimization
|
|
506
|
+
*/
|
|
507
|
+
async recv(size: number): Promise<Buffer> {
|
|
508
|
+
return new Promise((resolve, reject) => {
|
|
509
|
+
if (!this.socket) {
|
|
510
|
+
reject(new Error('Socket not set'));
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this.socket.once('data', (data: Buffer) => {
|
|
515
|
+
resolve(data);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
this.socket.once('error', reject);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Async send with optimization
|
|
524
|
+
*/
|
|
525
|
+
async send(data: Buffer): Promise<void> {
|
|
526
|
+
return new Promise((resolve, reject) => {
|
|
527
|
+
if (!this.socket) {
|
|
528
|
+
reject(new Error('Socket not set'));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
this.socket.write(data, (err: Error | null | undefined) => {
|
|
533
|
+
if (err) {
|
|
534
|
+
reject(err);
|
|
535
|
+
} else {
|
|
536
|
+
resolve();
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ===== I/O Batch Processor =====
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Batch processor for I/O operations with kernel bypass
|
|
547
|
+
*/
|
|
548
|
+
export class IOBatchProcessor {
|
|
549
|
+
private maxBatchSize: number;
|
|
550
|
+
private readBatch: Array<{
|
|
551
|
+
fd: number;
|
|
552
|
+
size: number;
|
|
553
|
+
offset: number;
|
|
554
|
+
callback: (id: number, data: Buffer | null, bytesTransferred: number) => void;
|
|
555
|
+
}> = [];
|
|
556
|
+
private writeBatch: Array<{
|
|
557
|
+
fd: number;
|
|
558
|
+
data: Buffer;
|
|
559
|
+
offset: number;
|
|
560
|
+
callback: (id: number, data: Buffer | null, bytesTransferred: number) => void;
|
|
561
|
+
}> = [];
|
|
562
|
+
private manager: KernelBypassManager;
|
|
563
|
+
|
|
564
|
+
constructor(maxBatchSize: number = 32) {
|
|
565
|
+
this.maxBatchSize = maxBatchSize;
|
|
566
|
+
this.manager = new KernelBypassManager();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Add read to batch
|
|
571
|
+
*/
|
|
572
|
+
addRead(
|
|
573
|
+
fd: number,
|
|
574
|
+
size: number,
|
|
575
|
+
offset: number = 0,
|
|
576
|
+
callback: (id: number, data: Buffer | null, bytesTransferred: number) => void = () => {}
|
|
577
|
+
): void {
|
|
578
|
+
this.readBatch.push({ fd, size, offset, callback });
|
|
579
|
+
|
|
580
|
+
if (this.readBatch.length >= this.maxBatchSize) {
|
|
581
|
+
this.flushReads();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Add write to batch
|
|
587
|
+
*/
|
|
588
|
+
addWrite(
|
|
589
|
+
fd: number,
|
|
590
|
+
data: Buffer,
|
|
591
|
+
offset: number = 0,
|
|
592
|
+
callback: (id: number, data: Buffer | null, bytesTransferred: number) => void = () => {}
|
|
593
|
+
): void {
|
|
594
|
+
this.writeBatch.push({ fd, data, offset, callback });
|
|
595
|
+
|
|
596
|
+
if (this.writeBatch.length >= this.maxBatchSize) {
|
|
597
|
+
this.flushWrites();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Flush all pending reads
|
|
603
|
+
*/
|
|
604
|
+
flushReads(): number[] {
|
|
605
|
+
if (this.readBatch.length === 0) {
|
|
606
|
+
return [];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const requestIds = this.readBatch.map((req) =>
|
|
610
|
+
this.manager.submitRead(req.fd, req.size, req.offset, req.callback)
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
this.readBatch = [];
|
|
614
|
+
return requestIds;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Flush all pending writes
|
|
619
|
+
*/
|
|
620
|
+
flushWrites(): number[] {
|
|
621
|
+
if (this.writeBatch.length === 0) {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const requestIds = this.writeBatch.map((req) =>
|
|
626
|
+
this.manager.submitWrite(req.fd, req.data, req.offset, req.callback)
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
this.writeBatch = [];
|
|
630
|
+
return requestIds;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Flush all pending operations
|
|
635
|
+
*/
|
|
636
|
+
flushAll(): { readIds: number[]; writeIds: number[] } {
|
|
637
|
+
return {
|
|
638
|
+
readIds: this.flushReads(),
|
|
639
|
+
writeIds: this.flushWrites(),
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ===== Global Manager =====
|
|
645
|
+
|
|
646
|
+
let globalKBManager: KernelBypassManager | null = null;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Get or create global kernel bypass manager
|
|
650
|
+
*/
|
|
651
|
+
export function getKernelBypassManager(config?: IOUringConfig): KernelBypassManager {
|
|
652
|
+
if (!globalKBManager) {
|
|
653
|
+
globalKBManager = new KernelBypassManager(config);
|
|
654
|
+
}
|
|
655
|
+
return globalKBManager;
|
|
656
|
+
}
|