Skip to content

Commit

Permalink
Initial implementation of TypedArray support.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza committed Jun 28, 2024
1 parent dcf50eb commit 2f8cc7f
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 4 deletions.
34 changes: 31 additions & 3 deletions src/describe/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import {
SubTypeKey,
TupleSchema,
} from '../structure';
import { DynamicArraySchema } from '../structure/dynamicArray';
import { ArraySchema } from '../structure/array';
import { KeyedSchema } from '../structure/keyed';
import { OptionalSchema } from '../structure/optional';
import { TypedArraySchema } from '../structure/typedArray';
import { DynamicArraySchema } from '../structure/dynamicArray';
import { AnyObjectSchema, GenericObjectSchema } from '../structure/object';
import { ArraySchema } from '../structure/array';
import {
ISchema,
Ref,
AnySchema,
Unwrap,
AnySchemaWithProperties,
} from '../structure/types';
import { KeyedSchema } from '../structure/keyed';
import { MergeRecordUnion } from '../utilityTypes';

export const chars = <T extends number>(length: T) => new CharsSchema(length);
Expand Down Expand Up @@ -56,6 +57,33 @@ export const tupleOf = <TSchema extends [AnySchema, ...AnySchema[]]>(
schemas: TSchema,
) => new TupleSchema(schemas);

export const u8Array = (length: number) =>
new TypedArraySchema(length, Uint8Array);

export const u8ClampedArray = (length: number) =>
new TypedArraySchema(length, Uint8ClampedArray);

export const u16Array = (length: number) =>
new TypedArraySchema(length, Uint16Array);

export const u32Array = (length: number) =>
new TypedArraySchema(length, Uint32Array);

export const i8Array = (length: number) =>
new TypedArraySchema(length, Int8Array);

export const i16Array = (length: number) =>
new TypedArraySchema(length, Int16Array);

export const i32Array = (length: number) =>
new TypedArraySchema(length, Int32Array);

export const f32Array = (length: number) =>
new TypedArraySchema(length, Float32Array);

export const f64Array = (length: number) =>
new TypedArraySchema(length, Float64Array);

export const optional = <TSchema extends AnySchema>(innerType: TSchema) =>
new OptionalSchema(innerType);

Expand Down
10 changes: 9 additions & 1 deletion src/io/bufferReader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BufferIOBase, BufferIOOptions } from './bufferIOBase';
import { ISerialInput } from './types';
import { ISerialInput, MutableBufferView } from './types';

export class BufferReader extends BufferIOBase implements ISerialInput {
constructor(buffer: ArrayBufferLike, options?: BufferIOOptions) {
Expand Down Expand Up @@ -50,4 +50,12 @@ export class BufferReader extends BufferIOBase implements ISerialInput {

return contents;
}

readSlice(
bufferView: MutableBufferView,
offset: number,
length: number,
): void {
bufferView.set(this.uint8View.subarray(this.byteOffset, length), offset);
}
}
5 changes: 5 additions & 0 deletions src/io/bufferWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ export class BufferWriter extends BufferIOBase implements ISerialOutput {
// Extra null character
this.uint8View[this.byteOffset++] = 0;
}

writeSlice(bufferView: ArrayLike<number> & ArrayBufferView): void {
this.uint8View.set(bufferView, this.byteOffset);
this.byteOffset += bufferView.byteLength;
}
}
12 changes: 12 additions & 0 deletions src/io/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
export type Endianness = 'big' | 'little';

export type ImmutableBufferView = ArrayLike<number> & ArrayBufferView;

export type MutableBufferView = ImmutableBufferView & {
set(array: ArrayLike<number>, offset?: number): void;
};

export interface ISerialInput {
readBool(): boolean;
readByte(): number;
readInt32(): number;
readUint32(): number;
readFloat32(): number;
readString(): string;
readSlice(
bufferView: MutableBufferView,
offset: number,
length: number,
): void;
seekTo(offset: number): void;
skipBytes(bytes: number): void;
readonly endianness: Endianness;
Expand All @@ -20,6 +31,7 @@ export interface ISerialOutput {
writeUint32(value: number): void;
writeFloat32(value: number): void;
writeString(value: string): void;
writeSlice(bufferView: ImmutableBufferView): void;
seekTo(offset: number): void;
skipBytes(bytes: number): void;
readonly endianness: Endianness;
Expand Down
47 changes: 47 additions & 0 deletions src/structure/typedArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
type ISerialInput,
type ISerialOutput,
type IMeasurer,
Measurer,
} from '../io';
import { MutableBufferView } from '../io/types';
import { Parsed } from '../utilityTypes';
import { Schema, MaxValue } from './types';

type TypedArrayConstructor<T> = {
readonly BYTES_PER_ELEMENT: number;
new (buffer: ArrayBufferLike, offset?: number, length?: number): T;
};

export class TypedArraySchema<
TTypedArray extends MutableBufferView,
> extends Schema<TTypedArray> {
public readonly byteLength: number;

constructor(
public readonly length: number,
private readonly _arrayConstructor: TypedArrayConstructor<TTypedArray>,
) {
super();

this.byteLength = length * _arrayConstructor.BYTES_PER_ELEMENT;
}

write(output: ISerialOutput, value: Parsed<TTypedArray>): void {
output.writeSlice(value);
}

read(input: ISerialInput): Parsed<TTypedArray> {
const buffer = new ArrayBuffer(this.byteLength);
const view = new this._arrayConstructor(buffer, 0, this.length);
input.readSlice(view, 0, this.byteLength);
return view as Parsed<TTypedArray>;
}

measure(
_value: Parsed<TTypedArray> | typeof MaxValue,
measurer: IMeasurer = new Measurer(),
): IMeasurer {
return measurer.add(this.byteLength);
}
}

0 comments on commit 2f8cc7f

Please sign in to comment.