Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
hazae41 committed Aug 21, 2024
1 parent b72532d commit a5f199f
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 16 deletions.
93 changes: 81 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Box
# Box and its friends

Rust-like Box for TypeScript
Rust-like Box and similar objects for TypeScript

```bash
npm i @hazae41/box
Expand All @@ -19,33 +19,38 @@ npm i @hazae41/box

## Usage

### Box<T>
### `Box<T>`

A reference that can be unset
A movable reference

```typescript
import { Box } from "@hazae41/box"

class Resource {

[Symbol.dispose]() {
console.log("This should only happen once")
console.log("Disposed")
}

}

async function take(box: Box<Resource>) {
using box2 = box.moveOrThrow()
await doSomethingOrThrow()
}

/**
* Resource will only be disposed once
* Resource will only be disposed after the promise settles
*/
{
using box = new Box(new Resource())
using box2 = box.moveOrThrow()
take(box).catch(console.error)
}
```

### Slot<T>
### `Slot<T>`

A reference that can change
A mutable reference

```tsx
class Pointer {
Expand Down Expand Up @@ -89,10 +94,12 @@ function* getPointersOrThrow() {

Everything is correctly disposed if `getNumbersOrThrow()` throws in the midst of the loop

### Auto<T>
### `Auto<T>`

A reference that will be disposed when garbage collected

These references are NOT guaranteed to be disposed

```tsx
class Pointer {

Expand Down Expand Up @@ -126,7 +133,9 @@ class MyObject {

The pointer will be freed when the object will be garbage collected

But `Auto<T>` can be disposed to unregister itself from garbage collection
---

An auto can be disposed to unregister itself from garbage collection

```tsx
function unwrap<T extends Disposable>(auto: Auto<T>) {
Expand All @@ -147,4 +156,64 @@ You can also use `.unwrap()` to do this
const raw = new Pointer(123)
const auto = new Auto(raw)
using raw2 = auto.unwrap()
```
```

### `Tick<T>`

A reference that will be disposed after some delay (0 by default)

These references are guaranteed to be disposed

```tsx
{
const pointer = new Tick(new Pointer(123))

await doSomethingOrThrow()

// Pointer is guaranteed to be freed here
}
```

This is useful to prevent WebAssembly memory from growing without using `using`

### `Once<T>`

A reference that can only be disposed once

```tsx
class Socket {

constructor(
readonly socket: WebSocket
) {}

[Symbol.dispose]() {
this.socket.close()
}

get() {
return this.socket
}

}

function terminate(socket: Once<Socket>) {
using _ = socket

socket.get().send("closing")
}

{
const socket = new Auto(new Once(new Socket(raw)))

if (something) {
terminate(socket.get())
return
// Will be closed here
}

// Will be closed on garbage collection
}
```

This can enable mixed behaviour where a resource can be disposed on demand or disposed on garbage collection
10 changes: 9 additions & 1 deletion src/mods/auto/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/**
* A reference that will be disposed when garbage collected
*/
export class Auto<T extends Disposable> {

static readonly cleanup = (x: Disposable) => x[Symbol.dispose]?.()
static readonly registry = new FinalizationRegistry(Auto.cleanup)

/**
* A reference that will be disposed when garbage collected
* @param inner
*/
constructor(
readonly inner: T
Expand All @@ -27,13 +31,17 @@ export class Auto<T extends Disposable> {

}

/**
* A reference that will be disposed when garbage collected
*/
export class AsyncAuto<T extends AsyncDisposable> {

static readonly cleanup = (x: AsyncDisposable) => x[Symbol.asyncDispose]?.().then(undefined, console.error)
static readonly registry = new FinalizationRegistry(AsyncAuto.cleanup)

/**
* A readonly slot that will be automatically disposed
* A reference that will be disposed when garbage collected
* @param inner
*/
constructor(
readonly inner: T
Expand Down
6 changes: 5 additions & 1 deletion src/mods/box/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ export class BoxMovedError extends Error {
}
}

/**
* A movable reference
*/
export class Box<T> {

#moved = false

/**
* A reference that can be unset
* A movable reference
* @param inner
*/
constructor(
readonly inner: T
Expand Down
1 change: 1 addition & 0 deletions src/mods/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./auto/index.js";
export * from "./box/index.js";
export * from "./slot/index.js";
export * from "./tick/index.js";

40 changes: 40 additions & 0 deletions src/mods/once/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* A reference that can only be disposed once
*/
export class Once<T> {

#disposed = false

/**
* A reference that can only be disposed once
* @param inner
*/
constructor(
readonly inner: T
) { }

[Symbol.dispose](this: Once<Disposable>) {
if (this.#disposed)
return
this.#disposed = true

this.inner[Symbol.dispose]?.()
}

async [Symbol.asyncDispose](this: Once<AsyncDisposable>) {
if (this.#disposed)
return
this.#disposed = true

await this.inner[Symbol.asyncDispose]?.()
}

get disposed() {
return this.#disposed
}

get() {
return this.inner
}

}
6 changes: 4 additions & 2 deletions src/mods/slot/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

/**
* A mutable reference
*/
export class Slot<T> {

/**
* A reference that can change
* A mutable reference
* @param inner
*/
constructor(
Expand Down
68 changes: 68 additions & 0 deletions src/mods/tick/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* A reference that will be disposed after some delay
*/
export class Tick<T extends Disposable> {

#timeout: NodeJS.Timeout

/**
* A reference that will be disposed after some delay
* @param inner
* @param delay
*/
constructor(
readonly inner: T,
readonly delay = 0
) {
this.#timeout = setTimeout(() => this.inner[Symbol.dispose](), delay)
}

[Symbol.dispose]() {
clearTimeout(this.#timeout)
}

get() {
return this.inner
}

unwrap() {
using _ = this
return this.inner
}

}

/**
* A reference that will be disposed after some delay
*/
export class AsyncTick<T extends AsyncDisposable> {

#timeout: NodeJS.Timeout

/**
* A reference that will be disposed after some delay
* @param inner
* @param delay
*/
constructor(
readonly inner: T,
readonly delay = 0
) {
this.#timeout = setTimeout(() => this.inner[Symbol.asyncDispose]().then(undefined, console.error), delay)
}


[Symbol.dispose]() {
clearTimeout(this.#timeout)
}

get() {
return this.inner
}

unwrap() {
using _ = this
return this.inner
}

}

0 comments on commit a5f199f

Please sign in to comment.