15
import { float32, int32 } from "./types"
21
export enum RuntimeType {
47
export function runtimeType(value: any): int32 {
48
let type = typeof value
49
if (type == "number") return RuntimeType.NUMBER
50
if (type == "string") return RuntimeType.STRING
51
if (type == "undefined") return RuntimeType.UNDEFINED
52
if (type == "object") return RuntimeType.OBJECT
53
if (type == "boolean") return RuntimeType.BOOLEAN
54
if (type == "bigint") return RuntimeType.BIGINT
55
if (type == "function") return RuntimeType.FUNCTION
56
if (type == "symbol") return RuntimeType.SYMBOL
58
throw new Error(`bug: ${value} is ${type}`)
61
export type Function = object
63
export function withLength(valueLength: Length|undefined, body: (value: float32, unit: int32, resource: int32) => void) {
64
let type = runtimeType(valueLength)
69
case RuntimeType.UNDEFINED:
73
case RuntimeType.NUMBER:
74
value = valueLength as float32
76
case RuntimeType.STRING:
77
let valueStr = valueLength as string
79
if (valueStr.endsWith("vp")) {
81
value = Number(valueStr.substring(0, valueStr.length - 2))
82
} else if (valueStr.endsWith("%")) {
84
value = Number(valueStr.substring(0, valueStr.length - 1))
85
} else if (valueStr.endsWith("lpx")) {
87
value = Number(valueStr.substring(0, valueStr.length - 3))
88
} else if (valueStr.endsWith("px")) {
90
value = Number(valueStr.substring(0, valueStr.length - 2))
93
case RuntimeType.OBJECT:
94
resource = (valueLength as Resource).id
97
body(value, unit, resource)
101
export function withLengthArray(valueLength: Length|undefined, body: (valuePtr: Int32Array) => void) {
102
withLength(valueLength, (value, unit, resource) => {
103
let array = new Int32Array(3)
111
let textEncoder = new TextEncoder()
114
export abstract class CustomSerializer {
115
constructor(protected supported: Array<string>) {}
116
supports(kind: string): boolean { return this.supported.includes(kind) }
117
abstract serialize(serializer: SerializerBase, value: any, kind: string): void
118
next: CustomSerializer | undefined = undefined
121
export class SerializerBase {
123
private buffer: ArrayBuffer
124
private view: DataView
126
private static customSerializers: CustomSerializer | undefined = undefined
127
static registerCustomSerializer(serializer: CustomSerializer) {
128
if (SerializerBase.customSerializers == undefined) {
129
SerializerBase.customSerializers = serializer
131
let current = SerializerBase.customSerializers
132
while (current.next != undefined) { current = current.next }
133
current.next = serializer
136
constructor(expectedSize: int32) {
137
this.buffer = new ArrayBuffer(expectedSize)
138
this.view = new DataView(this.buffer)
140
asArray(): Uint8Array {
141
return new Uint8Array(this.buffer)
146
currentPosition(): int32 { return this.position }
147
private checkCapacity(value: int32) {
149
throw new Error(`${value} is less than 1`)
151
let buffSize = this.buffer.byteLength
152
if (this.position > buffSize - value) {
153
const minSize = this.position + value
154
const resizedSize = Math.max(minSize, Math.round(3 * buffSize / 2))
155
let resizedBuffer = new ArrayBuffer(resizedSize)
156
new Uint8Array(resizedBuffer).set(new Uint8Array(this.buffer));
157
this.buffer = resizedBuffer
158
this.view = new DataView(resizedBuffer)
161
writeCustom(kind: string, value: any) {
162
let current = SerializerBase.customSerializers
164
if (current.supports(kind)) {
165
current.serialize(this, value, kind)
169
console.log(`Unsupported custom serialization for ${kind}, write undefined`)
170
this.writeInt8(Tags.UNDEFINED)
172
writeNumber(value: number|undefined) {
173
this.checkCapacity(5)
174
if (value == undefined) {
175
this.view.setInt8(this.position, Tags.UNDEFINED)
179
if (value == Math.round(value)) {
180
this.view.setInt8(this.position, Tags.INT32)
181
this.view.setInt32(this.position + 1, value, true)
185
this.view.setInt8(this.position, Tags.FLOAT32)
186
this.view.setFloat32(this.position + 1, value, true)
189
writeInt8(value: int32) {
190
this.checkCapacity(1)
191
this.view.setInt8(this.position, value)
194
writeInt32(value: int32) {
195
this.checkCapacity(4)
196
this.view.setInt32(this.position, value, true)
199
writeFloat32(value: float32) {
200
this.checkCapacity(4)
201
this.view.setFloat32(this.position, value, true)
204
writeBoolean(value: boolean|undefined) {
205
this.checkCapacity(1)
206
this.view.setInt8(this.position, value == undefined ? RuntimeType.UNDEFINED : +value)
209
writeFunction(value: object | undefined) {
210
this.writeCustom("Function", value)
212
writeString(value: string|undefined) {
213
if (value == undefined) {
214
this.writeInt8(Tags.UNDEFINED)
217
let encoded = textEncoder.encode(value)
218
this.checkCapacity(5 + encoded.length)
219
this.view.setInt8(this.position, Tags.STRING)
220
this.view.setInt32(this.position + 1, encoded.length, true)
221
new Uint8Array(this.view.buffer, this.position + 5).set(encoded)
222
this.position += 5 + encoded.length
224
writeAny(value: any) {
225
throw new Error("How to write any?")
228
writeLength(value: Length|undefined) {
229
this.checkCapacity(1)
230
let valueType = runtimeType(value)
231
this.writeInt8(valueType == RuntimeType.UNDEFINED ? Tags.UNDEFINED : Tags.LENGTH)
232
if (valueType != RuntimeType.UNDEFINED) {
233
withLength(value, (value, unit, resource) => {
234
this.writeFloat32(value)
235
this.writeInt32(unit)
236
this.writeInt32(resource)
240
writeAnimationRange(value: AnimationRange<number>|undefined) {
242
this.writeInt8(Tags.UNDEFINED)
245
this.writeInt8(Tags.OBJECT)
246
this.writeNumber(value[0])
247
this.writeNumber(value[1])
250
writeCallback(value: Callback<any>|undefined) {
251
this.writeCustom("Callback", value)
255
class OurCustomSerializer extends CustomSerializer {
257
super(["Resource", "Pixmap", "Function"])
259
serialize(serializer: SerializerBase, value: any, kind: string): void {
260
console.log(`managed serialize() for ${kind}`)
261
serializer.writeString(kind == "Function" ? value.toString(): JSON.stringify(value))
266
SerializerBase.registerCustomSerializer(new OurCustomSerializer())