* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _SERIALIZER_BASE_H
#define _SERIALIZER_BASE_H
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <string>
#include <memory>
#include <cstddef>
#include <vector>
#include "callback-resource.h"
#include "interop-types.h"
#include "koala-types.h"
#include "interop-logging.h"
#ifdef __arm__
#define KOALA_NO_UNALIGNED_ACCESS 1
#endif
constexpr int BUFFER_MAX_LEN = 64;
template <typename T>
inline InteropRuntimeType runtimeType(const T& value) = delete;
template <>
inline InteropRuntimeType runtimeType(const InteropCustomObject& value) {
return INTEROP_RUNTIME_OBJECT;
}
template <>
inline InteropRuntimeType runtimeType(const InteropMaterialized& value) {
return INTEROP_RUNTIME_OBJECT;
}
static const std::size_t buffer_size = 1024 * 1024;
static std::size_t offset = 0;
alignas(std::max_align_t) static char buffer[buffer_size];
template <typename T, std::size_t size>
T* allocArray(const std::array<T, size>& ref) {
std::size_t space = sizeof(buffer) - offset;
void* ptr = buffer + offset;
void* aligned_ptr = std::align(alignof(T), sizeof(T) * size, ptr, space);
ASSERT(aligned_ptr != nullptr);
offset = (char*)aligned_ptr + sizeof(T) * size - buffer;
T* array = reinterpret_cast<T*>(aligned_ptr);
for (size_t i = 0; i < size; ++i) {
new (&array[i]) T(ref[i]);
}
return array;
}
class SerializerBase {
private:
uint8_t* data;
uint32_t dataLength;
uint32_t position;
bool ownData;
CallbackResourceHolder* resourceHolder;
void resize(uint32_t newLength) {
ASSERT(ownData);
ASSERT(newLength > dataLength);
auto* newData = reinterpret_cast<uint8_t*>(malloc(newLength));
if (newData == nullptr) {
return;
}
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(newData, newLength, data, position);
if (res != EOK) {
free(newData);
return;
}
#else
memcpy(newData, data, position);
#endif
free(data);
data = newData;
}
public:
SerializerBase(CallbackResourceHolder* resourceHolder = nullptr):
position(0), ownData(true), resourceHolder(resourceHolder) {
this->dataLength = 256;
this->data = reinterpret_cast<uint8_t*>(malloc(this->dataLength));
}
SerializerBase(uint8_t* data, uint32_t dataLength, CallbackResourceHolder* resourceHolder = nullptr):
data(data), dataLength(dataLength), position(0), ownData(false), resourceHolder(resourceHolder) {
}
virtual ~SerializerBase() {
if (ownData) {
free(data);
}
}
SerializerBase(const SerializerBase&) = delete;
SerializerBase& operator=(const SerializerBase&) = delete;
void* release() {
ownData = false;
return data;
}
int length() {
return position;
}
inline void check(int more) {
if (position + more > dataLength) {
if (ownData) {
resize(dataLength * 3 / 2 + 2);
} else {
INTEROP_FATAL("Buffer overrun: %d > %d\n", position + more, dataLength);
}
}
}
void writeInt8(InteropInt8 value) {
check(1);
*((InteropInt8*)(data + position)) = value;
position += 1;
}
void writeInt32(InteropInt32 value) {
check(4);
#ifdef KOALA_NO_UNALIGNED_ACCESS
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(data + position, dataLength, &value, 4);
if (res != EOK) {
return;
}
#else
memcpy(data + position, &value, 4);
#endif
#else
*((InteropInt32*)(data + position)) = value;
#endif
position += 4;
}
void writeInt64(InteropInt64 value) {
check(8);
#ifdef KOALA_NO_UNALIGNED_ACCESS
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(data + position, dataLength, &value, 8);
if (res != EOK) {
return;
}
#else
memcpy(data + position, &value, 8);
#endif
#else
*((InteropInt64*)(data + position)) = value;
#endif
position += 8;
}
void writeUInt64(InteropUInt64 value) {
check(8);
#ifdef KOALA_NO_UNALIGNED_ACCESS
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(data + position, dataLength, &value, 8);
if (res != EOK) {
return;
}
#else
memcpy(data + position, &value, 8);
#endif
#else
*((InteropUInt64*)(data + position)) = value;
#endif
position += 8;
}
void writeFloat32(InteropFloat32 value) {
check(8);
#ifdef KOALA_NO_UNALIGNED_ACCESS
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(data + position, dataLength, &value, 4);
if (res != EOK) {
return;
}
#else
memcpy(data + position, &value, 4);
#endif
#else
*((InteropFloat32*)(data + position)) = value;
#endif
position += 4;
}
void writePointer(InteropNativePointer value) {
check(8);
#ifdef KOALA_NO_UNALIGNED_ACCESS
#ifdef __STDC_LIB_EXT1__
errno_t res = memcpy_s(data + position, dataLength, &value64, 8);
if (res != EOK) {
return;
}
#else
memcpy(data + position, &value, 8);
#endif
#else
*((int64_t*)(data + position)) = reinterpret_cast<int64_t>(value);
#endif
position += 8;
}
void writeFunction(InteropFunction value) {
writeInt32(0x666);
}
void writeNumber(InteropNumber value) {
writeInt8(value.tag);
if (value.tag == InteropTag::INTEROP_TAG_INT32) {
writeInt32(value.i32);
} else if (value.tag == InteropTag::INTEROP_TAG_FLOAT32) {
writeFloat32(value.f32);
} else {
INTEROP_FATAL("Unknown tag number");
}
}
void writeString(InteropString value) {
writeInt32(value.length + 1);
check(value.length + 1);
strcpy((char*)(data + position), value.chars);
position += value.length + 1;
}
void writeBoolean(InteropBoolean value) {
writeInt8(value);
}
void writeLength(InteropLength value) {
InteropRuntimeType tag = (InteropRuntimeType) value.type;
writeInt8(tag);
switch (tag) {
case INTEROP_RUNTIME_NUMBER:
writeFloat32(value.value);
break;
case INTEROP_RUNTIME_OBJECT:
writeInt32(value.resource);
break;
case INTEROP_RUNTIME_STRING: {
char buf[BUFFER_MAX_LEN];
std::string suffix;
switch (value.unit) {
case 0: suffix = "px"; break;
case 1: suffix = "vp"; break;
case 2: suffix = "fp"; break;
case 3: suffix = "%"; break;
case 4: suffix = "lpx"; break;
}
#ifdef __STDC_LIB_EXT1__
errno_t res = snprintf_s(buf, BUFFER_MAX_LEN, "%.8f%s", value.value, suffix.c_str());
if (res != EOK) {
return;
}
#else
snprintf(buf, BUFFER_MAX_LEN, "%.8f%s", value.value, suffix.c_str());
#endif
InteropString str = { buf, (InteropInt32) strlen(buf) };
writeString(str);
break;
}
default:
break;
}
}
void writeCallbackResource(const InteropCallbackResource resource) {
writeInt32(resource.resourceId);
writePointer(reinterpret_cast<void*>(resource.hold));
writePointer(reinterpret_cast<void*>(resource.release));
if (this->resourceHolder != nullptr) {
this->resourceHolder->holdCallbackResource(&resource);
}
}
void writeCustomObject(std::string type, InteropCustomObject value) {
}
void writeBuffer(InteropBuffer buffer) {
writeCallbackResource(buffer.resource);
writePointer((void*)buffer.data);
writeInt64(buffer.length);
}
KInteropReturnBuffer toReturnBuffer() {
if (this->ownData) {
KInteropReturnBuffer buffer {this->length(), this->release(), [](KNativePointer data, KInt length) { free(data); }};
return buffer;
} else {
return {this->length(), this->data, nullptr};
}
}
};
#endif