Skip to content

Commit

Permalink
Added guides.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza committed Aug 12, 2024
1 parent 5a50c2d commit de59e2a
Show file tree
Hide file tree
Showing 13 changed files with 541 additions and 157 deletions.
32 changes: 14 additions & 18 deletions apps/typed-binary-docs/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import starlight from '@astrojs/starlight';
import { defineConfig } from 'astro/config';
import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc';

// https://astro.build/config
export default defineConfig({
Expand All @@ -17,29 +16,26 @@ export default defineConfig({
github: 'https://github.com/iwoplaza/typed-binary',
},
sidebar: [
{
label: 'Guides',
items: [
// Each item here is one entry in the navigation menu.
{ label: 'Why Typed Binary?', slug: 'guides/why-typed-binary' },
{ label: 'Getting Started', slug: 'guides/getting-started' },
],
},
{ label: 'Why Typed Binary?', slug: 'guides/why-typed-binary' },
{
label: 'Learn the Basics',
items: [
// Each item here is one entry in the navigation menu.
{ label: 'Getting Started', slug: 'guides/getting-started' },
{
label: 'Serialization and Deserialization',
slug: 'guides/serialization-and-deserialization',
},
{ label: 'Primitive Values', slug: 'guides/primitive-values' },
{ label: 'Objects', slug: 'guides/objects' },
{ label: 'Arrays and Tuples', slug: 'guides/arrays-and-tuples' },
{ label: 'Optionals', slug: 'guides/optionals' },
{ label: 'Recursive Types', slug: 'guides/recursive-types' },
{
label: 'Custom Schema Types',
slug: 'guides/custom-schema-types',
},
],
},
typeDocSidebarGroup,
],
plugins: [
// Generate the documentation.
starlightTypeDoc({
entryPoints: ['../../packages/typed-binary/src'],
tsconfig: '../../packages/typed-binary/tsconfig.json',
}),
],
}),
],
Expand Down
3 changes: 0 additions & 3 deletions apps/typed-binary-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
"@astrojs/starlight": "^0.25.1",
"astro": "^4.10.2",
"sharp": "^0.32.5",
"starlight-typedoc": "^0.13.1",
"typedoc": "^0.26.5",
"typedoc-plugin-markdown": "^4.2.3",
"typescript": "^5.5.3"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Arrays and Tuples
description: A guide on how arrays and tuples can be represented in Typed Binary
---

## Arrays

The items are encoded right next to each other. No need to store length information, as that is constant (built into the schema).

```ts
import { f32, arrayOf } from 'typed-binary';

const Vector2 = arrayOf(f32, 2);
const Vector3 = arrayOf(f32, 3);
const Vector4 = arrayOf(f32, 4);
```

## Dynamic Arrays

First 4 bytes of encoding are the length of the array, then its items next to one another.

```ts
import { i32, dynamicArrayOf } from 'typed-binary';

const IntArray = dynamicArrayOf(i32);
```

## Tuple

Encodes an ordered set of schemas, one next to another.

```ts
import { f32, string, tupleOf } from 'typed-binary';

const Vec3f = tupleOf([f32, f32, f32]);
type Vec3f = Parsed<typeof Vec3f>; // [number, number, number]

const RecordEntry = tupleOf([string, Vec3f]);
type RecordEntry = Parsed<typeof RecordEntry>; // [string, [number, number, number]]
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: Custom Schema Types
description: A guide on how to create custom schema types in Typed Binary
---

Custom schema types can be defined. They are, under the hood, classes that extend the `Schema<T>` base class. The generic `T` type represents what kind of data this schema serializes from and deserializes into.

```ts
import {
ISerialInput,
ISerialOutput,
Schema,
IRefResolver,
} from 'typed-binary';

/**
* A schema storing radians with 2 bytes of precision.
*/
class RadiansSchema extends Schema<number> {
read(input: ISerialInput): number {
const low = input.readByte();
const high = input.readByte();

const discrete = (high << 8) | low;
return (discrete / 65535) * Math.PI;
}

write(output: ISerialOutput, value: number): void {
// The value will be wrapped to be in range of [0, Math.PI)
const wrapped = ((value % Math.PI) + Math.PI) % Math.PI;
// Quantizing the value to range of [0, 65535]
const discrete = Math.min(Math.floor((wrapped / Math.PI) * 65535), 65535);

const low = discrete & 0xff;
const high = (discrete >> 8) & 0xff;

output.writeByte(low);
output.writeByte(high);
}

measure(_: number, measurer: IMeasurer = new Measurer()): IMeasurer {
// The size of the data serialized by this schema
// doesn't depend on the actual value. It's always 2 bytes.
return measurer.add(2);
}
}

// Creating a singleton instance of the schema,
// since it has no configuration properties.
export const radians = new RadiansSchema();
```
26 changes: 26 additions & 0 deletions apps/typed-binary-docs/src/content/docs/guides/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,29 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
```
</TabItem>
</Tabs>

## Use in the browser

```ts {7}
import { tupleOf, f32, MaxValue, BufferWriter } from 'typed-binary';

// Define a schema
const Vec2f = tupleOf([f32, f32]);
const Vec2fSize = Vec2f.measure(MaxValue).size;

const buffer = new ArrayBuffer(Vec2fSize);
Vec2f.write(new BufferWriter(buffer), [0.5, 3.14]);
```

