idlize

Форк
0
309 строк · 10.4 Кб
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 "@koalaui/common"
16
import { pointer } from "@koalaui/interop"
17
import { nativeModule } from "@koalaui/arkoala"
18
import { wrapCallback } from "./callback_registry"
19
import { FinalizableBase } from "./Finalizable"
20

21
// imports required intarfaces (now generation is disabled)
22
// import { Resource, Length, PixelMap } from "@arkoala/arkui"
23
/**
24
 * Value representing possible JS runtime object type.
25
 * Must be synced with "enum RuntimeType" in C++.
26
 */
27
export enum RuntimeType {
28
    UNEXPECTED = -1,
29
    NUMBER = 1,
30
    STRING = 2,
31
    OBJECT = 3,
32
    BOOLEAN = 4,
33
    UNDEFINED = 5,
34
    BIGINT = 6,
35
    FUNCTION = 7,
36
    SYMBOL = 8,
37
    MATERIALIZED = 9,
38
}
39

40
/**
41
 * Value representing object type in serialized data.
42
 * Must be synced with "enum Tags" in C++.
43
 */
44
export enum Tags {
45
    UNDEFINED = 101,
46
    INT32 = 102,
47
    FLOAT32 = 103,
48
    STRING = 104,
49
    LENGTH = 105,
50
    RESOURCE = 106,
51
    OBJECT = 107,
52
}
53

54
export function runtimeType(value: any): int32 {
55
    let type = typeof value
56
    if (type == "number") return RuntimeType.NUMBER
57
    if (type == "string") return RuntimeType.STRING
58
    if (type == "undefined") return RuntimeType.UNDEFINED
59
    if (type == "object") return RuntimeType.OBJECT
60
    if (type == "boolean") return RuntimeType.BOOLEAN
61
    if (type == "bigint") return RuntimeType.BIGINT
62
    if (type == "function") return RuntimeType.FUNCTION
63
    if (type == "symbol") return RuntimeType.SYMBOL
64

65
    throw new Error(`bug: ${value} is ${type}`)
66
}
67

68
export function isPixelMap(value: Object): value is PixelMap {
69
    // Object.hasOwn need es2022
70
    return value.hasOwnProperty('isEditable') && value.hasOwnProperty('isStrideAlignment')
71
}
72

73
export function isResource(value: Object): value is Resource {
74
    return value.hasOwnProperty("bundleName") && value.hasOwnProperty("moduleName")
75
}
76

77
// Poor man's instanceof, fails on subclasses
78
export function isInstanceOf(className: string, value: Object): boolean {
79
    return value.constructor.name === className
80
}
81

82
export function withLength(valueLength: Length|undefined, body: (type: int32, value: float32, unit: int32, resource: int32) => void) {
83
    let type = runtimeType(valueLength)
84
    let value = 0
85
    let unit = 1 // vp
86
    let resource = 0
87
    switch (type) {
88
        case RuntimeType.UNDEFINED:
89
            value = 0
90
            unit = 0
91
            break
92
        case RuntimeType.NUMBER:
93
            value = valueLength as float32
94
            break
95
        case RuntimeType.STRING:
96
            let valueStr = valueLength as string
97
            // TODO: faster parse.
98
            if (valueStr.endsWith("vp")) {
99
                unit = 1 // vp
100
                value = Number(valueStr.substring(0, valueStr.length - 2))
101
            } else if (valueStr.endsWith("%")) {
102
                unit = 3 // percent
103
                value = Number(valueStr.substring(0, valueStr.length - 1))
104
            } else if (valueStr.endsWith("lpx")) {
105
                unit = 4 // lpx
106
                value = Number(valueStr.substring(0, valueStr.length - 3))
107
            } else if (valueStr.endsWith("px")) {
108
                unit = 0 // px
109
                value = Number(valueStr.substring(0, valueStr.length - 2))
110
            }
111
            break
112
        case RuntimeType.OBJECT:
113
            resource = (valueLength as Resource).id
114
            break
115
    }
116
    body(type, value, unit, resource)
117
}
118

119
export function withLengthArray(valueLength: Length|undefined, body: (valuePtr: Int32Array) => void) {
120
    withLength(valueLength, (type: int32, value, unit, resource) => {
121
        let array = new Int32Array(4)
122
        array[0] = type
123
        array[1] = value
124
        array[2] = unit
125
        array[3] = resource
126
        body(array)
127
    })
128
}
129

130
export function registerCallback(value: object|undefined): int32 {
131
    return wrapCallback((args: Uint8Array, length: int32) => {
132
        // TBD: deserialize the callback arguments and call the callback
133
        return 42
134
    })
135
}
136

137
export function registerMaterialized(value: object|undefined): number {
138
    // TODO: fix me!
139
    return 42
140
}
141

142
class SerializersCache {
143
    cache: Array<SerializerBase|undefined>
144
    constructor(maxCount: number) {
145
        this.cache = new Array<SerializerBase|undefined>(maxCount)
146
    }
147
    get<T extends SerializerBase>(factory: () => T, index: int32): T {
148
        let result = this.cache[index]
149
        if (result) {
150
            result.resetCurrentPosition()
151
            return result as T
152
        }
153
        result = factory()
154
        this.cache[index] = result
155
        return result as T
156
    }
157
}
158

159
/* Serialization extension point */
160
export abstract class CustomSerializer {
161
    constructor(protected supported: Array<string>) {}
162
    supports(kind: string): boolean { return this.supported.includes(kind) }
163
    abstract serialize(serializer: SerializerBase, value: any, kind: string): void
164
    next: CustomSerializer | undefined = undefined
165
}
166

