Skip to content

Commit

Permalink
Can now estimate the max value of a schema.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza committed Oct 14, 2023
1 parent 5d0fb52 commit 28b195a
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 160 deletions.
15 changes: 13 additions & 2 deletions src/structure/array.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { ISerialInput, ISerialOutput } from '../io';
import { i32 } from './baseTypes';
import { IRefResolver, ISchema, IStableSchema, Schema } from './types';
import {
IRefResolver,
ISchema,
IStableSchema,
Schema,
MaxValue,
} from './types';

export class ArraySchema<T> extends Schema<T[]> {
public elementType: IStableSchema<T>;
Expand Down Expand Up @@ -37,7 +43,12 @@ export class ArraySchema<T> extends Schema<T[]> {
return array;
}

sizeOf(values: T[]): number {
sizeOf(values: T[] | typeof MaxValue): number {
if (values === MaxValue) {
// arrays cannot be bound
return NaN;
}

// Length encoding
let size = i32.sizeOf();
// Values encoding
Expand Down
8 changes: 6 additions & 2 deletions src/structure/baseTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ISerialInput, ISerialOutput } from '../io';
import { Schema } from './types';
import { Schema, MaxValue } from './types';

////
// BOOL
Expand Down Expand Up @@ -42,7 +42,11 @@ export class StringSchema extends Schema<string> {
output.writeString(value);
}

sizeOf<T extends string>(value: T): number {
sizeOf<T extends string>(value: T | typeof MaxValue): number {
if (value === MaxValue) {
// A string cannot be bound
return NaN;
}
return value.length + 1;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/structure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
u32,
f32,
string,
MaxValue,
Ref,
IRefResolver,
Schema,
Expand Down
161 changes: 91 additions & 70 deletions src/structure/keyed.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,113 @@
import { Parsed } from '..';
import { TypedBinaryError } from '../error';
import { ISerialInput, ISerialOutput } from '../io';
import { IRefResolver, ISchema, IStableSchema, Keyed, Ref } from './types';
import {
IRefResolver,
ISchema,
IStableSchema,
Keyed,
Ref,
MaxValue,
} from './types';

class RefSchema<K extends string> implements IStableSchema<Ref<K>> {
public readonly _infered!: Ref<K>;
public readonly ref: Ref<K>;

constructor(key: K) {
this.ref = new Ref(key);
}

resolve(): void {
throw new TypedBinaryError(`Tried to resolve a reference directly. Do it through a RefResolver instead.`);
}

read(): Ref<K> {
throw new TypedBinaryError(`Tried to read a reference directly. Resolve it instead.`);
}

write(): void {
throw new TypedBinaryError(`Tried to write a reference directly. Resolve it instead.`);
}

sizeOf(): number {
throw new TypedBinaryError(`Tried to estimate size of a reference directly. Resolve it instead.`);
}
public readonly _infered!: Ref<K>;
public readonly ref: Ref<K>;

constructor(key: K) {
this.ref = new Ref(key);
}

resolve(): void {
throw new TypedBinaryError(
`Tried to resolve a reference directly. Do it through a RefResolver instead.`,
);
}

read(): Ref<K> {
throw new TypedBinaryError(
`Tried to read a reference directly. Resolve it instead.`,
);
}

write(): void {
throw new TypedBinaryError(
`Tried to write a reference directly. Resolve it instead.`,
);
}

sizeOf(): number {
throw new TypedBinaryError(
`Tried to estimate size of a reference directly. Resolve it instead.`,
);
}
}

class RefResolve implements IRefResolver {
private registry: {[key: string]: IStableSchema<unknown>} = {};

hasKey(key: string): boolean {
return this.registry[key] !== undefined;
private registry: { [key: string]: IStableSchema<unknown> } = {};

hasKey(key: string): boolean {
return this.registry[key] !== undefined;
}

register<K extends string>(key: K, schema: IStableSchema<unknown>): void {
this.registry[key] = schema;
}

resolve<T>(unstableSchema: ISchema<T>): IStableSchema<T> {
if (unstableSchema instanceof RefSchema) {
const ref = unstableSchema.ref;
const key = ref.key as string;
if (this.registry[key] !== undefined) {
return this.registry[key] as IStableSchema<T>;
}

throw new TypedBinaryError(
`Couldn't resolve reference to ${key}. Unknown key.`,
);
}

register<K extends string>(key: K, schema: IStableSchema<unknown>): void {
this.registry[key] = schema;
}

resolve<T>(unstableSchema: ISchema<T>): IStableSchema<T> {
if (unstableSchema instanceof RefSchema) {
const ref = unstableSchema.ref;
const key = ref.key as string;
if (this.registry[key] !== undefined) {
return this.registry[key] as IStableSchema<T>;
}

throw new TypedBinaryError(`Couldn't resolve reference to ${key}. Unknown key.`);
}
// Since it's not a RefSchema, we assume it can be resolved.
(unstableSchema as IStableSchema<T>).resolve(this);

// Since it's not a RefSchema, we assume it can be resolved.
(unstableSchema as IStableSchema<T>).resolve(this);

return unstableSchema as IStableSchema<T>;
}
return unstableSchema as IStableSchema<T>;
}
}

export class KeyedSchema<K extends string, S extends IStableSchema<unknown>> implements ISchema<Keyed<K, S>> {
public readonly _infered!: Keyed<K, S>;
public innerType: S;
export class KeyedSchema<K extends string, S extends IStableSchema<unknown>>
implements ISchema<Keyed<K, S>>
{
public readonly _infered!: Keyed<K, S>;
public innerType: S;

constructor(public readonly key: K, innerResolver: (ref: ISchema<Ref<K>>) => S) {
this.innerType = innerResolver(new RefSchema(key));
this._infered = new Keyed(key, this.innerType);
constructor(
public readonly key: K,
innerResolver: (ref: ISchema<Ref<K>>) => S,
) {
this.innerType = innerResolver(new RefSchema(key));
this._infered = new Keyed(key, this.innerType);

// Automatically resolving after keyed creation.
this.resolve(new RefResolve());
}
// Automatically resolving after keyed creation.
this.resolve(new RefResolve());
}

resolve(ctx: IRefResolver): void {
if (!ctx.hasKey(this.key)) {
ctx.register(this.key, this.innerType);
resolve(ctx: IRefResolver): void {
if (!ctx.hasKey(this.key)) {
ctx.register(this.key, this.innerType);

this.innerType.resolve(ctx);
}
this.innerType.resolve(ctx);
}
}

read(input: ISerialInput): Parsed<S> {
return this.innerType.read(input) as Parsed<S>;
}
read(input: ISerialInput): Parsed<S> {
return this.innerType.read(input) as Parsed<S>;
}

write(output: ISerialOutput, value: Parsed<S>): void {
this.innerType.write(output, value);
}
write(output: ISerialOutput, value: Parsed<S>): void {
this.innerType.write(output, value);
}

sizeOf(value: Parsed<S>): number {
return this.innerType.sizeOf(value);
}
sizeOf(value: Parsed<S> | typeof MaxValue): number {
return this.innerType.sizeOf(value);
}
}

7 changes: 5 additions & 2 deletions src/structure/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ISchemaWithProperties,
SchemaMap,
StableSchemaMap,
MaxValue,
} from './types';
import { SubTypeKey } from './types';

Expand Down Expand Up @@ -73,9 +74,11 @@ export class ObjectSchema<T extends { [key: string]: unknown }>
return result;
}

sizeOf<I extends T>(value: I): number {
sizeOf<I extends T>(value: I | typeof MaxValue): number {
return exactEntries(this.properties)
.map(([key, property]) => property.sizeOf(value[key])) // Mapping properties into their sizes.
.map(([key, property]) =>
property.sizeOf(value == MaxValue ? MaxValue : value[key]),
) // Mapping properties into their sizes.
.reduce((a, b) => a + b, 0); // Summing them up
}
}
Expand Down
72 changes: 38 additions & 34 deletions src/structure/optional.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
import type { ISerialInput, ISerialOutput } from '../io';
import { IRefResolver, ISchema, IStableSchema, Schema } from './types';
import {
IRefResolver,
ISchema,
IStableSchema,
MaxValue,
Schema,
} from './types';

export class OptionalSchema<T> extends Schema<T|undefined> {
private innerSchema: IStableSchema<T>;
export class OptionalSchema<T> extends Schema<T | undefined> {
private innerSchema: IStableSchema<T>;

constructor(private readonly _innerUnstableSchema: ISchema<T>) {
super();
constructor(private readonly _innerUnstableSchema: ISchema<T>) {
super();

// In case this optional isn't part of a keyed chain,
// let's assume the inner type is stable.
this.innerSchema = _innerUnstableSchema as IStableSchema<T>;
}
// In case this optional isn't part of a keyed chain,
// let's assume the inner type is stable.
this.innerSchema = _innerUnstableSchema as IStableSchema<T>;
}

resolve(ctx: IRefResolver): void {
this.innerSchema = ctx.resolve(this._innerUnstableSchema);
}
resolve(ctx: IRefResolver): void {
this.innerSchema = ctx.resolve(this._innerUnstableSchema);
}

write(output: ISerialOutput, value: T|undefined): void {
if (value !== undefined && value !== null) {
output.writeBool(true);
this.innerSchema.write(output, value);
}
else {
output.writeBool(false);
}
write(output: ISerialOutput, value: T | undefined): void {
if (value !== undefined && value !== null) {
output.writeBool(true);
this.innerSchema.write(output, value);
} else {
output.writeBool(false);
}
}

read(input: ISerialInput): T|undefined {
const valueExists = input.readBool();

if (valueExists) {
return this.innerSchema.read(input);
}

return undefined;
}
read(input: ISerialInput): T | undefined {
const valueExists = input.readBool();

sizeOf(value: T|undefined): number {
if (value === undefined)
return 1;

return 1 + this.innerSchema.sizeOf(value);
if (valueExists) {
return this.innerSchema.read(input);
}

return undefined;
}

sizeOf(value: T | undefined | typeof MaxValue): number {
if (value === undefined) return 1;

return 1 + this.innerSchema.sizeOf(value);
}
}
Loading

0 comments on commit 28b195a

Please sign in to comment.