tildefriends/src/serialize.c

437 lines
13 KiB
C
Raw Normal View History

#include "serialize.h"
#include "task.h"
#include "taskstub.js.h"
#include "util.js.h"
#include <string.h>
#include <stdio.h>
#include "quickjs-libc.h"
#include <assert.h>
typedef enum _serialize_type_t {
kUndefined,
kNull,
kUninitialized,
kBoolean,
kInt32,
kInt64,
kNumber,
kString,
kArray,
kArrayBuffer,
kObject,
kFunction,
kError,
kException,
} serialize_type_t;
typedef struct _buffer_t {
char* data;
size_t size;
size_t capacity;
} buffer_t;
static bool _serialize_store(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value);
static JSValue _serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size);
static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value, int depth);
static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, const char** buffer, size_t* size, int depth);
static void _serialize_writeInt8(buffer_t* buffer, int8_t value);
static void _serialize_writeInt32(buffer_t* buffer, int32_t value);
static void _serialize_writeInt64(buffer_t* buffer, int64_t value);
static void _serialize_writeDouble(buffer_t* buffer, double value);
static int8_t _serialize_readInt8(const char** buffer, size_t* size);
static int32_t _serialize_readInt32(const char** buffer, size_t* size);
static int64_t _serialize_readInt64(const char** buffer, size_t* size);
static double _serialize_readDouble(const char** buffer, size_t* size);
void tf_serialize_store(tf_task_t* task, tf_taskstub_t* to, void** out_buffer, size_t* out_size, JSValue value)
{
buffer_t tmp = { 0 };
_serialize_store(task, to, &tmp, value);
tmp.data = realloc(tmp.data, tmp.size);
*out_buffer = tmp.data;
*out_size = tmp.size;
}
JSValue tf_serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size)
{
return _serialize_load(task, from, buffer, size);
}
static void _buffer_append(buffer_t* buffer, const void* data, size_t size)
{
if (buffer->capacity < buffer->size + size)
{
size_t new_capacity = (size + buffer->capacity) * 2;
buffer->data = realloc(buffer->data, new_capacity);
buffer->capacity = new_capacity;
}
memcpy((char*)buffer->data + buffer->size, data, size);
buffer->size += size;
}
void _serialize_writeInt8(buffer_t* buffer, int8_t value)
{
_buffer_append(buffer, &value, sizeof(value));
}
void _serialize_writeInt32(buffer_t* buffer, int32_t value)
{
_buffer_append(buffer, &value, sizeof(value));
}
void _serialize_writeInt64(buffer_t* buffer, int64_t value)
{
_buffer_append(buffer, &value, sizeof(value));
}
void _serialize_writeDouble(buffer_t* buffer, double value)
{
_buffer_append(buffer, &value, sizeof(value));
}
static void _serialize_read(const char** buffer, size_t* size, void* target, size_t target_size)
{
assert(*size >= target_size);
memcpy(target, *buffer, target_size);
*buffer += target_size;
*size -= target_size;
}
static int8_t _serialize_readInt8(const char** buffer, size_t* size)
{
int8_t result;
_serialize_read(buffer, size, &result, sizeof(result));
return result;
}
int32_t _serialize_readInt32(const char** buffer, size_t* size)
{
int32_t result;
_serialize_read(buffer, size, &result, sizeof(result));
return result;
}
int64_t _serialize_readInt64(const char** buffer, size_t* size)
{
int64_t result;
_serialize_read(buffer, size, &result, sizeof(result));
return result;
}
double _serialize_readDouble(const char** buffer, size_t* size)
{
double result;
_serialize_read(buffer, size, &result, sizeof(result));
return result;
}
static bool _serialize_store(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value)
{
return _serialize_storeInternal(task, to, buffer, value, 0);
}
static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value, int depth)
{
JSContext* context = tf_task_get_context(task);
size_t size;
size_t offset;
size_t element_size;
JSValue typed;
uint8_t* bytes;
if (JS_IsUndefined(value))
{
_serialize_writeInt32(buffer, kUndefined);
}
else if (JS_IsUninitialized(value))
{
_serialize_writeInt32(buffer, kUninitialized);
}
else if (JS_IsNull(value))
{
_serialize_writeInt32(buffer, kNull);
}
else if (JS_IsBool(value))
{
_serialize_writeInt32(buffer, kBoolean);
_serialize_writeInt8(buffer, JS_ToBool(tf_task_get_context(task), value) ? 1 : 0);
}
else if (JS_IsNumber(value))
{
int64_t result = 0;
if (JS_ToInt64(tf_task_get_context(task), &result, value) == 0)
{
_serialize_writeInt32(buffer, kInt64);
_serialize_writeInt64(buffer, result);
}
else
{
fprintf(stderr, "Unable to store integer.\n");
}
}
else if (JS_IsNumber(value))
{
double result = 0.0;
if (JS_ToFloat64(tf_task_get_context(task), &result, value) == 0)
{
_serialize_writeInt32(buffer, kNumber);
_serialize_writeDouble(buffer, result);
}
else
{
fprintf(stderr, "Unable to store number.\n");
}
}
else if (JS_IsString(value))
{
size_t len = 0;
const char* result = JS_ToCStringLen(tf_task_get_context(task), &len, value);
_serialize_writeInt32(buffer, kString);
_serialize_writeInt32(buffer, (int32_t)len);
_buffer_append(buffer, result, len);
JS_FreeCString(tf_task_get_context(task), result);
}
else if ((bytes = tf_util_try_get_array_buffer(tf_task_get_context(task), &size, value)) != 0)
{
_serialize_writeInt32(buffer, kArrayBuffer);
_serialize_writeInt32(buffer, (int32_t)size);
_buffer_append(buffer, bytes, size);
}
else if (!JS_IsException((typed = tf_util_try_get_typed_array_buffer(tf_task_get_context(task), value, &offset, &size, &element_size))))
{
size_t total_size;
uint8_t* bytes = tf_util_try_get_array_buffer(tf_task_get_context(task), &total_size, typed);
_serialize_writeInt32(buffer, kArrayBuffer);
_serialize_writeInt32(buffer, (int32_t)size);
_buffer_append(buffer, bytes, size);
JS_FreeValue(tf_task_get_context(task), typed);
}
else if (JS_IsArray(tf_task_get_context(task), value))
{
_serialize_writeInt32(buffer, kArray);
JSValue length_val = JS_GetPropertyStr(tf_task_get_context(task), value, "length");
int length;
if (JS_ToInt32(tf_task_get_context(task), &length, length_val) == 0)
{
_serialize_writeInt32(buffer, length);
for (int i = 0; i < length; ++i)
{
_serialize_storeInternal(task, to, buffer, JS_GetPropertyUint32(tf_task_get_context(task), value, i), depth + 1);
}
}
else
{
_serialize_writeInt32(buffer, 0);
}
}
else if (JS_IsFunction(tf_task_get_context(task), value))
{
_serialize_writeInt32(buffer, kFunction);
exportid_t exportId = tf_task_export_function(task, to, value);
_serialize_writeInt32(buffer, exportId);
}
else if (JS_IsException(value))
{
JSValue exception = JS_GetException(context);
JSValue error = JS_NewObject(context);
JSValue message = JS_GetPropertyStr(context, exception, "message");
if (!JS_IsException(message))
{
JS_SetPropertyStr(context, error, "message", message);
}
if (JS_IsError(context, exception))
{
JSValue stack = JS_GetPropertyStr(context, exception, "stack");
if (!JS_IsUndefined(stack))
{
JS_SetPropertyStr(context, error, "stack", JS_DupValue(context, stack));
}
}
_serialize_writeInt32(buffer, kException);
_serialize_storeInternal(task, to, buffer, error, depth + 1);
JS_FreeValue(context, error);
}
else if (JS_IsError(tf_task_get_context(task), value))
{
_serialize_writeInt32(buffer, kError);
JSPropertyEnum* ptab;
uint32_t plen;
JS_GetOwnPropertyNames(tf_task_get_context(task), &ptab, &plen, value, JS_GPN_STRING_MASK);
_serialize_writeInt32(buffer, plen);
for (uint32_t i = 0; i < plen; ++i)
{
JSValue key = JS_AtomToString(tf_task_get_context(task), ptab[i].atom);
JSPropertyDescriptor desc;
JSValue key_value = JS_NULL;
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1)
{
key_value = desc.value;
JS_FreeValue(tf_task_get_context(task), desc.setter);
JS_FreeValue(tf_task_get_context(task), desc.getter);
}
_serialize_storeInternal(task, to, buffer, key, depth + 1);
_serialize_storeInternal(task, to, buffer, key_value, depth + 1);
JS_FreeValue(tf_task_get_context(task), key);
JS_FreeValue(tf_task_get_context(task), key_value);
}
for (uint32_t i = 0; i < plen; ++i)
{
JS_FreeAtom(tf_task_get_context(task), ptab[i].atom);
}
js_free(tf_task_get_context(task), ptab);
}
else if (JS_IsObject(value))
{
_serialize_writeInt32(buffer, kObject);
JSPropertyEnum* ptab;
uint32_t plen;
JS_GetOwnPropertyNames(tf_task_get_context(task), &ptab, &plen, value, JS_GPN_STRING_MASK);
_serialize_writeInt32(buffer, plen);
for (uint32_t i = 0; i < plen; ++i)
{
JSValue key = JS_AtomToString(tf_task_get_context(task), ptab[i].atom);
JSPropertyDescriptor desc;
JSValue key_value = JS_NULL;
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1)
{
key_value = desc.value;
JS_FreeValue(tf_task_get_context(task), desc.setter);
JS_FreeValue(tf_task_get_context(task), desc.getter);
}
_serialize_storeInternal(task, to, buffer, key, depth + 1);
_serialize_storeInternal(task, to, buffer, key_value, depth + 1);
JS_FreeValue(tf_task_get_context(task), key);
JS_FreeValue(tf_task_get_context(task), key_value);
}
for (uint32_t i = 0; i < plen; ++i)
{
JS_FreeAtom(tf_task_get_context(task), ptab[i].atom);
}
js_free(tf_task_get_context(task), ptab);
}
else
{
fprintf(stderr, "Unknown JSValue type: %d.\n", JS_VALUE_GET_TAG(value));
abort();
}
return true;
}
static JSValue _serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size)
{
return _serialize_loadInternal(task, from, &buffer, &size, 0);
}
static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, const char** buffer, size_t* size, int depth)
{
if (*size < sizeof(int32_t))
{
return JS_UNDEFINED;
}
else
{
int32_t type = _serialize_readInt32(buffer, size);
JSValue result = JS_UNDEFINED;
switch (type)
{
case kUndefined:
result = JS_UNDEFINED;
break;
case kNull:
result = JS_NULL;
break;
case kUninitialized:
result = JS_UNINITIALIZED;
break;
case kBoolean:
result = JS_NewBool(tf_task_get_context(task), _serialize_readInt8(buffer, size) != 0);
break;
case kInt32:
result = JS_NewInt32(tf_task_get_context(task), _serialize_readInt32(buffer, size));
break;
case kInt64:
result = JS_NewInt64(tf_task_get_context(task), _serialize_readInt64(buffer, size));
break;
case kNumber:
result = JS_NewFloat64(tf_task_get_context(task), _serialize_readDouble(buffer, size));
break;
case kString:
{
int32_t length = _serialize_readInt32(buffer, size);
result = JS_NewStringLen(tf_task_get_context(task), *buffer, length);
*buffer += length;
*size -= length;
}
break;
case kArrayBuffer:
{
int32_t length = _serialize_readInt32(buffer, size);
result = JS_NewArrayBufferCopy(tf_task_get_context(task), (const uint8_t*)*buffer, length);
*buffer += length;
*size -= length;
}
break;
case kArray:
{
int32_t length = _serialize_readInt32(buffer, size);
result = JS_NewArray(tf_task_get_context(task));
for (int i = 0; i < length; ++i)
{
JS_SetPropertyUint32(tf_task_get_context(task), result, i, _serialize_loadInternal(task, from, buffer, size, depth + 1));
}
}
break;
case kFunction:
{
exportid_t exportId = _serialize_readInt32(buffer, size);
result = tf_task_add_import(task, tf_taskstub_get_id(from), exportId);
}
break;
case kException:
{
_serialize_readInt32(buffer, size);
JSValue error = JS_NewError(tf_task_get_context(task));
int32_t length = _serialize_readInt32(buffer, size);
for (int i = 0; i < length; ++i)
{
JSValue key = _serialize_loadInternal(task, from, buffer, size, depth + 1);
JSValue value = _serialize_loadInternal(task, from, buffer, size, depth + 1);
const char* key_str = JS_ToCString(tf_task_get_context(task), key);
JS_SetPropertyStr(tf_task_get_context(task), error, key_str, value);
JS_FreeCString(tf_task_get_context(task), key_str);
JS_FreeValue(tf_task_get_context(task), key);
}
result = error;
}
break;
case kError:
case kObject:
{
int32_t length = _serialize_readInt32(buffer, size);
result = JS_NewObject(tf_task_get_context(task));
for (int i = 0; i < length; ++i)
{
JSValue key = _serialize_loadInternal(task, from, buffer, size, depth + 1);
JSValue value = _serialize_loadInternal(task, from, buffer, size, depth + 1);
const char* key_str = JS_ToCString(tf_task_get_context(task), key);
JS_SetPropertyStr(tf_task_get_context(task), result, key_str, value);
JS_FreeCString(tf_task_get_context(task), key_str);
JS_FreeValue(tf_task_get_context(task), key);
}
}
break;
default:
abort();
break;
}
return result;
}
}