This commit is contained in:
root
2026-06-15 21:42:50 +00:00
parent 34a97e2577
commit 99cd92fb4a
126 changed files with 1615 additions and 1057 deletions
+157 -17
View File
@@ -174,6 +174,74 @@ void DValue::each(std::function <void (const DValue& t, String key)> f) const
}
}
StringList DValue::keys() const
{
StringList result;
each([&](const DValue& item, String key) {
(void)item;
if(key != "")
result.push_back(key);
});
return(result);
}
DValue DValue::values() const
{
DValue result;
result.set_array();
each([&](const DValue& item, String key) {
(void)key;
result.push(item);
});
return(result);
}
DValue DValue::filter(StringList keys) const
{
DValue result;
const DValue& target = deref();
for(auto key : keys)
{
const DValue* item = target.key(key);
if(item)
result[key] = *item;
}
return(result);
}
DValue DValue::filter(std::function<bool (const DValue&, String)> f) const
{
DValue result;
bool input_is_list = is_list();
if(input_is_list)
result.set_array();
each([&](const DValue& item, String key) {
if(!f(item, key))
return;
if(key != "" && !input_is_list)
result[key] = item;
else
result.push(item);
});
return(result);
}
DValue DValue::map(std::function<DValue (const DValue&, String)> f) const
{
DValue result;
bool input_is_list = is_list();
if(input_is_list)
result.set_array();
each([&](const DValue& item, String key) {
DValue mapped = f(item, key);
if(key != "" && !input_is_list)
result[key] = mapped;
else
result.push(mapped);
});
return(result);
}
bool DValue::is_array() const
{
return(deref().type == 'M');
@@ -798,8 +866,9 @@ void DValue::clear()
namespace {
const char* UCEB_MAGIC = "UCEB";
const u8 UCEB_VERSION = 1;
const u8 UCEB_VERSION = 2;
const u8 UCEB_FLAG_LIST = 1;
const u32 UCEB_MAX_NESTING_DEPTH = 64;
thread_local String uce_dv_last_error_text;
thread_local String uce_dv_value_result;
@@ -831,6 +900,23 @@ bool ucb_read_varint(const String& src, size_t& offset, u64& value_out)
return(false);
}
char ucb_node_type(const DValue& value)
{
const DValue& target = value.deref();
switch(target.type)
{
case('M'):
case('S'):
case('F'):
case('B'):
return(target.type);
default:
// Raw pointers/references are not meaningful across the native/wasm
// membrane; preserve the historical wire behavior as an empty scalar.
return('S');
}
}
String ucb_node_scalar(const DValue& value)
{
const DValue& target = value.deref();
@@ -845,7 +931,7 @@ String ucb_node_scalar(const DValue& value)
return(out.str());
}
case('B'):
return(target._bool ? "(true)" : "(false)");
return(target._bool ? "1" : "0");
case('P'):
return("");
default:
@@ -853,11 +939,50 @@ String ucb_node_scalar(const DValue& value)
}
}
bool ucb_decode_scalar(char node_type, const String& scalar, DValue& out, String& error)
{
switch(node_type)
{
case('S'):
out = scalar;
return(true);
case('F'):
{
const char* begin = scalar.c_str();
char* end = 0;
f64 value = strtod(begin, &end);
if(end == begin || end != begin + scalar.size() || !std::isfinite(value))
{
error = "invalid UCEB2 f64 scalar";
return(false);
}
out = value;
return(true);
}
case('B'):
if(scalar == "1" || scalar == "true" || scalar == "(true)")
{
out.set_bool(true);
return(true);
}
if(scalar == "0" || scalar == "false" || scalar == "(false)")
{
out.set_bool(false);
return(true);
}
error = "invalid UCEB2 bool scalar";
return(false);
}
error = "invalid UCEB2 scalar type tag";
return(false);
}
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);
out.push_back(ucb_node_type(target));
String scalar = ucb_node_scalar(target);
ucb_append_varint(out, scalar.size());
out.append(scalar.data(), scalar.size());
@@ -877,26 +1002,32 @@ void ucb_encode_node(String& out, const DValue& value)
bool ucb_decode_node(const String& src, size_t& offset, DValue& out, String& error, u32 depth = 0)
{
if(depth > 1024)
if(depth >= UCEB_MAX_NESTING_DEPTH)
{
error = "UCEB1 nesting limit exceeded";
error = "UCEB2 nesting limit exceeded";
return(false);
}
if(offset >= src.size())
if(offset > src.size() || src.size() - offset < 2)
{
error = "unexpected end of UCEB1 node";
error = "unexpected end of UCEB2 node";
return(false);
}
u8 flags = (u8)src[offset++];
char node_type = src[offset++];
if(node_type != 'M' && node_type != 'S' && node_type != 'F' && node_type != 'B')
{
error = "invalid UCEB2 node type tag";
return(false);
}
u64 scalar_len = 0;
if(!ucb_read_varint(src, offset, scalar_len))
{
error = "invalid UCEB1 scalar length";
error = "invalid UCEB2 scalar length";
return(false);
}
if(scalar_len > src.size() - offset)
if(offset > src.size() || scalar_len > src.size() - offset)
{
error = "UCEB1 scalar length exceeds input";
error = "UCEB2 scalar length exceeds input";
return(false);
}
String scalar(src.data() + offset, (size_t)scalar_len);
@@ -905,15 +1036,19 @@ bool ucb_decode_node(const String& src, size_t& offset, DValue& out, String& err
u64 child_count = 0;
if(!ucb_read_varint(src, offset, child_count))
{
error = "invalid UCEB1 child count";
error = "invalid UCEB2 child count";
return(false);
}
out.clear();
if(child_count == 0 && (flags & UCEB_FLAG_LIST) == 0)
if(node_type != 'M')
{
out = scalar;
return(true);
if(child_count != 0 || (flags & UCEB_FLAG_LIST) != 0)
{
error = "UCEB2 scalar node cannot have children or list flag";
return(false);
}
return(ucb_decode_scalar(node_type, scalar, out, error));
}
if((flags & UCEB_FLAG_LIST) != 0)
out.set_array();
@@ -923,16 +1058,21 @@ bool ucb_decode_node(const String& src, size_t& offset, DValue& out, String& err
u64 key_len = 0;
if(!ucb_read_varint(src, offset, key_len))
{
error = "invalid UCEB1 child key length";
error = "invalid UCEB2 child key length";
return(false);
}
if(key_len > src.size() - offset)
if(offset > src.size() || key_len > src.size() - offset)
{
error = "UCEB1 child key length exceeds input";
error = "UCEB2 child key length exceeds input";
return(false);
}
String key(src.data() + offset, (size_t)key_len);
offset += (size_t)key_len;
if(depth + 1 >= UCEB_MAX_NESTING_DEPTH)
{
error = "UCEB2 nesting limit exceeded";
return(false);
}
DValue child;
if(!ucb_decode_node(src, offset, child, error, depth + 1))
return(false);
@@ -992,7 +1132,7 @@ bool ucb_decode(const String& encoded, DValue& out, String* error_out)
return(true);
}
if(error == "")
error = "trailing bytes after UCEB1 document";
error = "trailing bytes after UCEB2 document";
}
if(error_out)
*error_out = error;