Skip to content

Commit 8d86fec

Browse files
committed
Refactor to pinBuffer API to make garbage collection guarantees
1 parent d208e45 commit 8d86fec

File tree

2 files changed

+60
-9
lines changed

2 files changed

+60
-9
lines changed

lib/index.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,49 @@
11
import { nativeBuffer2address, nativeAddress2buffer } from "./native.ts";
2+
import nodeAssert from "node:assert";
23

3-
const buffer2address = ({ buffer }: { buffer: Uint8Array }) => {
4-
return nativeBuffer2address(buffer);
4+
let pinnedBuffers: Uint8Array[] = [];
5+
6+
type TPinnedBuffer = {
7+
address: bigint;
8+
unpin: () => void;
59
};
610

7-
const address2buffer = ({ address, size }: { address: bigint, size: number }) => {
11+
const pinBuffer = ({ buffer }: { buffer: Uint8Array }): TPinnedBuffer => {
12+
13+
pinnedBuffers = [...pinnedBuffers, buffer];
14+
let pinned = true;
15+
16+
const unpin = () => {
17+
18+
if (!pinned) {
19+
throw new Error("buffer already unpinned");
20+
}
21+
pinned = false;
22+
23+
// do not use filter, as one could pin the same buffer multiple times
24+
const pinnedIndex = pinnedBuffers.indexOf(buffer);
25+
nodeAssert.ok(pinnedIndex >= 0, "buffer should be in pinned buffers, this is a bug");
826

27+
pinnedBuffers.splice(pinnedIndex, 1);
28+
};
929

30+
const address = nativeBuffer2address(buffer);
31+
32+
return {
33+
address,
34+
unpin
35+
};
36+
};
37+
38+
const address2buffer = ({ address, size }: { address: bigint, size: number }) => {
1039
return nativeAddress2buffer(address, BigInt(size));
1140
};
1241

1342
export {
14-
buffer2address,
43+
pinBuffer,
1544
address2buffer
1645
};
46+
47+
export type {
48+
TPinnedBuffer
49+
};

test/basic.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
/* global it */
33

44
import assert from "assert";
5-
import { buffer2address, address2buffer } from "../lib/index.ts";
5+
import { pinBuffer, address2buffer } from "../lib/index.ts";
66

77
describe("buffer2address", () => {
88
it("should provide address of buffer as BigInt", () => {
99
const buffer = Buffer.alloc(32);
10-
const addr = buffer2address({ buffer });
11-
assert.equal(typeof addr, "bigint");
10+
11+
const pinnedBuffer = pinBuffer({ buffer });
12+
assert.equal(typeof pinnedBuffer.address, "bigint");
13+
pinnedBuffer.unpin();
1214
});
1315

1416
// it("should throw an error when no arguments are given", () => {
@@ -25,22 +27,38 @@ describe("buffer2address", () => {
2527
// // @ts-expect-error testing wrong argument type
2628
// assert.throws(() => buffer2address("abc"));
2729
// });
30+
31+
it("should throw an error when unpinning twice", () => {
32+
const buffer = Buffer.alloc(16);
33+
const pinnedBuffer = pinBuffer({ buffer });
34+
35+
pinnedBuffer.unpin();
36+
37+
assert.throws(() => {
38+
pinnedBuffer.unpin();
39+
}, (err: Error) => {
40+
assert.equal(err.message, "buffer already unpinned");
41+
return true;
42+
});
43+
});
2844
});
2945

3046
describe("address2buffer", () => {
3147
it("should create buffer from address correctly", () => {
3248
const size = 32;
3349
const buf = new Uint8Array(size);
34-
const address = buffer2address({ buffer: buf });
3550

36-
const newBuf = address2buffer({ address, size });
51+
const pinnedBuffer = pinBuffer({ buffer: buf });
52+
const newBuf = address2buffer({ address: pinnedBuffer.address, size });
3753

3854
const testValues = [0x22, 0x33, 0x44];
3955

4056
testValues.forEach((value) => {
4157
buf[0] = value;
4258
assert.equal(newBuf[0], value);
4359
});
60+
61+
pinnedBuffer.unpin();
4462
});
4563

4664
// it("should fail on missing address parameter", () => {

0 commit comments

Comments
 (0)