idlize

Форк
0
269 строк · 9.0 Кб
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 { Length, Resource } from "./dts-exports"
18
import { NativeModule } from "@arkoala/arkui/NativeModule"
19

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

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

51
export function runtimeType(value: Object|String|number|undefined|null|void): RuntimeType {
52
    let type = typeof value
53
    if (type == "number") return RuntimeType.NUMBER
54
    if (type == "string") return RuntimeType.STRING
55
    if (type == "undefined") return RuntimeType.UNDEFINED
56
    if (type == "object") return RuntimeType.OBJECT
57
    if (type == "boolean") return RuntimeType.BOOLEAN
58
    if (type == "bigint") return RuntimeType.BIGINT
59
    if (type == "function") return RuntimeType.FUNCTION
60
    if (type == "symbol") return RuntimeType.SYMBOL
61

62
    throw new Error(`bug: ${value} is ${type}`)
63
}
64

65
export function registerCallback(value: object): int32 {
66
    // TODO: fix me!
67
    return 42
68
}
69

70
function registerMaterialized(value: Object): int32 {
71
    // TODO: fix me!
72
    return 42
73
}
74

75
export function isPixelMap(value: Object|undefined): boolean {
76
    // TODO: fix me!
77
    return false
78
}
79

80
export function isResource(value: Object|undefined): boolean {
81
    // TODO: fix me!
82
    return false
83
}
84

85
export function isInstanceOf(className: string, value: Object): boolean {
86
    // TODO: fix me!
87
    return false
88
}
89

90
/* Serialization extension point */
91
export abstract class CustomSerializer {
92
    protected supported: Array<string>
93
    constructor(supported: Array<string>) {
94
        this.supported = supported
95
    }
96
    supports(kind: string): boolean { return this.supported.includes(kind) }
97
    abstract serialize(serializer: SerializerBase, value: object, kind: string): void
98
    next: CustomSerializer | undefined = undefined
99
}
100

101
class SerializersCache {
102
    cache: Array<SerializerBase|undefined>
103
    constructor(maxCount: number) {
104
        this.cache = new Array<SerializerBase|undefined>(maxCount)
105
    }
106
    get<T extends SerializerBase>(factory: () => T, index: int32): T {
107
        let result = this.cache[index]
108
        if (result) {
109
            result.resetCurrentPosition()
110
            return result as T
111
        }
112
        result = factory()
113
        this.cache[index] = result
114
        return result as T
115
    }
116
}
117

