idlize

Форк
0
/
SerializerBase.ts 
266 строк · 8.8 Кб
1
/*
2
 * Copyright (c) 2024 Huawei Device Co., Ltd.
3
 * Licensed under the Apache License, Version 2.0 (the "License");
4
 * you may not use this file except in compliance with the License.
5
 * You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software
10
 * distributed under the License is distributed on an "AS IS" BASIS,
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
 * See the License for the specific language governing permissions and
13
 * limitations under the License.
14
 */
15
import { float32, int32 } from "./types"
16

17
/**
18
 * Value representing possible JS runtime object type.
19
 * Must be synced with "enum RuntimeType" in C++.
20
 */
21
export enum RuntimeType {
22
    UNEXPECTED = -1,
23
    NUMBER = 1,
24
    STRING = 2,
25
    OBJECT = 3,
26
    BOOLEAN = 4,
27
    UNDEFINED = 5,
28
    BIGINT = 6,
29
    FUNCTION = 7,
30
    SYMBOL = 8
31
}
32

33
/**
34
 * Value representing object type in serialized data.
35
 * Must be synced with "enum Tags" in C++.
36
 */
37
export enum Tags {
38
    UNDEFINED = 101,
39
    INT32 = 102,
40
    FLOAT32 = 103,
41
    STRING = 104,
42
    LENGTH = 105,
43
    RESOURCE = 106,
44
    OBJECT = 107,
45
}
46

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
57

58
    throw new Error(`bug: ${value} is ${type}`)
59
}
60

61
export type Function = object
62

63
export function withLength(valueLength: Length|undefined, body: (value: float32, unit: int32, resource: int32) => void) {
64
    let type = runtimeType(valueLength)
65
    let value = 0
66
    let unit = 1 // vp
67
    let resource = 0
68
    switch (type) {
69
        case RuntimeType.UNDEFINED:
70
            value = 0
71
            unit = 0
72
            break
73
        case RuntimeType.NUMBER:
74
            value = valueLength as float32
75
            break
76
        case RuntimeType.STRING:
77
            let valueStr = valueLength as string
78
            // TODO: faster parse.
79
            if (valueStr.endsWith("vp")) {
80
                unit = 1 // vp
81
                value = Number(valueStr.substring(0, valueStr.length - 2))
82
            } else if (valueStr.endsWith("%")) {
83
                unit = 3 // percent
84
                value = Number(valueStr.substring(0, valueStr.length - 1))
85
            } else if (valueStr.endsWith("lpx")) {
86
                unit = 4 // lpx
87
                value = Number(valueStr.substring(0, valueStr.length - 3))
88
            } else if (valueStr.endsWith("px")) {
89
                unit = 0 // px
90
                value = Number(valueStr.substring(0, valueStr.length - 2))
91
            }
92
            break
93
        case RuntimeType.OBJECT:
94
            resource = (valueLength as Resource).id
95
            break
96
    }
97
    body(value, unit, resource)
98
}
99

100

101
export function withLengthArray(valueLength: Length|undefined, body: (valuePtr: Int32Array) => void) {
102
    withLength(valueLength, (value, unit, resource) => {
103
        let array = new Int32Array(3)
104
        array[0] = value
105
        array[1] = unit
106
        array[2] = resource
107
        body(array)
108
    })
109
}
110

111
let textEncoder = new TextEncoder()
112

113
/* Serialization extension point */
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
119
}
120

121
export class SerializerBase {
122
    private position = 0
123
    private buffer: ArrayBuffer
124
    private view: DataView
125

126
    private static customSerializers: CustomSerializer | undefined = undefined
127
    static registerCustomSerializer(serializer: CustomSerializer) {
128
        if (SerializerBase.customSerializers == undefined) {
129
            SerializerBase.customSerializers = serializer
130
        } else {
131
            let current = SerializerBase.customSerializers
132
            while (current.next != undefined) { current = current.next }
133
            current.next = serializer
134
        }
135
    }
136
    constructor(expectedSize: int32) {
137
        this.buffer = new ArrayBuffer(expectedSize)
138
        this.view = new DataView(this.buffer)
139
    }
140
    asArray(): Uint8Array {
141
        return new Uint8Array(this.buffer)
142
    }
143
    length(): int32 {
144
        return this.position
145
    }
146
    currentPosition(): int32 { return this.position }
147
    private checkCapacity(value: int32) {
148
        if (value < 1) {
149
            throw new Error(`${value} is less than 1`)
150
        }
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)
159
        }
160
    }
161
    writeCustom(kind: string, value: any) {
162
        let current = SerializerBase.customSerializers
163
        while (current) {
164
            if (current.supports(kind)) {
165
                current.serialize(this, value, kind)
166
                return
167
            }
168
        }
169
        console.log(`Unsupported custom serialization for ${kind}, write undefined`)
170
        this.writeInt8(Tags.UNDEFINED)
171
    }
172
    writeNumber(value: number|undefined) {
173
        this.checkCapacity(5)
174
        if (value == undefined) {
175
            this.view.setInt8(this.position, Tags.UNDEFINED)
176
            this.position++
177
            return
178
        }
179
        if (value == Math.round(value)) {
180
            this.view.setInt8(this.position, Tags.INT32)
181
            this.view.setInt32(this.position + 1, value, true)
182
            this.position += 5
183
            return
184
        }
185
        this.view.setInt8(this.position, Tags.FLOAT32)
186
        this.view.setFloat32(this.position + 1, value, true)
187
        this.position += 5
188
    }
189
    writeInt8(value: int32) {
190
        this.checkCapacity(1)
191
        this.view.setInt8(this.position, value)
192
        this.position += 1
193
    }
194
    writeInt32(value: int32) {
195
        this.checkCapacity(4)
196
        this.view.setInt32(this.position, value, true)
197
        this.position += 4
198
    }
199
    writeFloat32(value: float32) {
200
        this.checkCapacity(4)
201
        this.view.setFloat32(this.position, value, true)
202
        this.position += 4
203
    }
204
    writeBoolean(value: boolean|undefined) {
205
        this.checkCapacity(1)
206
        this.view.setInt8(this.position, value == undefined ? RuntimeType.UNDEFINED : +value)
207
        this.position++
208
    }
209
    writeFunction(value: object | undefined) {
210
        this.writeCustom("Function", value)
211
    }
212
    writeString(value: string|undefined) {
213
        if (value == undefined) {
214
            this.writeInt8(Tags.UNDEFINED)
215
            return
216
        }
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
223
    }
224
    writeAny(value: any) {
225
        throw new Error("How to write any?")
226
    }
227
    // Length is an important common case.
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)
237
            })
238
        }
239
    }
240
    writeAnimationRange(value: AnimationRange<number>|undefined) {
241
       if (!value) {
242
           this.writeInt8(Tags.UNDEFINED)
243
           return
244
        }
245
        this.writeInt8(Tags.OBJECT)
246
        this.writeNumber(value[0])
247
        this.writeNumber(value[1])
248
    }
249

250
    writeCallback(value: Callback<any>|undefined) {
251
        this.writeCustom("Callback", value)
252
    }
253
}
254

255
class OurCustomSerializer extends CustomSerializer {
256
    constructor() {
257
        super(["Resource", "Pixmap", "Function"])
258
    }
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))
262
    }
263
}
264

265
// TODO, remove me!
266
SerializerBase.registerCustomSerializer(new OurCustomSerializer())

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.