## Use in Node.js

```ts {7}
import { tupleOf, f32, MaxValue, BufferWriter } from 'typed-binary';

// Define a schema
const Vec2f = tupleOf([f32, f32]);
const Vec2fSize = Vec2f.measure(MaxValue).size;

const buffer = Buffer.alloc(Vec2fSize);
Vec2f.write(new BufferWriter(buffer), [0.5, 3.14]);
```
146 changes: 146 additions & 0 deletions apps/typed-binary-docs/src/content/docs/guides/objects.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: Objects
description: Objects store their properties in key-ascending-alphabetical order, one next to another.
---

Primitive values in JavaScript can be composed into *Plain-Old-JavaScript-Objects*. This can be
easily represented using the `object()` schema constructor function.

## Simple objects

```ts
import { i32, string, object } from 'typed-binary';

// Simple object schema
const Person = object({
firstName: string,
lastName: string,
age: i32,
});

// Writing a Person
Person.write(writer, {
firstName: 'John',
lastName: 'Doe',
age: 43,
});

console.log(JSON.stringify(Person.read(reader))); // { "firstName": "John", ... }
```

:::note
Objects store their properties in the order they are defined in the record passed into the `object()` constructor function.
:::

## Generic objects

This feature allows for the parsing of a type that contains different fields depending on its previous values. For example, if you want to store an animal description, certain animal types might have differing features from one another.

### Keyed by strings

```ts
import {
i32,
string,
bool,
generic,
object,
} from 'typed-binary';

// Generic object schema
const Animal = generic(
{
nickname: string,
age: i32,
},
{
dog: object({
// Animal can be a dog
breed: string,
}),
cat: object({
// Animal can be a cat
striped: bool,
}),
}
);

// Writing an Animal
Animal.write(writer, {
type: 'cat', // We're specyfing which concrete type we want this object to be.

// Base properties
nickname: 'James',
age: 5,

// Concrete type specific properties
striped: true,
});

// Deserializing the animal
const animal = Animal.read(reader);
console.log(JSON.stringify(animal)); // { "age": 5, "striped": true ... }

// -- Type checking works here! --
// animal.type => 'cat' | 'dog'
if (animal.type === 'cat') {
// animal.type => 'cat'
console.log("It's a cat!");
// animal.striped => bool
console.log(animal.striped ? 'Striped' : 'Not striped');
} else {
// animal.type => 'dog'
console.log("It's a dog!");
// animal.breed => string
console.log(`More specifically, a ${animal.breed}`);

// This would result in a type error (Static typing FTW!)
// console.log(`Striped: ${animal.striped}`);
}
```

### Keyed by an enum (byte)

```ts
import { BufferWriter, BufferReader, i32, string, genericEnum, object } from 'typed-binary';

enum AnimalType = {
DOG = 0,
CAT = 1,
};

// Generic (enum) object schema
const Animal = genericEnum({
nickname: string,
age: i32,
}, {
[AnimalType.DOG]: object({ // Animal can be a dog
breed: string,
}),
[AnimalType.CAT]: object({ // Animal can be a cat
striped: bool,
}),
});

// ...
// Same as for the string keyed case
// ...

// -- Type checking works here! --
// animal.type => AnimalType
if (animal.type === AnimalType.CAT) {
// animal.type => AnimalType.CAT
console.log("It's a cat!");
// animal.striped => bool
console.log(animal.striped ? "Striped" : "Not striped");
}
else {
// animal.type => AnimalType.DOG
console.log("It's a dog!");
// animal.breed => string
console.log(`More specifically, a ${animal.breed}`);

// This would result in a type error (Static typing FTW!)
// console.log(`Striped: ${animal.striped}`);
}
```
63 changes: 63 additions & 0 deletions apps/typed-binary-docs/src/content/docs/guides/optionals.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: Optionals
description: A guide on how optionals can be used to create variable-length schemas.
---

Optionals are a good way of ensuring that no excessive data is stored as binary.

They are encoded as:

- `0` given `value === undefined`.
- `1 encoded(value)` given `value !== undefined`.

```ts
import {
BufferWriter,
BufferReader,
i32,
string,
object,
optional,
} from 'typed-binary';

const buffer = Buffer.alloc(16);
const writer = new BufferWriter(buffer);
const reader = new BufferReader(buffer);

// Simple object schema
const Address = object({
city: string,
street: string,
postalCode: string,
});

// Simple object schema (with optional field)
const Person = object({
firstName: string,
lastName: string,
age: i32,
address: optional(Address),
});

// Writing a Person (no address)
Person.write(writer, {
firstName: 'John',
lastName: 'Doe',
age: 43,
});

// Writing a Person (with an address)
Person.write(writer, {
firstName: 'Jesse',
lastName: 'Doe',
age: 38,
address: {
city: 'New York',
street: 'Binary St.',
postalCode: '11-111',
},
});

console.log(JSON.stringify(Person.read(reader).address)); // undefined
console.log(JSON.stringify(Person.read(reader).address)); // { "city": "New York", ... }
```
Loading

0 comments on commit de59e2a

Please sign in to comment.