1 /*
2 * Copyright (c) 2025 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
16 #ifndef _SERIALIZER_BASE_H
17 #define _SERIALIZER_BASE_H
18
19 #include <cstdint>
20 #include <cstdio>
21 #include <cstring>
22 #include <string>
23 #include <memory>
24 #include <cstddef>
25 #include <vector>
26
27 #include "callback-resource.h"
28 #include "interop-types.h"
29 #include "koala-types.h"
30 #include "interop-logging.h"
31
32 #ifdef __arm__
33 #define KOALA_NO_UNALIGNED_ACCESS 1
34 #endif
35
36 constexpr int BUFFER_MAX_LEN = 64;
37
38 template <typename T>
39 inline InteropRuntimeType runtimeType(const T& value) = delete;
40
41 template <>
runtimeType(const InteropCustomObject & value)42 inline InteropRuntimeType runtimeType(const InteropCustomObject& value) {
43 return INTEROP_RUNTIME_OBJECT;
44 }
45
46 template <>
runtimeType(const InteropMaterialized & value)47 inline InteropRuntimeType runtimeType(const InteropMaterialized& value) {
48 return INTEROP_RUNTIME_OBJECT;
49 }
50
51 static const std::size_t buffer_size = 1024 * 1024; // 1 MB
52 static std::size_t offset = 0;
53 alignas(std::max_align_t) static char buffer[buffer_size];
54
55 template <typename T, std::size_t size>
allocArray(const std::array<T,size> & ref)56 T* allocArray(const std::array<T, size>& ref) {
57 std::size_t space = sizeof(buffer) - offset;
58 void* ptr = buffer + offset;
59 void* aligned_ptr = std::align(alignof(T), sizeof(T) * size, ptr, space);
60 ASSERT(aligned_ptr != nullptr); // "Insufficient space or alignment failed!"
61 offset = (char*)aligned_ptr + sizeof(T) * size - buffer;
62 T* array = reinterpret_cast<T*>(aligned_ptr);
63 for (size_t i = 0; i < size; ++i) {
64 new (&array[i]) T(ref[i]);
65 }
66 return array;
67 }
68
69 class SerializerBase {
70 private:
71 uint8_t* data;
72 uint32_t dataLength;
73 uint32_t position;
74 bool ownData;
75 CallbackResourceHolder* resourceHolder;
resize(uint32_t newLength)76 void resize(uint32_t newLength) {
77 ASSERT(ownData);
78 ASSERT(newLength > dataLength);
79 auto* newData = reinterpret_cast<uint8_t*>(malloc(newLength));
80 #ifdef __STDC_LIB_EXT1__
81 errno_t res = memcpy_s(newData, newLength, data, position);
82 if (res != EOK) {
83 return;
84 }
85 #else
86 memcpy(newData, data, position);
87 #endif
88 free(data);
89 data = newData;
90 }
91 public:
92 SerializerBase(CallbackResourceHolder* resourceHolder = nullptr):
93 position(0), ownData(true), resourceHolder(resourceHolder) {
94 this->dataLength = 256;
95 this->data = reinterpret_cast<uint8_t*>(malloc(this->dataLength));
96 }
97
98 SerializerBase(uint8_t* data, uint32_t dataLength, CallbackResourceHolder* resourceHolder = nullptr):
data(data)99 data(data), dataLength(dataLength), position(0), ownData(false), resourceHolder(resourceHolder) {
100 }
101
~SerializerBase()102 virtual ~SerializerBase() {
103 if (ownData) {
104 free(data);
105 }
106 }
107
108 SerializerBase(const SerializerBase&) = delete;
109 SerializerBase& operator=(const SerializerBase&) = delete;
110
release()111 void* release() {
112 ownData = false;
113 return data;
114 }
length()115 int length() {
116 return position;
117 }
118
check(int more)119 inline void check(int more) {
120 if (position + more > dataLength) {
121 if (ownData) {
122 resize(dataLength * 3 / 2 + 2);
123 } else {
124 INTEROP_FATAL("Buffer overrun: %d > %d\n", position + more, dataLength);
125 }
126 }
127 }
128
writeInt8(InteropInt8 value)129 void writeInt8(InteropInt8 value) {
130 check(1);
131 *((InteropInt8*)(data + position)) = value;
132 position += 1;
133 }
134
writeInt32(InteropInt32 value)135 void writeInt32(InteropInt32 value) {
136 check(4);
137 #ifdef KOALA_NO_UNALIGNED_ACCESS
138 #ifdef __STDC_LIB_EXT1__
139 errno_t res = memcpy_s(data + position, dataLength, &value, 4);
140 if (res != EOK) {
141 return;
142 }
143 #else
144 memcpy(data + position, &value, 4);
145 #endif
146 #else
147 *((InteropInt32*)(data + position)) = value;
148 #endif
149 position += 4;
150 }
151
writeInt64(InteropInt64 value)152 void writeInt64(InteropInt64 value) {
153 check(8);
154 #ifdef KOALA_NO_UNALIGNED_ACCESS
155 #ifdef __STDC_LIB_EXT1__
156 errno_t res = memcpy_s(data + position, dataLength, &value, 8);
157 if (res != EOK) {
158 return;
159 }
160 #else
161 memcpy(data + position, &value, 8);
162 #endif
163 #else
164 *((InteropInt64*)(data + position)) = value;
165 #endif
166 position += 8;
167 }
168
writeUInt64(InteropUInt64 value)169 void writeUInt64(InteropUInt64 value) {
170 check(8);
171 #ifdef KOALA_NO_UNALIGNED_ACCESS
172 #ifdef __STDC_LIB_EXT1__
173 errno_t res = memcpy_s(data + position, dataLength, &value, 8);
174 if (res != EOK) {
175 return;
176 }
177 #else
178 memcpy(data + position, &value, 8);
179 #endif
180 #else
181 *((InteropUInt64*)(data + position)) = value;
182 #endif
183 position += 8;
184 }
185
writeFloat32(InteropFloat32 value)186 void writeFloat32(InteropFloat32 value) {
187 check(8);
188 #ifdef KOALA_NO_UNALIGNED_ACCESS
189 #ifdef __STDC_LIB_EXT1__
190 errno_t res = memcpy_s(data + position, dataLength, &value, 4);
191 if (res != EOK) {
192 return;
193 }
194 #else
195 memcpy(data + position, &value, 4);
196 #endif
197 #else
198 *((InteropFloat32*)(data + position)) = value;
199 #endif
200 position += 4;
201 }
202
writePointer(InteropNativePointer value)203 void writePointer(InteropNativePointer value) {
204 check(8);
205 #ifdef KOALA_NO_UNALIGNED_ACCESS
206 #ifdef __STDC_LIB_EXT1__
207 errno_t res = memcpy_s(data + position, dataLength, &value64, 8);
208 if (res != EOK) {
209 return;
210 }
211 #else
212 memcpy(data + position, &value, 8);
213 #endif
214 #else
215 *((int64_t*)(data + position)) = reinterpret_cast<int64_t>(value);
216 #endif
217 position += 8;
218 }
219
writeFunction(InteropFunction value)220 void writeFunction(InteropFunction value) {
221 // TODO: ignored, remove!
222 writeInt32(0x666);
223 }
224
writeNumber(InteropNumber value)225 void writeNumber(InteropNumber value) {
226 writeInt8(value.tag);
227 if (value.tag == InteropTag::INTEROP_TAG_INT32) {
228 writeInt32(value.i32);
229 } else if (value.tag == InteropTag::INTEROP_TAG_FLOAT32) {
230 writeFloat32(value.f32);
231 } else {
232 INTEROP_FATAL("Unknown tag number");
233 }
234 }
235
writeString(InteropString value)236 void writeString(InteropString value) {
237 writeInt32(value.length + 1);
238 check(value.length + 1);
239 strcpy((char*)(data + position), value.chars);
240 position += value.length + 1;
241 }
242
writeBoolean(InteropBoolean value)243 void writeBoolean(InteropBoolean value) {
244 writeInt8(value);
245 }
246
writeLength(InteropLength value)247 void writeLength(InteropLength value) {
248 InteropRuntimeType tag = (InteropRuntimeType) value.type;
249 writeInt8(tag);
250 switch (tag) {
251 case INTEROP_RUNTIME_NUMBER:
252 writeFloat32(value.value);
253 break;
254 case INTEROP_RUNTIME_OBJECT:
255 writeInt32(value.resource);
256 break;
257 case INTEROP_RUNTIME_STRING: {
258 char buf[BUFFER_MAX_LEN];
259 std::string suffix;
260 switch (value.unit) {
261 case 0: suffix = "px"; break;
262 case 1: suffix = "vp"; break;
263 case 2: suffix = "fp"; break;
264 case 3: suffix = "%"; break;
265 case 4: suffix = "lpx"; break;
266 }
267 #ifdef __STDC_LIB_EXT1__
268 errno_t res = snprintf_s(buf, BUFFER_MAX_LEN, "%.8f%s", value.value, suffix.c_str());
269 if (res != EOK) {
270 return;
271 }
272 #else
273 snprintf(buf, BUFFER_MAX_LEN, "%.8f%s", value.value, suffix.c_str());
274 #endif
275 InteropString str = { buf, (InteropInt32) strlen(buf) };
276 writeString(str);
277 break;
278 }
279 default:
280 break;
281 }
282 }
283
writeCallbackResource(const InteropCallbackResource resource)284 void writeCallbackResource(const InteropCallbackResource resource) {
285 writeInt32(resource.resourceId);
286 writePointer(reinterpret_cast<void*>(resource.hold));
287 writePointer(reinterpret_cast<void*>(resource.release));
288 if (this->resourceHolder != nullptr) {
289 this->resourceHolder->holdCallbackResource(&resource);
290 }
291 }
292
writeCustomObject(std::string type,InteropCustomObject value)293 void writeCustomObject(std::string type, InteropCustomObject value) {
294 // TODO implement
295 }
296
writeBuffer(InteropBuffer buffer)297 void writeBuffer(InteropBuffer buffer) {
298 writeCallbackResource(buffer.resource);
299 writePointer((void*)buffer.data);
300 writeInt64(buffer.length);
301 }
302
toReturnBuffer()303 KInteropReturnBuffer toReturnBuffer() {
304 if (this->ownData) {
305 KInteropReturnBuffer buffer {this->length(), this->release(), [](KNativePointer data, KInt length) { free(data); }};
306 // TODO fix memory issues
307 return buffer;
308 } else {
309 return {this->length(), this->data, nullptr};
310 }
311 }
312 };
313
314 #endif // _SERIALIZER_BASE_H