167
export class SerializerBase {
168
    private static cache = new SerializersCache(22)
169

170
    private position = 0
171
    private buffer: ArrayBuffer
172
    private view: DataView
173

174
    private static customSerializers: CustomSerializer | undefined = undefined
175
    static registerCustomSerializer(serializer: CustomSerializer) {
176
        if (SerializerBase.customSerializers == undefined) {
177
            SerializerBase.customSerializers = serializer
178
        } else {
179
            let current = SerializerBase.customSerializers
180
            while (current.next != undefined) { current = current.next }
181
            current.next = serializer
182
        }
183
    }
184
    constructor() {
185
        this.buffer = new ArrayBuffer(96)
186
        this.view = new DataView(this.buffer)
187
    }
188

189
    static get<T extends SerializerBase>(factory: () => T, index: int32): T {
190
        return SerializerBase.cache.get<T>(factory, index)
191
    }
192
    asArray(): Uint8Array {
193
        return new Uint8Array(this.buffer)
194
    }
195
    length(): int32 {
196
        return this.position
197
    }
198
    currentPosition(): int32 { return this.position }
199
    resetCurrentPosition(): void { this.position = 0 }
200

201
    private checkCapacity(value: int32) {
202
        if (value < 1) {
203
            throw new Error(`${value} is less than 1`)
204
        }
205
        let buffSize = this.buffer.byteLength
206
        if (this.position > buffSize - value) {
207
            const minSize = this.position + value
208
            const resizedSize = Math.max(minSize, Math.round(3 * buffSize / 2))
209
            let resizedBuffer = new ArrayBuffer(resizedSize)
210
            // TODO: can we grow without new?
211
            new Uint8Array(resizedBuffer).set(new Uint8Array(this.buffer))
212
            this.buffer = resizedBuffer
213
            this.view = new DataView(resizedBuffer)
214
        }
215
    }
216
    writeCustomObject(kind: string, value: any) {
217
        let current = SerializerBase.customSerializers
218
        while (current) {
219
            if (current.supports(kind)) {
220
                current.serialize(this, value, kind)
221
                return
222
            }
223
            current = current.next
224
        }
225
        console.log(`Unsupported custom serialization for ${kind}, write undefined`)
226
        this.writeInt8(Tags.UNDEFINED)
227
    }
228
    writeNumber(value: number|undefined) {
229
        this.checkCapacity(5)
230
        if (value == undefined) {
231
            this.view.setInt8(this.position, Tags.UNDEFINED)
232
            this.position++
233
            return
234
        }
235
        if (value == Math.round(value)) {
236
            this.view.setInt8(this.position, Tags.INT32)
237
            this.view.setInt32(this.position + 1, value, true)
238
            this.position += 5
239
            return
240
        }
241
        this.view.setInt8(this.position, Tags.FLOAT32)
242
        this.view.setFloat32(this.position + 1, value, true)
243
        this.position += 5
244
    }
245
    writeInt8(value: int32) {
246
        this.checkCapacity(1)
247
        this.view.setInt8(this.position, value)
248
        this.position += 1
249
    }
250
    writeInt32(value: int32) {
251
        this.checkCapacity(4)
252
        this.view.setInt32(this.position, value, true)
253
        this.position += 4
254
    }
255
    writePointer(value: pointer) {
256
        this.checkCapacity(8)
257
        this.view.setBigInt64(this.position, BigInt(value), true)
258
        this.position += 8
259
    }
260
    writeFloat32(value: float32) {
261
        this.checkCapacity(4)
262
        this.view.setFloat32(this.position, value, true)
263
        this.position += 4
264
    }
265
    writeBoolean(value: boolean|undefined) {
266
        this.checkCapacity(1)
267
        this.view.setInt8(this.position, value == undefined ? RuntimeType.UNDEFINED : +value)
268
        this.position++
269
    }
270
    writeFunction(value: object | undefined) {
271
        this.writeInt32(registerCallback(value))
272
    }
273
    writeMaterialized(value: object | undefined) {
274
        this.writePointer(value ? (value as FinalizableBase).ptr : 0)
275
    }
276
    writeString(value: string) {
277
        this.checkCapacity(4 + value.length * 4) // length, data
278
        let encodedLength =
279
            nativeModule()._ManagedStringWrite(value, new Uint8Array(this.view.buffer, 0), this.position + 4)
280
        this.view.setInt32(this.position, encodedLength, true)
281
        this.position += encodedLength + 4
282
    }
283
    // Length is an important common case.
284
    writeLength(value: Length|undefined) {
285
        this.checkCapacity(1)
286
        let valueType = runtimeType(value)
287
        this.writeInt8(valueType)
288
        if (valueType == RuntimeType.NUMBER) {
289
            this.writeFloat32(value as number)
290
        } else if (valueType == RuntimeType.STRING) {
291
            this.writeString(value as string)
292
        } else if (valueType == RuntimeType.OBJECT) {
293
            this.writeInt32((value as Resource).id)
294
        }
295
    }
296
}
297

298
class OurCustomSerializer extends CustomSerializer {
299
    constructor() {
300
        super(["Resource", "Pixmap"])
301
    }
302
    serialize(serializer: SerializerBase, value: any, kind: string): void {
303
        // console.log(`managed serialize() for ${kind}`)
304
        serializer.writeString(JSON.stringify(value))
305
    }
306
}
307

308
// TODO, remove me!
309
SerializerBase.registerCustomSerializer(new OurCustomSerializer())

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

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

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

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