mirror of
https://github.com/hanzoai/zap-es.git
synced 2026-10-30 18:17:07 +00:00
247 lines
5.4 KiB
TypeScript
247 lines
5.4 KiB
TypeScript
// Based on https://github.com/jdiaz5513/capnp-ts (MIT - Julián Díaz)
|
|
|
|
import { test, assert as t } from "vitest";
|
|
import { compareBuffers, readFileBuffer } from "test/utils";
|
|
import * as C from "src/constants";
|
|
import { Message } from "src/serialization";
|
|
import { MultiSegmentArena } from "src/serialization/arena";
|
|
import {
|
|
getFramedSegments,
|
|
preallocateSegments,
|
|
} from "src/serialization/message";
|
|
|
|
import { Person } from "test/fixtures/serialization-demo";
|
|
|
|
const SEGMENTED_PACKED = readFileBuffer(
|
|
"test/fixtures/data/segmented-packed.bin",
|
|
);
|
|
const SEGMENTED_UNPACKED = readFileBuffer("test/fixtures/data/segmented.bin");
|
|
|
|
test("new Message(ArrayBuffer, false)", () => {
|
|
const message = new Message(SEGMENTED_UNPACKED, false);
|
|
|
|
compareBuffers(
|
|
message.toArrayBuffer(),
|
|
SEGMENTED_UNPACKED,
|
|
"should read segmented messages",
|
|
);
|
|
});
|
|
|
|
test("new Message(Buffer, false)", () => {
|
|
const message = new Message(Buffer.from(SEGMENTED_UNPACKED), false);
|
|
|
|
compareBuffers(
|
|
message.toArrayBuffer(),
|
|
SEGMENTED_UNPACKED,
|
|
"should read messages from a Buffer",
|
|
);
|
|
});
|
|
|
|
test("new Message(ArrayBuffer)", () => {
|
|
const message = new Message(SEGMENTED_PACKED);
|
|
|
|
compareBuffers(
|
|
message.toArrayBuffer(),
|
|
SEGMENTED_UNPACKED,
|
|
"should read packed messages",
|
|
);
|
|
});
|
|
|
|
test("new Message(Buffer)", () => {
|
|
const message = new Message(Buffer.from(SEGMENTED_PACKED));
|
|
|
|
compareBuffers(
|
|
message.toArrayBuffer(),
|
|
SEGMENTED_UNPACKED,
|
|
"should read packed messages from a Buffer",
|
|
);
|
|
});
|
|
|
|
test("getFramedSegments()", () => {
|
|
t.throws(
|
|
() =>
|
|
getFramedSegments(
|
|
new Uint8Array([
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00, // need at least 4 more bytes for an empty message
|
|
]).buffer,
|
|
),
|
|
// "should throw when segment counts are missing",
|
|
);
|
|
|
|
t.throws(
|
|
() =>
|
|
getFramedSegments(
|
|
new Uint8Array([
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x01,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00, // need at least 4 more bytes for the second segment length
|
|
]).buffer,
|
|
),
|
|
// "should throw when there are not enough segment counts",
|
|
);
|
|
|
|
t.throws(
|
|
() =>
|
|
getFramedSegments(
|
|
new Uint8Array([
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x10,
|
|
0x00,
|
|
0x00,
|
|
0x00, // should have 16 words in a single segment
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00, // but only get 2
|
|
]).buffer,
|
|
),
|
|
// "should throw when message is truncated",
|
|
);
|
|
});
|
|
|
|
test("Message.allocateSegment()", () => {
|
|
const length = C.DEFAULT_BUFFER_SIZE;
|
|
|
|
const m1 = new Message();
|
|
|
|
m1.allocateSegment(length);
|
|
m1.allocateSegment(length);
|
|
|
|
t.throws(() => m1.getSegment(1));
|
|
|
|
// Single segment arenas always grow by slightly more than what was allocated.
|
|
|
|
t.equal(
|
|
m1.getSegment(0).buffer.byteLength,
|
|
length * 2 + C.MIN_SINGLE_SEGMENT_GROWTH,
|
|
"should replace existing segments",
|
|
);
|
|
|
|
const m2 = new Message(new MultiSegmentArena([]));
|
|
|
|
m2.allocateSegment(length);
|
|
m2.allocateSegment(length);
|
|
|
|
t.equal(
|
|
m2.getSegment(1).buffer.byteLength,
|
|
length,
|
|
"should allocate new segments",
|
|
);
|
|
});
|
|
|
|
test("Message.dump()", () => {
|
|
const m1 = new Message(new MultiSegmentArena([]));
|
|
|
|
t.equal(
|
|
m1.dump(),
|
|
`================
|
|
No Segments
|
|
================
|
|
`,
|
|
"should print an empty message",
|
|
);
|
|
|
|
const m2 = new Message();
|
|
|
|
m2.allocateSegment(16).allocate(16);
|
|
|
|
t.equal(
|
|
m2.dump(),
|
|
`================
|
|
Segment #0
|
|
================
|
|
|
|
=== buffer[16] ===
|
|
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ················
|
|
`,
|
|
"should print messages",
|
|
);
|
|
});
|
|
|
|
test("Message.getSegment()", () => {
|
|
const s = new Message(new MultiSegmentArena([])).getSegment(0);
|
|
|
|
t.equal(s.byteLength, 8, "should preallocate segment 0");
|
|
|
|
t.throws(
|
|
() => new Message().getSegment(1),
|
|
// "should throw when getting out of range segments",
|
|
);
|
|
|
|
t.throws(
|
|
// this is too small to hold the root pointer
|
|
() =>
|
|
new Message(new MultiSegmentArena([new ArrayBuffer(2)])).getSegment(0),
|
|
// "should throw when segment 0 is too small",
|
|
);
|
|
});
|
|
|
|
test("Message.onCreatePointer()", () => {
|
|
// This is why you should cache the result of `getList()` calls and use `List.toArray()` liberally...
|
|
const m = new Message();
|
|
m._zap.traversalLimit = 100;
|
|
const p = m.initRoot(Person);
|
|
t.throws(
|
|
() => {
|
|
for (let i = 0; i < 101 + 1; i++) {
|
|
p.phones;
|
|
}
|
|
},
|
|
// "should throw when exceeding the pointer traversal limit",
|
|
);
|
|
});
|
|
|
|
test("Message.toArrayBuffer()", () => {
|
|
t.equal(
|
|
new Message().toArrayBuffer().byteLength,
|
|
16,
|
|
"should allocate segment 0 before converting",
|
|
);
|
|
});
|
|
|
|
test("Message.toPackedArrayBuffer()", () => {
|
|
const message = new Message(SEGMENTED_UNPACKED, false);
|
|
|
|
compareBuffers(
|
|
message.toPackedArrayBuffer(),
|
|
SEGMENTED_PACKED,
|
|
"should pack messages properly",
|
|
);
|
|
});
|
|
|
|
test("preallocateSegments()", () => {
|
|
t.throws(
|
|
() => {
|
|
const message = new Message(
|
|
new MultiSegmentArena([new ArrayBuffer(8), new ArrayBuffer(7)]),
|
|
);
|
|
|
|
preallocateSegments(message);
|
|
},
|
|
// "should throw when preallocating an empty arena",
|
|
);
|
|
});
|