Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement some documentation on types #30

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/types/_common.ts → src/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export interface Options {
byteOffset: number;
}

/** Extract the inner value of a codec */
export type InnerType<T> = T extends Unsized<infer I> ? I : never;
export type ValueOf<T> = T[keyof T];
2 changes: 1 addition & 1 deletion src/types/mod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./_common.ts";
export * from "./common.ts";
export { SizedType } from "./sized.ts";
export { UnsizedType } from "./unsized.ts";
10 changes: 9 additions & 1 deletion src/types/sized.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { type Unsized, UnsizedType } from "./unsized.ts";
import type { Options } from "./_common.ts";
import type { Options } from "./common.ts";

interface Sized<T> extends Unsized<T> {
readonly byteSize: number;
}

/**
* `SizedType<T>` is one of the two base classes for implementing a codec.
*
* It is recommended to use this class if you know the size of your type ahead of time.
* In future released this may be used for certain optimizations
*/
export abstract class SizedType<T> extends UnsizedType<T> implements Sized<T> {
constructor(readonly byteSize: number, byteAlignment: number = 1) {
super(byteAlignment);
}

/** Increments offset by `this.byteSize` */
protected override incrementOffset(options: Options): void {
super.incrementOffset(options, this.byteSize);
}

/** Allows you to check upfront if you will go out of bound */
protected rangeCheck(byteLength: number, offset: number): void {
if (this.byteSize > (byteLength - offset)) {
throw new RangeError("Out of bound");
Expand Down
44 changes: 41 additions & 3 deletions src/types/unsized.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { align } from "../util.ts";
import type { Options } from "./_common.ts";
import type { Options } from "./common.ts";

export interface Unsized<T> {
readonly byteAlignment: number;
Expand All @@ -8,28 +8,66 @@ export interface Unsized<T> {
writePacked(value: T, dt: DataView, options?: Options): void;
}

/**
* `UnsizedType<T>` is one of the two base classes for implementing a codec.
*
* This is the most common used class for when you do not know the size of your struct.
*/
export abstract class UnsizedType<T> implements Unsized<T> {
constructor(readonly byteAlignment: number) {}

/**
* Read a value from the provided buffer while consuming as little bytes as possible.
* This method is used for data over the network or when reading memory that may not be aligned
*
* ### Implementors be aware!
* - This method is the base functionality of `Unsized<T>.read()` if that method is not overridden.
* - This method does not automatically offset or align the `Options.byteOffset`. You need to do this yourself.
*/
abstract readPacked(dt: DataView, options?: Options): T;

/**
* write a value into the provided buffer while consuming as little bytes as possible.
* This method is used for data over the network or when writing memory that may not be aligned
*
* ### Implementors be aware!
* - This method is the base functionality of `Unsized<T>.write()` if it's not implemented
* - This method does not automatically offset or align the `Options.byteOffset`. You need to do this yourself
*/
abstract writePacked(value: T, dt: DataView, options?: Options): void;

/** In most cases you don't need to reimplement read. as long as your `readPacked` is correct */
/**
* Read a aligned value from the provided buffer and optional byte offset
*
* ### Implementors be aware
* - This is a function that is automatically implemented but may not be correct all the time.
* - This function only works in a vacuum. This means that it will only work on single types.
* - Composable types may not give the correct result and need to be written from scratch.
*/
read(dt: DataView, options: Options = { byteOffset: 0 }): T {
this.alignOffset(options);
return this.readPacked(dt, options);
}

/** In most cases you don't need to reimplement write. as long as your `writePacked` is correct */
/**
* Write a value into the provided buffer at a optional offset that is automatically aligned
*
* ### Implementors be aware
* - This is a function that is automatically implemented but may not be correct all the time.
* - This function only works in a vacuum. This means that it will only work on single types.
* - Composable types may not give the correct result and need to be written from scratch.
*/
write(value: T, dt: DataView, options: Options = { byteOffset: 0 }): void {
this.alignOffset(options);
this.writePacked(value, dt, options);
}

/** Align the offset of `Options.byteOffset` to the nearest integer divisable by `UnsizedType<T>.byteAlignment` */
protected alignOffset(options: Options) {
options.byteOffset = align(options.byteOffset, this.byteAlignment);
}

/** Increment offset by the provided `byteSize` */
protected incrementOffset(options: Options, byteSize: number) {
options.byteOffset += byteSize;
}
Expand Down
2 changes: 2 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ export const isLittleEndian = (() => {
return new Uint16Array(buffer)[0] === 256;
})();

/** Align the value `unaligned` to the first integer that is divisible by `alignment` */
export const align = (unaligned: number, alignment: number) =>
(unaligned + alignment - 1) & ~(alignment - 1);

type ArrayOrRecord<T> = T[] | Record<string | number, T>;

/** Find and returns the biggest alignment out of a record / array of types */
export const getBiggestAlignment = (
input: ArrayOrRecord<UnsizedType<unknown>>,
) =>
Expand Down
Loading