118
export class SerializerBase {
119
    private static cache = new SerializersCache(22)
120

121
    private position = 0
122
    private buffer: byte[]
123

124
    private static customSerializers: CustomSerializer | undefined = undefined
125
    static registerCustomSerializer(serializer: CustomSerializer) {
126
        if (SerializerBase.customSerializers == undefined) {
127
            SerializerBase.customSerializers = serializer
128
        } else {
129
            let current = SerializerBase.customSerializers
130
            while (current!.next != undefined) {
131
                current = current!.next
132
            }
133
            current!.next = serializer
134
        }
135
    }
136
    resetCurrentPosition(): void { this.position = 0 }
137

138
    constructor() {
139
        this.buffer = new byte[96]
140
    }
141
    static get<T extends SerializerBase>(factory: () => T, index: int32): T {
142
        return SerializerBase.cache.get<T>(factory, index)
143
    }
144
    asArray(): byte[] {
145
        return this.buffer
146
    }
147
    length(): int32 {
148
        return this.position
149
    }
150
    currentPosition(): int32 { return this.position }
151
    private checkCapacity(value: int32) {
152
        if (value < 1) {
153
            throw new Error(`${value} is less than 1`)
154
        }
155
        let buffSize = this.buffer.length
156
        if (this.position > buffSize - value) {
157
            const minSize = this.position + value
158
            const resizedSize = Math.max(minSize, Math.round(3 * buffSize / 2))
159
            let resizedBuffer = new byte[resizedSize]
160
            for (let i = 0; i < this.buffer.length; i++) {
161
                resizedBuffer[i] = this.buffer[i]
162
            }
163
            this.buffer = resizedBuffer
164
        }
165
    }
166
    writeCustomObject(kind: string, value: object) {
167
        let current = SerializerBase.customSerializers
168
        while (current) {
169
            if (current!.supports(kind)) {
170
                current!.serialize(this, value, kind)
171
                return
172
            }
173
            current = current!.next
174
        }
175
        console.log(`Unsupported custom serialization for ${kind}, write undefined`)
176
        this.writeInt8(Tags.UNDEFINED as int32)
177
    }
178
    writeFunction(value: Object) {
179
        this.writeInt32(registerCallback(value))
180
    }
181
    writeTag(tag: int32): void {
182
        this.buffer[this.position] = tag as byte
183
        this.position++
184
    }
185
    writeNumber(value: number|undefined) {
186
        this.checkCapacity(5)
187
        if (value == undefined) {
188
            this.writeTag(Tags.UNDEFINED)
189
            this.position++
190
            return
191
        }
192
        if ((value as double) == Math.round(value)) {
193
            this.writeTag(Tags.INT32)
194
            this.writeInt32(value as int32)
195
            return
196
        } else {
197
            this.writeTag(Tags.FLOAT32)
198
            this.writeFloat32(value as float32)
199
        }
200
    }
201
    writeInt8(value: int32) {
202
        this.checkCapacity(1)
203
        this.buffer[this.position] = value as byte
204
        this.position += 1
205
    }
206
    private setInt32(position: int32, value: int32): void {
207
        this.buffer[position + 0] = ((value      ) & 0xff) as byte
208
        this.buffer[position + 1] = ((value >>  8) & 0xff) as byte
209
        this.buffer[position + 2] = ((value >> 16) & 0xff) as byte
210
        this.buffer[position + 3] = ((value >> 24) & 0xff) as byte
211
    }
212
    writeInt32(value: int32) {
213
        this.checkCapacity(4)
214
        this.setInt32(this.position, value)
215
        this.position += 4
216
    }
217
    writeFloat32(value: float32) {
218
        // TODO: this is wrong!
219
        this.checkCapacity(4)
220
        this.buffer[this.position + 0] = ((value      ) & 0xff) as byte
221
        this.buffer[this.position + 1] = ((value >>  8) & 0xff) as byte
222
        this.buffer[this.position + 2] = ((value >> 16) & 0xff) as byte
223
        this.buffer[this.position + 3] = ((value >> 24) & 0xff) as byte
224
        this.position += 4
225
    }
226
    writePointer(value: pointer) {
227
        this.checkCapacity(8)
228
        this.buffer[this.position + 0] = ((value      ) & 0xff) as byte
229
        this.buffer[this.position + 1] = ((value >>  8) & 0xff) as byte
230
        this.buffer[this.position + 2] = ((value >> 16) & 0xff) as byte
231
        this.buffer[this.position + 3] = ((value >> 24) & 0xff) as byte
232
        this.buffer[this.position + 4] = ((value >> 32) & 0xff) as byte
233
        this.buffer[this.position + 5] = ((value >> 40) & 0xff) as byte
234
        this.buffer[this.position + 6] = ((value >> 48) & 0xff) as byte
235
        this.buffer[this.position + 7] = ((value >> 56) & 0xff) as byte
236
        this.position += 8
237
    }
238
    writeBoolean(value: boolean|undefined) {
239
        this.checkCapacity(1)
240
        if (value == undefined) {
241
            this.buffer[this.position] = RuntimeType.UNDEFINED as int32 as byte
242
        } else {
243
            this.buffer[this.position] = (value ? 1 : 0) as byte
244
        }
245
        this.position++
246
    }
247
    writeMaterialized(value: Object) {
248
        this.writePointer(registerMaterialized(value))
249
    }
250
    writeString(value: string) {
251
        this.checkCapacity((4 + value.length * 4 + 1) as int32) // length, data
252
        let encodedLength = NativeModule._ManagedStringWrite(value, this.buffer, this.position + 4)
253
        this.setInt32(this.position, encodedLength)
254
        this.position += encodedLength + 4
255
    }
256
    // Length is an important common case.
257
    writeLength(value: Length|undefined) {
258
        this.checkCapacity(1)
259
        let valueType = runtimeType(value)
260
        this.writeInt8(valueType as int32)
261
        if (valueType == RuntimeType.NUMBER) {
262
            this.writeFloat32(value as float32)
263
        } else if (valueType == RuntimeType.STRING) {
264
            this.writeString(value as string)
265
        } else if (valueType == RuntimeType.OBJECT) {
266
            this.writeInt32((value as Resource).id as int32)
267
        }
268
    }
269
}
270

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

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

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

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