feat: implement WASM phase 1 DValue ABI

This commit is contained in:
udo
2026-06-12 12:42:56 +00:00
parent 7e2faf1472
commit 80285b7fb4
8 changed files with 657 additions and 725 deletions
+356
View File
@@ -2,6 +2,8 @@
#include <cctype>
#include <cmath>
#include <limits>
#include <cstring>
#include <iomanip>
namespace {
@@ -793,6 +795,360 @@ void DValue::clear()
_array_index = 0;
}
namespace {
const char* UCEB_MAGIC = "UCEB";
const u8 UCEB_VERSION = 1;
const u8 UCEB_FLAG_LIST = 1;
thread_local String uce_dv_last_error_text;
thread_local String uce_dv_value_result;
thread_local DValue uce_dv_decode_result;
bool ucb_append_varint(String& out, u64 value)
{
while(value >= 0x80)
{
out.push_back((char)((value & 0x7f) | 0x80));
value >>= 7;
}
out.push_back((char)value);
return(true);
}
bool ucb_read_varint(const String& src, size_t& offset, u64& value_out)
{
value_out = 0;
u32 shift = 0;
while(offset < src.size() && shift <= 63)
{
u8 byte = (u8)src[offset++];
value_out |= ((u64)(byte & 0x7f) << shift);
if((byte & 0x80) == 0)
return(true);
shift += 7;
}
return(false);
}
String ucb_node_scalar(const DValue& value)
{
const DValue& target = value.deref();
switch(target.type)
{
case('S'):
return(target._String);
case('F'):
{
std::ostringstream out;
out << std::setprecision(std::numeric_limits<f64>::max_digits10) << target._float;
return(out.str());
}
case('B'):
return(target._bool ? "(true)" : "(false)");
case('P'):
return("");
default:
return("");
}
}
void ucb_encode_node(String& out, const DValue& value)
{
const DValue& target = value.deref();
u8 flags = target.is_list() ? UCEB_FLAG_LIST : 0;
out.push_back((char)flags);
String scalar = ucb_node_scalar(target);
ucb_append_varint(out, scalar.size());
out.append(scalar.data(), scalar.size());
if(target.type != 'M')
{
ucb_append_varint(out, 0);
return;
}
ucb_append_varint(out, target._map.size());
target.each([&](const DValue& child, String key) {
ucb_append_varint(out, key.size());
out.append(key.data(), key.size());
ucb_encode_node(out, child);
});
}
bool ucb_decode_node(const String& src, size_t& offset, DValue& out, String& error, u32 depth = 0)
{
if(depth > 1024)
{
error = "UCEB1 nesting limit exceeded";
return(false);
}
if(offset >= src.size())
{
error = "unexpected end of UCEB1 node";
return(false);
}
u8 flags = (u8)src[offset++];
u64 scalar_len = 0;
if(!ucb_read_varint(src, offset, scalar_len))
{
error = "invalid UCEB1 scalar length";
return(false);
}
if(scalar_len > src.size() - offset)
{
error = "UCEB1 scalar length exceeds input";
return(false);
}
String scalar(src.data() + offset, (size_t)scalar_len);
offset += (size_t)scalar_len;
u64 child_count = 0;
if(!ucb_read_varint(src, offset, child_count))
{
error = "invalid UCEB1 child count";
return(false);
}
out.clear();
if(child_count == 0 && (flags & UCEB_FLAG_LIST) == 0)
{
out = scalar;
return(true);
}
if((flags & UCEB_FLAG_LIST) != 0)
out.set_array();
for(u64 i = 0; i < child_count; i++)
{
u64 key_len = 0;
if(!ucb_read_varint(src, offset, key_len))
{
error = "invalid UCEB1 child key length";
return(false);
}
if(key_len > src.size() - offset)
{
error = "UCEB1 child key length exceeds input";
return(false);
}
String key(src.data() + offset, (size_t)key_len);
offset += (size_t)key_len;
DValue child;
if(!ucb_decode_node(src, offset, child, error, depth + 1))
return(false);
out[key] = child;
}
return(true);
}
String uce_dv_key(const char* key, size_t key_len)
{
if(key == 0)
return("");
return(String(key, key_len));
}
DValue* uce_dv_target(uce_dvalue* value)
{
if(value == 0)
return(0);
return(reinterpret_cast<DValue*>(value)->reference_target() ? reinterpret_cast<DValue*>(value)->reference_target() : reinterpret_cast<DValue*>(value));
}
const DValue* uce_dv_target_const(uce_dvalue* value)
{
if(value == 0)
return(0);
return(&reinterpret_cast<DValue*>(value)->deref());
}
}
String ucb_encode(const DValue& value)
{
String out;
out.append(UCEB_MAGIC, 4);
out.push_back((char)UCEB_VERSION);
ucb_encode_node(out, value);
return(out);
}
bool ucb_decode(const String& encoded, DValue& out, String* error_out)
{
String error;
if(encoded.size() < 5 || encoded.compare(0, 4, UCEB_MAGIC) != 0)
error = "missing UCEB magic header";
else if((u8)encoded[4] != UCEB_VERSION)
error = "unsupported UCEB version";
else
{
size_t offset = 5;
DValue decoded;
if(ucb_decode_node(encoded, offset, decoded, error) && offset == encoded.size())
{
out = decoded;
if(error_out)
*error_out = "";
return(true);
}
if(error == "")
error = "trailing bytes after UCEB1 document";
}
if(error_out)
*error_out = error;
return(false);
}
DValue ucb_decode(const String& encoded)
{
DValue out;
String error;
ucb_decode(encoded, out, &error);
return(out);
}
extern "C" {
uce_dvalue* uce_dv_root(void)
{
if(context == 0)
return(0);
return(reinterpret_cast<uce_dvalue*>(&context->call));
}
uce_dvalue* uce_dv_get(uce_dvalue* value, const char* key, size_t key_len)
{
DValue* target = uce_dv_target(value);
if(target == 0)
return(0);
return(reinterpret_cast<uce_dvalue*>(target->get_or_create(uce_dv_key(key, key_len))));
}
uce_dvalue* uce_dv_find(uce_dvalue* value, const char* key, size_t key_len)
{
DValue* target = uce_dv_target(value);
if(target == 0)
return(0);
return(reinterpret_cast<uce_dvalue*>(target->key(uce_dv_key(key, key_len))));
}
const char* uce_dv_value(uce_dvalue* value, size_t* len_out)
{
const DValue* target = uce_dv_target_const(value);
if(target == 0)
{
if(len_out)
*len_out = 0;
return(0);
}
uce_dv_value_result = ucb_node_scalar(*target);
if(len_out)
*len_out = uce_dv_value_result.size();
return(uce_dv_value_result.data());
}
void uce_dv_set_value(uce_dvalue* value, const char* bytes, size_t len)
{
DValue* target = uce_dv_target(value);
if(target == 0)
return;
if(bytes == 0 && len > 0)
{
target->set("");
return;
}
target->set(String(bytes ? bytes : "", len));
}
size_t uce_dv_count(uce_dvalue* value)
{
const DValue* target = uce_dv_target_const(value);
if(target == 0 || target->type != 'M')
return(0);
return(target->_map.size());
}
int uce_dv_is_list(uce_dvalue* value)
{
const DValue* target = uce_dv_target_const(value);
return(target && target->is_list() ? 1 : 0);
}
uce_dv_iter uce_dv_iter_begin(uce_dvalue* value)
{
uce_dv_iter iter;
iter.position = 0;
iter.reserved[0] = 0;
iter.reserved[1] = 0;
iter.reserved[2] = 0;
return(iter);
}
int uce_dv_iter_next(uce_dvalue* value, uce_dv_iter* iter, const char** key_out, size_t* key_len_out, uce_dvalue** child_out)
{
const DValue* target = uce_dv_target_const(value);
if(target == 0 || target->type != 'M' || iter == 0)
return(0);
std::map<String, DValue>::const_iterator entry;
if(target->is_list())
{
entry = target->_map.find(std::to_string((u64)iter->position));
if(entry == target->_map.end())
return(0);
}
else
{
if(iter->position >= target->_map.size())
return(0);
entry = target->_map.begin();
std::advance(entry, iter->position);
}
if(key_out)
*key_out = entry->first.data();
if(key_len_out)
*key_len_out = entry->first.size();
if(child_out)
*child_out = reinterpret_cast<uce_dvalue*>(const_cast<DValue*>(&entry->second));
iter->position += 1;
return(1);
}
size_t uce_dv_encode(uce_dvalue* value, char* buf, size_t cap)
{
const DValue* target = uce_dv_target_const(value);
if(target == 0)
return(0);
String encoded = ucb_encode(*target);
if(buf != 0 && cap > 0)
{
size_t copy_len = encoded.size() < cap ? encoded.size() : cap;
memcpy(buf, encoded.data(), copy_len);
}
return(encoded.size());
}
uce_dvalue* uce_dv_decode(const char* buf, size_t len)
{
String encoded(buf ? buf : "", buf ? len : 0);
String error;
DValue decoded;
if(!ucb_decode(encoded, decoded, &error))
{
uce_dv_last_error_text = error;
return(0);
}
uce_dv_last_error_text = "";
uce_dv_decode_result = decoded;
return(reinterpret_cast<uce_dvalue*>(&uce_dv_decode_result));
}
const char* uce_dv_last_error(void)
{
return(uce_dv_last_error_text.c_str());
}
}
String to_String(DValue t)
{
return(t.to_string());
+29
View File
@@ -67,3 +67,32 @@ struct DValue {
String to_String(DValue t);
String var_dump(const DValue& map, String prefix = "", String postfix = "\n");
String ucb_encode(const DValue& value);
DValue ucb_decode(const String& encoded);
bool ucb_decode(const String& encoded, DValue& out, String* error_out = 0);
extern "C" {
typedef struct DValue uce_dvalue;
typedef struct uce_dv_iter
{
size_t position;
size_t reserved[3];
} uce_dv_iter;
uce_dvalue* uce_dv_root(void);
uce_dvalue* uce_dv_get(uce_dvalue* value, const char* key, size_t key_len);
uce_dvalue* uce_dv_find(uce_dvalue* value, const char* key, size_t key_len);
const char* uce_dv_value(uce_dvalue* value, size_t* len_out);
void uce_dv_set_value(uce_dvalue* value, const char* bytes, size_t len);
size_t uce_dv_count(uce_dvalue* value);
int uce_dv_is_list(uce_dvalue* value);
uce_dv_iter uce_dv_iter_begin(uce_dvalue* value);
int uce_dv_iter_next(uce_dvalue* value, uce_dv_iter* iter, const char** key_out, size_t* key_len_out, uce_dvalue** child_out);
size_t uce_dv_encode(uce_dvalue* value, char* buf, size_t cap);
uce_dvalue* uce_dv_decode(const char* buf, size_t len);
const char* uce_dv_last_error(void);
}