Skip to content

Commit

Permalink
feat: implement some documentation on types (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
MierenManz authored Mar 5, 2024
1 parent a598ac0 commit 98cec14
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 5 deletions.
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

0 comments on commit 98cec14

Please sign in to comment.