preserves/implementations/javascript/packages/core/src/float.ts

96 lines
2.5 KiB
TypeScript

import { Encoder, Preservable } from "./encoder";
import { Tag } from "./constants";
import { AsPreserve, PreserveOn } from "./symbols";
import { Value } from "./values";
import { GenericEmbedded } from "./embedded";
export type FloatType = 'Single' | 'Double';
export const FloatType = Symbol.for('FloatType');
export abstract class Float {
readonly value: number;
constructor(value: number | Float) {
this.value = typeof value === 'number' ? value : value.value;
}
toString() {
return this.asPreservesText();
}
equals(other: any): boolean {
return Object.is(other.constructor, this.constructor) && (other.value === this.value);
}
hashCode(): number {
return (this.value | 0); // TODO: something better?
}
abstract asPreservesText(): string;
abstract get [FloatType](): FloatType;
static isFloat = (x: any): x is Float => x?.[FloatType] !== void 0;
static isSingle = (x: any): x is SingleFloat => x?.[FloatType] === 'Single';
static isDouble = (x: any): x is DoubleFloat => x?.[FloatType] === 'Double';
}
export function floatValue(f: any): number {
if (typeof f === 'number') {
return f;
} else if (Float.isFloat(f)) {
return f.value;
} else {
return NaN;
}
}
export class SingleFloat extends Float implements Preservable<never> {
[AsPreserve]<T = GenericEmbedded>(): Value<T> {
return this;
}
[PreserveOn](encoder: Encoder<never>) {
encoder.state.emitbyte(Tag.Float);
encoder.state.makeroom(4);
encoder.state.view.setFloat32(encoder.state.index, this.value, false);
encoder.state.index += 4;
}
get [FloatType](): 'Single' {
return 'Single';
}
asPreservesText(): string {
return '' + this.value + 'f';
}
}
export function Single(value: number | Float): SingleFloat {
return new SingleFloat(value);
}
export class DoubleFloat extends Float implements Preservable<never> {
[AsPreserve]<T = GenericEmbedded>(): Value<T> {
return this;
}
[PreserveOn](encoder: Encoder<never>) {
encoder.state.emitbyte(Tag.Double);
encoder.state.makeroom(8);
encoder.state.view.setFloat64(encoder.state.index, this.value, false);
encoder.state.index += 8;
}
get [FloatType](): 'Double' {
return 'Double';
}
asPreservesText(): string {
return '' + this.value;
}
}
export function Double(value: number | Float): DoubleFloat {
return new DoubleFloat(value);
}