docs
This commit is contained in:
+157
-17
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user