socket-function 1.0.4 → 1.0.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/index.d.ts +20 -1
- package/mobx/observer.d.ts +1 -1
- package/mobx/observer.tsx +5 -2
- package/package.json +1 -1
- package/require/RequireController.ts +1 -1
- package/require/require.ts +1 -1
- package/src/CallFactory.ts +83 -55
- package/src/Zip.d.ts +14 -0
- package/src/Zip.ts +113 -0
- package/src/profiling/measure.d.ts +1 -0
- package/src/profiling/measure.ts +3 -0
package/index.d.ts
CHANGED
|
@@ -262,7 +262,7 @@ declare module "socket-function/mobx/UrlParam" {
|
|
|
262
262
|
declare module "socket-function/mobx/observer" {
|
|
263
263
|
import type preact from "preact";
|
|
264
264
|
import { Reaction } from "mobx";
|
|
265
|
-
export declare function observer<T extends {
|
|
265
|
+
/** @deprecated Use the version from sliftutils instead. */ export declare function observer<T extends {
|
|
266
266
|
new (...args: any[]): {
|
|
267
267
|
render(): preact.ComponentChild;
|
|
268
268
|
forceUpdate(callback?: () => void): void;
|
|
@@ -528,6 +528,24 @@ declare module "socket-function/src/JSONLACKS/JSONLACKS.generated.js" {
|
|
|
528
528
|
export function parse(text: string): unknown;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
+
declare module "socket-function/src/Zip" {
|
|
532
|
+
/// <reference path="../index.d.ts" />
|
|
533
|
+
/// <reference types="node" />
|
|
534
|
+
/// <reference types="node" />
|
|
535
|
+
import { MaybePromise } from "socket-function/src/types";
|
|
536
|
+
export declare class Zip {
|
|
537
|
+
static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
|
|
538
|
+
static gzipSync(buffer: Buffer, level?: number): Buffer;
|
|
539
|
+
static gunzip(buffer: Buffer): MaybePromise<Buffer>;
|
|
540
|
+
static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
|
|
541
|
+
static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
|
|
542
|
+
static gunzipSync(buffer: Buffer): Buffer;
|
|
543
|
+
private static gunzipUntrackedSync;
|
|
544
|
+
static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
}
|
|
548
|
+
|
|
531
549
|
declare module "socket-function/src/args" {
|
|
532
550
|
export declare const getArgs: {
|
|
533
551
|
(): {
|
|
@@ -1042,6 +1060,7 @@ declare module "socket-function/src/profiling/measure" {
|
|
|
1042
1060
|
entries: {
|
|
1043
1061
|
[name: string]: ProfileEntry;
|
|
1044
1062
|
};
|
|
1063
|
+
creator: string;
|
|
1045
1064
|
}
|
|
1046
1065
|
export declare function createMeasureProfile(): MeasureProfile;
|
|
1047
1066
|
export declare function addToMeasureProfile(base: MeasureProfile, other: MeasureProfile): void;
|
package/mobx/observer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type preact from "preact";
|
|
2
2
|
import { Reaction } from "mobx";
|
|
3
|
-
export declare function observer<T extends {
|
|
3
|
+
/** @deprecated Use the version from sliftutils instead. */ export declare function observer<T extends {
|
|
4
4
|
new (...args: any[]): {
|
|
5
5
|
render(): preact.ComponentChild;
|
|
6
6
|
forceUpdate(callback?: () => void): void;
|
package/mobx/observer.tsx
CHANGED
|
@@ -2,13 +2,14 @@ import type preact from "preact";
|
|
|
2
2
|
import { setFlag } from "../require/compileFlags";
|
|
3
3
|
|
|
4
4
|
import { observable, Reaction } from "mobx";
|
|
5
|
+
import { measureBlock } from "socket-function/src/profiling/measure";
|
|
5
6
|
|
|
6
7
|
setFlag(require, "mobx", "allowclient", true);
|
|
7
8
|
setFlag(require, "preact", "allowclient", true);
|
|
8
9
|
|
|
9
10
|
let globalConstructOrder = 0;
|
|
10
11
|
|
|
11
|
-
export function observer<
|
|
12
|
+
/** @deprecated Use the version from sliftutils instead. */export function observer<
|
|
12
13
|
T extends {
|
|
13
14
|
new(...args: any[]): {
|
|
14
15
|
render(): preact.ComponentChild;
|
|
@@ -42,7 +43,9 @@ export function observer<
|
|
|
42
43
|
render() {
|
|
43
44
|
let output: preact.ComponentChild;
|
|
44
45
|
this.reaction.track(() => {
|
|
45
|
-
|
|
46
|
+
measureBlock(() => {
|
|
47
|
+
output = super.render();
|
|
48
|
+
}, `${name}|render`);
|
|
46
49
|
});
|
|
47
50
|
return output;
|
|
48
51
|
}
|
package/package.json
CHANGED
|
@@ -126,7 +126,7 @@ class RequireControllerBase {
|
|
|
126
126
|
}
|
|
127
127
|
if (httpRequest) {
|
|
128
128
|
let urlObj = new URL("https://" + httpRequest.headers.host + httpRequest.url);
|
|
129
|
-
if (urlObj.pathname !== "/" &&
|
|
129
|
+
if (urlObj.pathname !== "/" && staticRoots.length > 0) {
|
|
130
130
|
if (urlObj.pathname.includes("..")) {
|
|
131
131
|
throw new Error(`Invalid path, may not have ".." in the path, ${urlObj.pathname}`);
|
|
132
132
|
}
|
package/require/require.ts
CHANGED
|
@@ -659,7 +659,7 @@ export function requireMain() {
|
|
|
659
659
|
// NOTE: All on one line, so we don't break sourcemaps by TOO much. We could also parse
|
|
660
660
|
// the sourcemap and adjust it, but... it is much easier to just not change the line counts.
|
|
661
661
|
return eval(
|
|
662
|
-
`(function ${debugName}(exports, require, module, __filename, __dirname, importDynamic) {
|
|
662
|
+
`(function ${debugName}(exports, require, module, __filename, __dirname, importDynamic) {"use strict";${contents}\n })`
|
|
663
663
|
);
|
|
664
664
|
}
|
|
665
665
|
|
package/src/CallFactory.ts
CHANGED
|
@@ -15,8 +15,9 @@ import { formatNumber, formatTime } from "./formatting/format";
|
|
|
15
15
|
import zlib from "zlib";
|
|
16
16
|
import pako from "pako";
|
|
17
17
|
import { setFlag } from "../require/compileFlags";
|
|
18
|
-
import { measureFnc, measureWrap } from "./profiling/measure";
|
|
18
|
+
import { measureFnc, measureWrap, registerMeasureInfo } from "./profiling/measure";
|
|
19
19
|
import { MaybePromise } from "./types";
|
|
20
|
+
import { Zip } from "./Zip";
|
|
20
21
|
setFlag(require, "pako", "allowclient", true);
|
|
21
22
|
|
|
22
23
|
// NOTE: If it is too low, and too many servers disconnect, we can easily spend 100% of our time
|
|
@@ -158,9 +159,13 @@ export async function createCallFactory(
|
|
|
158
159
|
let data: Buffer[];
|
|
159
160
|
let originalArgs = call.args;
|
|
160
161
|
let time = Date.now();
|
|
162
|
+
let sendStats: CompressionStats = {
|
|
163
|
+
uncompressedSize: 0,
|
|
164
|
+
compressedSize: 0,
|
|
165
|
+
};
|
|
161
166
|
try {
|
|
162
167
|
if (shouldCompressCall(fullCall)) {
|
|
163
|
-
fullCall.args = await compressObj(fullCall.args) as any;
|
|
168
|
+
fullCall.args = await compressObj(fullCall.args, sendStats) as any;
|
|
164
169
|
fullCall.isArgsCompressed = true;
|
|
165
170
|
}
|
|
166
171
|
let dataMaybePromise = SocketFunction.WIRE_SERIALIZER.serialize(fullCall);
|
|
@@ -169,6 +174,15 @@ export async function createCallFactory(
|
|
|
169
174
|
} else {
|
|
170
175
|
data = dataMaybePromise;
|
|
171
176
|
}
|
|
177
|
+
if (!sendStats.compressedSize) {
|
|
178
|
+
let totalSize = 0;
|
|
179
|
+
for (let d of data) {
|
|
180
|
+
totalSize += d.length;
|
|
181
|
+
}
|
|
182
|
+
sendStats.uncompressedSize = totalSize;
|
|
183
|
+
sendStats.compressedSize = totalSize;
|
|
184
|
+
}
|
|
185
|
+
addSendStats(sendStats);
|
|
172
186
|
} catch (e: any) {
|
|
173
187
|
throw new Error(`Error serializing data for call ${call.classGuid}.${call.functionName}\n${e.stack}`);
|
|
174
188
|
}
|
|
@@ -475,6 +489,15 @@ export async function createCallFactory(
|
|
|
475
489
|
callback(resultSize);
|
|
476
490
|
}
|
|
477
491
|
|
|
492
|
+
let receiveStats: CompressionStats = {
|
|
493
|
+
uncompressedSize: resultSize,
|
|
494
|
+
compressedSize: resultSize,
|
|
495
|
+
};
|
|
496
|
+
let sendStats: CompressionStats = {
|
|
497
|
+
uncompressedSize: 0,
|
|
498
|
+
compressedSize: 0,
|
|
499
|
+
};
|
|
500
|
+
|
|
478
501
|
if (call.isReturn) {
|
|
479
502
|
let callbackObj = pendingCalls.get(call.seqNum);
|
|
480
503
|
if (parseTime > SocketFunction.WIRE_WARN_TIME) {
|
|
@@ -489,13 +512,13 @@ export async function createCallFactory(
|
|
|
489
512
|
console.log(`SIZE\t${(formatNumberSuffixed(resultSize) + "B").padEnd(4, " ")}\tRETURN\t${call.classGuid}.${call.functionName} at ${Date.now()}, (${nodeId} / ${localNodeId})`);
|
|
490
513
|
}
|
|
491
514
|
if (call.isResultCompressed) {
|
|
492
|
-
call.result = await decompressObj(call.result as Buffer);
|
|
515
|
+
call.result = await decompressObj(call.result as Buffer, receiveStats);
|
|
493
516
|
call.isResultCompressed = false;
|
|
494
517
|
}
|
|
495
518
|
callbackObj.callback(call);
|
|
496
519
|
} else {
|
|
497
520
|
if (call.isArgsCompressed) {
|
|
498
|
-
call.args = await decompressObj(call.args as any as Buffer) as any;
|
|
521
|
+
call.args = await decompressObj(call.args as any as Buffer, sendStats) as any;
|
|
499
522
|
call.isArgsCompressed = false;
|
|
500
523
|
}
|
|
501
524
|
if (call.functionName === "changeIdentity") {
|
|
@@ -592,7 +615,7 @@ export async function createCallFactory(
|
|
|
592
615
|
console.log(`DUR\t${(formatTime(timeTaken)).padEnd(6, " ")}\tFINISH\t${call.classGuid}.${call.functionName} at ${Date.now()}, (${nodeId} / ${localNodeId})`);
|
|
593
616
|
}
|
|
594
617
|
if (shouldCompressCall(call)) {
|
|
595
|
-
response.result = await compressObj(response.result) as any;
|
|
618
|
+
response.result = await compressObj(response.result, sendStats) as any;
|
|
596
619
|
response.isResultCompressed = true;
|
|
597
620
|
}
|
|
598
621
|
} catch (e: any) {
|
|
@@ -611,11 +634,17 @@ export async function createCallFactory(
|
|
|
611
634
|
}
|
|
612
635
|
}
|
|
613
636
|
|
|
637
|
+
let size = 0;
|
|
638
|
+
|
|
614
639
|
if (response.result instanceof Buffer) {
|
|
615
640
|
let { result, ...remaining } = response;
|
|
641
|
+
size = result.length;
|
|
616
642
|
await sendWithHeader([result], { type: "Buffer", bufferCount: 1, metadata: remaining });
|
|
617
643
|
} else if (Array.isArray(response.result) && response.result.every(x => x instanceof Buffer)) {
|
|
618
644
|
let { result, ...remaining } = response;
|
|
645
|
+
for (let r of result) {
|
|
646
|
+
size += r.length;
|
|
647
|
+
}
|
|
619
648
|
await sendWithHeader(result, { type: "Buffer[]", bufferCount: result.length, metadata: remaining });
|
|
620
649
|
} else {
|
|
621
650
|
const LIMIT = getCallFlags(call)?.responseLimit || SocketFunction.MAX_MESSAGE_SIZE * 1.5;
|
|
@@ -630,9 +659,21 @@ export async function createCallFactory(
|
|
|
630
659
|
};
|
|
631
660
|
result = await SocketFunction.WIRE_SERIALIZER.serialize(response);
|
|
632
661
|
}
|
|
662
|
+
for (let r of result) {
|
|
663
|
+
size += r.length;
|
|
664
|
+
}
|
|
633
665
|
await send(result);
|
|
634
666
|
}
|
|
667
|
+
|
|
668
|
+
// If we have no size, then it's probably uncompressed
|
|
669
|
+
if (!sendStats.compressedSize) {
|
|
670
|
+
sendStats.compressedSize = size;
|
|
671
|
+
sendStats.uncompressedSize = size;
|
|
672
|
+
}
|
|
635
673
|
}
|
|
674
|
+
|
|
675
|
+
addSendStats(sendStats);
|
|
676
|
+
addReceiveStats(receiveStats);
|
|
636
677
|
}
|
|
637
678
|
|
|
638
679
|
let clientsideSerial = runInSerial(async <T>(val: Promise<T>) => val);
|
|
@@ -717,63 +758,49 @@ export async function createCallFactory(
|
|
|
717
758
|
return callFactory;
|
|
718
759
|
}
|
|
719
760
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
outputBuffers.push(Buffer.from(value));
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
async function unzipBase(buffer: Buffer): Promise<Buffer> {
|
|
738
|
-
if (isNode()) {
|
|
739
|
-
return new Promise((resolve, reject) => {
|
|
740
|
-
zlib.gunzip(buffer, (err: any, result: Buffer) => {
|
|
741
|
-
if (err) reject(err);
|
|
742
|
-
else resolve(result);
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
} else {
|
|
746
|
-
// NOTE: pako seems to be faster, at least clientside.
|
|
747
|
-
// TIMING: 700ms vs 1200ms
|
|
748
|
-
// - This might just be faster for small files.
|
|
749
|
-
return Buffer.from(pako.inflate(buffer));
|
|
750
|
-
// @ts-ignore
|
|
751
|
-
// return await doStream(new DecompressionStream("gzip"), buffer);
|
|
752
|
-
}
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
let uncompressedSent = 0;
|
|
764
|
+
let compressedSent = 0;
|
|
765
|
+
let uncompressedReceived = 0;
|
|
766
|
+
let compressedReceived = 0;
|
|
767
|
+
|
|
768
|
+
let sendCount = 0;
|
|
769
|
+
let receiveCount = 0;
|
|
770
|
+
|
|
771
|
+
function addSendStats(stats: CompressionStats) {
|
|
772
|
+
uncompressedSent += stats.uncompressedSize;
|
|
773
|
+
compressedSent += stats.compressedSize;
|
|
774
|
+
sendCount++;
|
|
753
775
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
if (err) reject(err);
|
|
759
|
-
else resolve(result);
|
|
760
|
-
});
|
|
761
|
-
});
|
|
762
|
-
} else {
|
|
763
|
-
// @ts-ignore
|
|
764
|
-
return await doStream(new CompressionStream("gzip"), buffer);
|
|
765
|
-
}
|
|
776
|
+
function addReceiveStats(stats: CompressionStats) {
|
|
777
|
+
uncompressedReceived += stats.uncompressedSize;
|
|
778
|
+
compressedReceived += stats.compressedSize;
|
|
779
|
+
receiveCount++;
|
|
766
780
|
}
|
|
781
|
+
// Register this late as I don't want it to appear before the memory register info, which is use more useful than the network one.
|
|
782
|
+
setImmediate(() => {
|
|
783
|
+
registerMeasureInfo(() => `NET => ${formatNumber(compressedSent)}B (${formatNumber(uncompressedSent)}B/${formatNumber(sendCount)}) / <= ${formatNumber(compressedReceived)}B (${formatNumber(uncompressedReceived)}B/${formatNumber(receiveCount)})`);
|
|
784
|
+
});
|
|
767
785
|
|
|
768
|
-
|
|
786
|
+
type CompressionStats = {
|
|
787
|
+
uncompressedSize: number;
|
|
788
|
+
compressedSize: number;
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
const compressObj = measureWrap(async function wireCallCompress(obj: unknown, stats: CompressionStats): Promise<Buffer> {
|
|
769
792
|
let buffers = await SocketFunction.WIRE_SERIALIZER.serialize(obj);
|
|
770
793
|
let lengthBuffer = Buffer.from((new Float64Array(buffers.map(x => x.length))).buffer);
|
|
771
794
|
let buffer = Buffer.concat([lengthBuffer, ...buffers]);
|
|
772
|
-
|
|
795
|
+
stats.uncompressedSize += buffer.length;
|
|
796
|
+
let result = await Zip.gzip(buffer);
|
|
797
|
+
stats.compressedSize += result.length;
|
|
773
798
|
return result;
|
|
774
799
|
});
|
|
775
|
-
|
|
776
|
-
|
|
800
|
+
// Assumes the caller already added the obj.length to both the uncompressedSize and compressedSize, and is just looking to update the uncompressedSize to be larger according to the size after we uncompress
|
|
801
|
+
const decompressObj = measureWrap(async function wireCallDecompress(obj: Buffer, stats: CompressionStats): Promise<unknown> {
|
|
802
|
+
let buffer = await Zip.gunzip(obj);
|
|
803
|
+
stats.uncompressedSize += buffer.length - obj.length;
|
|
777
804
|
let lengthBuffer = buffer.slice(0, 8);
|
|
778
805
|
let lengths = new Float64Array(lengthBuffer.buffer, lengthBuffer.byteOffset, lengthBuffer.byteLength / 8);
|
|
779
806
|
let buffers: Buffer[] = [];
|
|
@@ -782,5 +809,6 @@ const decompressObj = measureWrap(async function wireCallDecompress(obj: Buffer)
|
|
|
782
809
|
buffers.push(buffer.slice(offset, offset + length));
|
|
783
810
|
offset += length;
|
|
784
811
|
}
|
|
785
|
-
|
|
812
|
+
let result = await SocketFunction.WIRE_SERIALIZER.deserialize(buffers);
|
|
813
|
+
return result;
|
|
786
814
|
});
|
package/src/Zip.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference path="../index.d.ts" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
import { MaybePromise } from "socket-function/src/types";
|
|
5
|
+
export declare class Zip {
|
|
6
|
+
static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
|
|
7
|
+
static gzipSync(buffer: Buffer, level?: number): Buffer;
|
|
8
|
+
static gunzip(buffer: Buffer): MaybePromise<Buffer>;
|
|
9
|
+
static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
|
|
10
|
+
static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
|
|
11
|
+
static gunzipSync(buffer: Buffer): Buffer;
|
|
12
|
+
private static gunzipUntrackedSync;
|
|
13
|
+
static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
|
|
14
|
+
}
|
package/src/Zip.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { isNode } from "socket-function/src/misc";
|
|
2
|
+
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
3
|
+
import zlib from "zlib";
|
|
4
|
+
import * as pako from "pako";
|
|
5
|
+
|
|
6
|
+
import { setFlag } from "socket-function/require/compileFlags";
|
|
7
|
+
import { MaybePromise } from "socket-function/src/types";
|
|
8
|
+
setFlag(require, "pako", "allowclient", true);
|
|
9
|
+
|
|
10
|
+
const SYNC_THRESHOLD_BYTES = 100_000_000;
|
|
11
|
+
|
|
12
|
+
export class Zip {
|
|
13
|
+
@measureFnc
|
|
14
|
+
public static async gzip(buffer: Buffer, level?: number): Promise<Buffer> {
|
|
15
|
+
if (isNode()) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
zlib.gzip(buffer, { level }, (err: any, result: Buffer) => {
|
|
18
|
+
if (err) reject(err);
|
|
19
|
+
else resolve(result);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
return await doStream(new CompressionStream("gzip"), buffer);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
@measureFnc
|
|
28
|
+
public static gzipSync(buffer: Buffer, level?: number): Buffer {
|
|
29
|
+
if (isNode()) {
|
|
30
|
+
return Buffer.from(zlib.gzipSync(buffer, { level }));
|
|
31
|
+
}
|
|
32
|
+
return Buffer.from(pako.deflate(buffer));
|
|
33
|
+
}
|
|
34
|
+
public static gunzip(buffer: Buffer): MaybePromise<Buffer> {
|
|
35
|
+
// Switch to the synchronous version if the buffer is small. This is a lot faster in Node.js and clientside.
|
|
36
|
+
// - On tests of random small amounts of data, this seems to be up to 7X faster (on node). However, on non-random data, on the actual data we're using, it seems to be almost 50 times faster. So... definitely worth it...
|
|
37
|
+
if (buffer.length < SYNC_THRESHOLD_BYTES) {
|
|
38
|
+
let time = Date.now();
|
|
39
|
+
let result = Zip.gunzipSync(buffer);
|
|
40
|
+
let duration = Date.now() - time;
|
|
41
|
+
if (duration > 50) {
|
|
42
|
+
// Wait, so we don't lock up the main thread. And if we already wait it 50ms, then waiting for one frame is marginal, even client-side.
|
|
43
|
+
return ((async () => {
|
|
44
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
45
|
+
return result;
|
|
46
|
+
}))();
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
return Zip.gunzipAsyncBase(buffer);
|
|
51
|
+
}
|
|
52
|
+
@measureFnc
|
|
53
|
+
public static async gunzipAsyncBase(buffer: Buffer): Promise<Buffer> {
|
|
54
|
+
return Zip.gunzipUntracked(buffer);
|
|
55
|
+
}
|
|
56
|
+
// A base function, so we can avoid instrumentation for batch calls
|
|
57
|
+
public static async gunzipUntracked(buffer: Buffer): Promise<Buffer> {
|
|
58
|
+
if (isNode()) {
|
|
59
|
+
return await new Promise<Buffer>((resolve, reject) => {
|
|
60
|
+
zlib.gunzip(buffer, (err: any, result: Buffer) => {
|
|
61
|
+
if (err) reject(err);
|
|
62
|
+
else resolve(result);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return await doStream(new DecompressionStream("gzip"), buffer);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@measureFnc
|
|
70
|
+
public static gunzipSync(buffer: Buffer): Buffer {
|
|
71
|
+
return this.gunzipUntrackedSync(buffer);
|
|
72
|
+
}
|
|
73
|
+
private static gunzipUntrackedSync(buffer: Buffer): Buffer {
|
|
74
|
+
if (isNode()) {
|
|
75
|
+
return Buffer.from(zlib.gunzipSync(buffer));
|
|
76
|
+
}
|
|
77
|
+
return Buffer.from(pako.inflate(buffer));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@measureFnc
|
|
81
|
+
public static async gunzipBatch(buffers: Buffer[]): Promise<Buffer[]> {
|
|
82
|
+
let time = Date.now();
|
|
83
|
+
buffers = await Promise.all(buffers.map(x => {
|
|
84
|
+
if (x.length < SYNC_THRESHOLD_BYTES) {
|
|
85
|
+
return this.gunzipUntrackedSync(x);
|
|
86
|
+
}
|
|
87
|
+
return this.gunzipUntracked(x);
|
|
88
|
+
}));
|
|
89
|
+
time = Date.now() - time;
|
|
90
|
+
let totalSize = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
|
|
91
|
+
//console.log(`Gunzip ${formatNumber(totalSize)}B at ${formatNumber(totalSize / time * 1000)}B/s`);
|
|
92
|
+
return buffers;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function doStream(stream: GenericTransformStream, buffer: Buffer): Promise<Buffer> {
|
|
97
|
+
let reader = stream.readable.getReader();
|
|
98
|
+
let writer = stream.writable.getWriter();
|
|
99
|
+
let writePromise = writer.write(buffer);
|
|
100
|
+
let closePromise = writer.close();
|
|
101
|
+
|
|
102
|
+
let outputBuffers: Buffer[] = [];
|
|
103
|
+
while (true) {
|
|
104
|
+
let { value, done } = await reader.read();
|
|
105
|
+
if (done) {
|
|
106
|
+
await writePromise;
|
|
107
|
+
await closePromise;
|
|
108
|
+
return Buffer.concat(outputBuffers);
|
|
109
|
+
}
|
|
110
|
+
outputBuffers.push(Buffer.from(value));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
@@ -47,6 +47,7 @@ export interface MeasureProfile {
|
|
|
47
47
|
entries: {
|
|
48
48
|
[name: string]: ProfileEntry;
|
|
49
49
|
};
|
|
50
|
+
creator: string;
|
|
50
51
|
}
|
|
51
52
|
export declare function createMeasureProfile(): MeasureProfile;
|
|
52
53
|
export declare function addToMeasureProfile(base: MeasureProfile, other: MeasureProfile): void;
|
package/src/profiling/measure.ts
CHANGED
|
@@ -100,6 +100,7 @@ export function startMeasure(): {
|
|
|
100
100
|
startTime: now,
|
|
101
101
|
endTime: now,
|
|
102
102
|
entries: Object.create(null),
|
|
103
|
+
creator: new Error().stack || "",
|
|
103
104
|
};
|
|
104
105
|
let openAtStart = new Set(getOpenTimesBase());
|
|
105
106
|
|
|
@@ -316,6 +317,7 @@ export interface MeasureProfile {
|
|
|
316
317
|
entries: {
|
|
317
318
|
[name: string]: ProfileEntry;
|
|
318
319
|
};
|
|
320
|
+
creator: string;
|
|
319
321
|
}
|
|
320
322
|
export function createMeasureProfile(): MeasureProfile {
|
|
321
323
|
let now = Date.now();
|
|
@@ -323,6 +325,7 @@ export function createMeasureProfile(): MeasureProfile {
|
|
|
323
325
|
startTime: now,
|
|
324
326
|
endTime: now,
|
|
325
327
|
entries: Object.create(null),
|
|
328
|
+
creator: new Error().stack || "",
|
|
326
329
|
};
|
|
327
330
|
}
|
|
328
331
|
|