docs
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
const u64 UCE_UNIT_ABI_VERSION = 6;
|
||||
const u64 UCE_UNIT_ABI_VERSION = 7;
|
||||
|
||||
struct SharedUnitFilesystemState
|
||||
{
|
||||
@@ -46,7 +46,7 @@ bool compiler_jit_compile_on_request_enabled(Request* context)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
return(true);
|
||||
return(config_bool("JIT_COMPILE_ON_REQUEST", true));
|
||||
return(to_bool(context->server->config["JIT_COMPILE_ON_REQUEST"], true));
|
||||
}
|
||||
|
||||
bool compiler_is_u64_string(String value)
|
||||
@@ -150,6 +150,8 @@ bool compiler_failure_retry_deferred(Request* context, SharedUnit* su, const Sha
|
||||
return(false);
|
||||
if(state.source_time == 0)
|
||||
return(false);
|
||||
if(!state.metadata_exists || !state.metadata_parsed || !state.abi_compatible || !state.input_signature_matches)
|
||||
return(false);
|
||||
auto required_failure_inputs_time = std::max({
|
||||
state.source_time,
|
||||
state.setup_template_time,
|
||||
|
||||
+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;
|
||||
|
||||
@@ -23,6 +23,11 @@ struct DValue {
|
||||
// conversions take an optional default that is returned when the value is
|
||||
// missing (empty) or cannot be converted to the requested type.
|
||||
void each(std::function <void (const DValue& t, String key)> f) const;
|
||||
StringList keys() const;
|
||||
DValue values() const;
|
||||
DValue filter(StringList keys) const;
|
||||
DValue filter(std::function<bool (const DValue&, String)> f) const;
|
||||
DValue map(std::function<DValue (const DValue&, String)> f) const;
|
||||
bool is_array() const;
|
||||
bool is_list() const;
|
||||
String to_string(String default_value = "") const;
|
||||
|
||||
+157
-98
@@ -112,95 +112,6 @@ String list_find(StringList items, std::function<bool (String)> f, String fallba
|
||||
return(fallback);
|
||||
}
|
||||
|
||||
StringList dv_keys(DValue tree)
|
||||
{
|
||||
StringList result;
|
||||
tree.each([&](const DValue& item, String key) {
|
||||
if(key != "")
|
||||
result.push_back(key);
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
DValue dv_values(DValue tree)
|
||||
{
|
||||
DValue result;
|
||||
result.set_array();
|
||||
tree.each([&](const DValue& item, String key) {
|
||||
result.push(item);
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
DValue dv_pick(DValue tree, StringList keys)
|
||||
{
|
||||
DValue result;
|
||||
for(auto key : keys)
|
||||
{
|
||||
DValue* item = tree.key(key);
|
||||
if(item)
|
||||
result[key] = *item;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DValue dv_omit(DValue tree, StringList keys)
|
||||
{
|
||||
DValue result;
|
||||
std::set<String> omitted(keys.begin(), keys.end());
|
||||
tree.each([&](const DValue& item, String key) {
|
||||
if(key != "" && omitted.find(key) == omitted.end())
|
||||
result[key] = item;
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
DValue dv_map(DValue tree, std::function<DValue (const DValue&, String)> f)
|
||||
{
|
||||
DValue result;
|
||||
bool input_is_list = tree.is_list();
|
||||
if(input_is_list)
|
||||
result.set_array();
|
||||
tree.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);
|
||||
}
|
||||
|
||||
DValue dv_filter(DValue tree, std::function<bool (const DValue&, String)> f)
|
||||
{
|
||||
DValue result;
|
||||
bool input_is_list = tree.is_list();
|
||||
if(input_is_list)
|
||||
result.set_array();
|
||||
tree.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 dv_group_by(DValue tree, std::function<String (const DValue&, String)> f)
|
||||
{
|
||||
DValue result;
|
||||
tree.each([&](const DValue& item, String key) {
|
||||
String group = f(item, key);
|
||||
DValue* group_items = result.get_or_create(group);
|
||||
if(!group_items->is_array())
|
||||
group_items->set_array();
|
||||
group_items->push(item);
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
String substr(String s, s64 start_pos)
|
||||
{
|
||||
s64 len = s.length();
|
||||
@@ -643,7 +554,7 @@ StringList regex_split(String pattern, String subject, String flags)
|
||||
#else
|
||||
|
||||
// PCRE2 is not compiled into the wasm core; regex runs host-side (the host
|
||||
// already links libpcre2). One UCEB1-marshalled hostcall carries the request
|
||||
// already links libpcre2). One UCEB2-marshalled hostcall carries the request
|
||||
// {op,pattern,subject,flags,replacement} in and the result tree out — the host
|
||||
// runs the native regex_* and packs the answer. See uce_host_regex in
|
||||
// src/wasm/worker.cpp.
|
||||
@@ -1063,6 +974,48 @@ f64 float_val(String s)
|
||||
return(strtod(s.c_str(), 0));
|
||||
}
|
||||
|
||||
u64 to_u64(String s, u64 fallback)
|
||||
{
|
||||
String raw = trim(s);
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
char* end = 0;
|
||||
unsigned long long value = strtoull(raw.c_str(), &end, 10);
|
||||
return(end && *end == 0 ? (u64)value : fallback);
|
||||
}
|
||||
|
||||
s64 to_s64(String s, s64 fallback)
|
||||
{
|
||||
String raw = trim(s);
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
char* end = 0;
|
||||
long long value = strtoll(raw.c_str(), &end, 10);
|
||||
return(end && *end == 0 ? (s64)value : fallback);
|
||||
}
|
||||
|
||||
f64 to_f64(String s, f64 fallback)
|
||||
{
|
||||
String raw = trim(s);
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
char* end = 0;
|
||||
double value = strtod(raw.c_str(), &end);
|
||||
return(end && *end == 0 ? (f64)value : fallback);
|
||||
}
|
||||
|
||||
bool to_bool(String s, bool fallback)
|
||||
{
|
||||
String raw = trim(to_lower(s));
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
if(raw == "1" || raw == "true" || raw == "yes" || raw == "on")
|
||||
return(true);
|
||||
if(raw == "0" || raw == "false" || raw == "no" || raw == "off")
|
||||
return(false);
|
||||
return(fallback);
|
||||
}
|
||||
|
||||
String nibble(String& haystack, String delim)
|
||||
{
|
||||
auto idx = haystack.find(delim);
|
||||
@@ -2255,6 +2208,58 @@ DValue yaml_decode(String s)
|
||||
return(parser.parse_document());
|
||||
}
|
||||
|
||||
static bool json_hex_to_u32(String s, u32 start, u32& value)
|
||||
{
|
||||
value = 0;
|
||||
if(start > s.length() || s.length() - start < 4)
|
||||
return(false);
|
||||
for(u32 i = 0; i < 4; i += 1)
|
||||
{
|
||||
char c = s[start + i];
|
||||
if(c >= '0' && c <= '9')
|
||||
value = (value << 4) | (c - '0');
|
||||
else if(c >= 'a' && c <= 'f')
|
||||
value = (value << 4) | (c - 'a' + 10);
|
||||
else if(c >= 'A' && c <= 'F')
|
||||
value = (value << 4) | (c - 'A' + 10);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
static bool json_decode_unicode_escape(String s, u32& i, String& result)
|
||||
{
|
||||
u32 codepoint = 0;
|
||||
if(i >= s.length() || s[i] != 'u' || !json_hex_to_u32(s, i + 1, codepoint))
|
||||
return(false);
|
||||
|
||||
u32 next_sequence_start = i + 5;
|
||||
if(codepoint >= 0xd800 && codepoint <= 0xdbff)
|
||||
{
|
||||
u32 low = 0;
|
||||
if(next_sequence_start <= s.length() && s.length() - next_sequence_start >= 6 && s[next_sequence_start] == '\\' && s[next_sequence_start + 1] == 'u' && json_hex_to_u32(s, next_sequence_start + 2, low) && low >= 0xdc00 && low <= 0xdfff)
|
||||
{
|
||||
codepoint = 0x10000 + (((codepoint - 0xd800) << 10) | (low - 0xdc00));
|
||||
String utf8 = xml_utf8_from_codepoint(codepoint);
|
||||
if(utf8 != "")
|
||||
result += utf8;
|
||||
i += 10;
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
if(codepoint >= 0xdc00 && codepoint <= 0xdfff)
|
||||
return(false);
|
||||
|
||||
String utf8 = xml_utf8_from_codepoint(codepoint);
|
||||
if(utf8 == "")
|
||||
return(false);
|
||||
result += utf8;
|
||||
i += 4;
|
||||
return(true);
|
||||
}
|
||||
|
||||
// https://i.stack.imgur.com/SHLOB.gif
|
||||
String json_decode_String(String s, u32& i, char termination_char)
|
||||
{
|
||||
@@ -2271,6 +2276,9 @@ String json_decode_String(String s, u32& i, char termination_char)
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
if(i + 1 >= s.length())
|
||||
return(result);
|
||||
|
||||
i += 1;
|
||||
c = s[i];
|
||||
switch(c)
|
||||
@@ -2293,9 +2301,16 @@ String json_decode_String(String s, u32& i, char termination_char)
|
||||
case('f'):
|
||||
result.append(1, '\f');
|
||||
break;
|
||||
case('u'):
|
||||
// todo decode
|
||||
case('"'):
|
||||
result.append(1, '"');
|
||||
break;
|
||||
case('\''):
|
||||
result.append(1, '\'');
|
||||
break;
|
||||
case('u'):
|
||||
if(i <= s.length() && s.length() - i >= 5 && json_decode_unicode_escape(s, i, result))
|
||||
break;
|
||||
return(result);
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
@@ -2342,19 +2357,58 @@ String json_decode_keyword(String s, u32& i)
|
||||
String json_decode_number(String s, u32& i)
|
||||
{
|
||||
String result;
|
||||
bool saw_dot = false;
|
||||
bool saw_exponent = false;
|
||||
|
||||
json_consume_space(s, i);
|
||||
if(i < s.length() && s[i] == '-')
|
||||
{
|
||||
result.append(1, '-');
|
||||
i += 1;
|
||||
}
|
||||
if(i >= s.length() || !isdigit(s[i]))
|
||||
return("");
|
||||
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(isdigit(c) || c == '.')
|
||||
if(isdigit(c))
|
||||
{
|
||||
result.append(1, c);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if(c == '.' && !saw_dot && !saw_exponent)
|
||||
{
|
||||
return(result);
|
||||
result.append(1, c);
|
||||
saw_dot = true;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
i += 1;
|
||||
if((c == 'e' || c == 'E') && !saw_exponent)
|
||||
{
|
||||
if(i + 1 >= s.length())
|
||||
break;
|
||||
u32 exponent_start = i;
|
||||
u32 next = i + 1;
|
||||
if(s[next] == '+' || s[next] == '-')
|
||||
next += 1;
|
||||
if(next < s.length() && isdigit(s[next]))
|
||||
{
|
||||
result.append(1, s[i]);
|
||||
i = i + 1;
|
||||
if(i < s.length() && (s[i] == '+' || s[i] == '-'))
|
||||
{
|
||||
result.append(1, s[i]);
|
||||
i += 1;
|
||||
}
|
||||
saw_exponent = true;
|
||||
continue;
|
||||
}
|
||||
i = exponent_start;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
@@ -2364,6 +2418,9 @@ DValue json_decode_value(String s, u32& i)
|
||||
DValue result;
|
||||
String value = "";
|
||||
json_consume_space(s, i);
|
||||
if(i >= s.length())
|
||||
return(result);
|
||||
|
||||
char c = s[i];
|
||||
//print("json_decode_value " + s.substr(i) + "\n");
|
||||
if(c == '"' || c == '\'') // String value
|
||||
@@ -2373,7 +2430,7 @@ DValue json_decode_value(String s, u32& i)
|
||||
result._String = json_decode_String(s, i, s[i-1]);
|
||||
return(result);
|
||||
}
|
||||
else if(isdigit(c))
|
||||
else if(c == '-' || isdigit(c))
|
||||
{
|
||||
result.type = 'S';
|
||||
result._String = json_decode_number(s, i);
|
||||
@@ -2426,9 +2483,11 @@ DValue json_decode_map(String s, u32& i)
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
i += 1;
|
||||
if(i >= s.length())
|
||||
return(result);
|
||||
key = json_decode_String(s, i, s[i-1]);
|
||||
json_consume_space(s, i);
|
||||
if(s[i] != ':')
|
||||
if(i >= s.length() || s[i] != ':')
|
||||
return(result); // malformed
|
||||
i += 1;
|
||||
DValue v = json_decode_value(s, i);
|
||||
|
||||
@@ -5,6 +5,10 @@ u8 char_to_u8(char input);
|
||||
u8 hex_to_u8(String src);
|
||||
u64 int_val(String s, u32 base = 10);
|
||||
f64 float_val(String s);
|
||||
u64 to_u64(String s, u64 fallback = 0);
|
||||
s64 to_s64(String s, s64 fallback = 0);
|
||||
f64 to_f64(String s, f64 fallback = 0);
|
||||
bool to_bool(String s, bool fallback = false);
|
||||
String to_lower(String s);
|
||||
String to_upper(String s);
|
||||
String substr(String s, s64 start_pos);
|
||||
@@ -95,13 +99,6 @@ StringList list_sort(StringList items);
|
||||
bool list_some(StringList items, std::function<bool (String)> f);
|
||||
bool list_every(StringList items, std::function<bool (String)> f);
|
||||
String list_find(StringList items, std::function<bool (String)> f, String fallback = "");
|
||||
StringList dv_keys(DValue tree);
|
||||
DValue dv_values(DValue tree);
|
||||
DValue dv_pick(DValue tree, StringList keys);
|
||||
DValue dv_omit(DValue tree, StringList keys);
|
||||
DValue dv_map(DValue tree, std::function<DValue (const DValue&, String)> f);
|
||||
DValue dv_filter(DValue tree, std::function<bool (const DValue&, String)> f);
|
||||
DValue dv_group_by(DValue tree, std::function<String (const DValue&, String)> f);
|
||||
|
||||
template <class ...Args>
|
||||
inline String first(Args... args)
|
||||
|
||||
+1
-61
@@ -193,13 +193,6 @@ StringList ls(String dir)
|
||||
return(StringList());
|
||||
return(split(listing, "\n"));
|
||||
}
|
||||
u64 config_map_u64(StringMap& cfg, String key, u64 fallback) { String raw = first(cfg[key], std::to_string(fallback)); char* end = 0; unsigned long long v = strtoull(raw.c_str(), &end, 10); return(end && *end == 0 ? (u64)v : fallback); }
|
||||
f64 config_map_f64(StringMap& cfg, String key, f64 fallback) { String raw = first(cfg[key], std::to_string(fallback)); char* end = 0; double v = strtod(raw.c_str(), &end); return(end && *end == 0 ? (f64)v : fallback); }
|
||||
bool config_bool_value(String raw, bool fallback) { if(raw == "") return(fallback); return(raw != "0" && raw != "false" && raw != "no" && raw != "off"); }
|
||||
bool config_map_bool(StringMap& cfg, String key, bool fallback) { return(config_bool_value(cfg[key], fallback)); }
|
||||
u64 config_u64(String key, u64 fallback) { return(context ? config_map_u64(context->server->config, key, fallback) : fallback); }
|
||||
f64 config_f64(String key, f64 fallback) { return(context ? config_map_f64(context->server->config, key, fallback) : fallback); }
|
||||
bool config_bool(String key, bool fallback) { return(context ? config_map_bool(context->server->config, key, fallback) : fallback); }
|
||||
DValue request_perf()
|
||||
{
|
||||
size_t required = uce_host_request_perf("", 0, 0, 0);
|
||||
@@ -1412,60 +1405,6 @@ StringList ls(String dir)
|
||||
return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n"));
|
||||
}
|
||||
|
||||
u64 config_map_u64(StringMap& cfg, String key, u64 fallback)
|
||||
{
|
||||
String raw = trim(cfg[key]);
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
return(int_val(raw));
|
||||
}
|
||||
|
||||
f64 config_map_f64(StringMap& cfg, String key, f64 fallback)
|
||||
{
|
||||
String raw = trim(cfg[key]);
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
return(float_val(raw));
|
||||
}
|
||||
|
||||
bool config_bool_value(String raw, bool fallback)
|
||||
{
|
||||
raw = trim(to_lower(raw));
|
||||
if(raw == "")
|
||||
return(fallback);
|
||||
if(raw == "1" || raw == "true" || raw == "yes" || raw == "on")
|
||||
return(true);
|
||||
if(raw == "0" || raw == "false" || raw == "no" || raw == "off")
|
||||
return(false);
|
||||
return(fallback);
|
||||
}
|
||||
|
||||
bool config_map_bool(StringMap& cfg, String key, bool fallback)
|
||||
{
|
||||
return(config_bool_value(cfg[key], fallback));
|
||||
}
|
||||
|
||||
u64 config_u64(String key, u64 fallback)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
return(fallback);
|
||||
return(config_map_u64(context->server->config, key, fallback));
|
||||
}
|
||||
|
||||
f64 config_f64(String key, f64 fallback)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
return(fallback);
|
||||
return(config_map_f64(context->server->config, key, fallback));
|
||||
}
|
||||
|
||||
bool config_bool(String key, bool fallback)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
return(fallback);
|
||||
return(config_map_bool(context->server->config, key, fallback));
|
||||
}
|
||||
|
||||
StringMap make_server_settings()
|
||||
{
|
||||
StringMap cfg;
|
||||
@@ -1485,6 +1424,7 @@ StringMap make_server_settings()
|
||||
// Command socket the WS broker listens on; workers flush ws_* dispatch
|
||||
// command batches here at workspace teardown.
|
||||
cfg["WS_BROKER_SOCKET_PATH"] = "/run/uce/ws-broker.sock";
|
||||
cfg["WS_BROKER_OUTBOUND_TIMEOUT_SECONDS"] = "30";
|
||||
cfg["TMP_UPLOAD_PATH"] = "/tmp/uce/uploads";
|
||||
cfg["SESSION_PATH"] = "/tmp/uce/sessions";
|
||||
cfg["COMPILER_SYS_PATH"] = ".";
|
||||
|
||||
@@ -56,14 +56,6 @@ time_t file_mtime(String file_name);
|
||||
void file_unlink(String file_name);
|
||||
String expand_path(String path, String relative_to_path = "");
|
||||
StringList ls(String dir);
|
||||
u64 config_map_u64(StringMap& cfg, String key, u64 fallback);
|
||||
f64 config_map_f64(StringMap& cfg, String key, f64 fallback);
|
||||
bool config_bool_value(String raw, bool fallback = true);
|
||||
bool config_map_bool(StringMap& cfg, String key, bool fallback = true);
|
||||
u64 config_u64(String key, u64 fallback);
|
||||
f64 config_f64(String key, f64 fallback);
|
||||
bool config_bool(String key, bool fallback = true);
|
||||
|
||||
f64 time_precise();
|
||||
u64 time();
|
||||
String time_format_local(String format = "", u64 timestamp = 0);
|
||||
|
||||
+54
-1
@@ -8,6 +8,7 @@
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
@@ -51,7 +52,58 @@ inline String operator+(String lhs, f32 rhs) {
|
||||
#define DEBUG_MEMORY_OFF
|
||||
|
||||
typedef std::map<String, String> StringMap;
|
||||
typedef std::vector<String> StringList;
|
||||
|
||||
struct StringList : std::vector<String> {
|
||||
using std::vector<String>::vector;
|
||||
using std::vector<String>::operator=;
|
||||
|
||||
StringList() = default;
|
||||
StringList(const StringList&) = default;
|
||||
StringList(StringList&&) = default;
|
||||
StringList& operator=(const StringList&) = default;
|
||||
StringList& operator=(StringList&&) = default;
|
||||
StringList(const std::vector<String>& source) : std::vector<String>(source) {}
|
||||
StringList(std::vector<String>&& source) : std::vector<String>(std::move(source)) {}
|
||||
StringList& operator=(const std::vector<String>& source) { std::vector<String>::operator=(source); return(*this); }
|
||||
StringList& operator=(std::vector<String>&& source) { std::vector<String>::operator=(std::move(source)); return(*this); }
|
||||
|
||||
template<typename F>
|
||||
StringList filter(F f) const
|
||||
{
|
||||
StringList result;
|
||||
for(const auto& item : *this)
|
||||
{
|
||||
if(f(item))
|
||||
result.push_back(item);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
StringList map(F f) const
|
||||
{
|
||||
StringList result;
|
||||
for(const auto& item : *this)
|
||||
result.push_back(f(item));
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringList keys() const
|
||||
{
|
||||
StringList result;
|
||||
for(size_t i = 0; i < size(); i++)
|
||||
result.push_back(std::to_string(i));
|
||||
return(result);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void each(F f) const
|
||||
{
|
||||
for(const auto& item : *this)
|
||||
f(item);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::ostringstream ByteStream;
|
||||
|
||||
struct Request;
|
||||
@@ -203,6 +255,7 @@ struct Request {
|
||||
String websocket_scope = "";
|
||||
DValue* websocket_connection_state = 0;
|
||||
StringList websocket_scope_connection_ids;
|
||||
DValue websocket_connection_state_before;
|
||||
DValue websocket_dispatch_commands;
|
||||
bool websocket_dispatch_capture = false;
|
||||
u8 websocket_opcode = 0;
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ String zip_error(String api, String detail)
|
||||
|
||||
void archive_check_size(String api, String label, u64 size, String config_key, u64 fallback)
|
||||
{
|
||||
u64 limit = config_u64(config_key, fallback);
|
||||
u64 limit = to_u64(context->server->config[config_key], fallback);
|
||||
if(limit > 0 && size > limit)
|
||||
throw std::runtime_error(zip_error(api, label + " exceeds configured limit"));
|
||||
}
|
||||
@@ -326,7 +326,7 @@ String gz_uncompress(String compressed)
|
||||
if(!out)
|
||||
throw std::runtime_error("gz_uncompress(): decompression failed");
|
||||
|
||||
u64 output_limit = config_u64("ARCHIVE_MAX_OUTPUT_BYTES", 64 * 1024 * 1024);
|
||||
u64 output_limit = to_u64(context->server->config["ARCHIVE_MAX_OUTPUT_BYTES"], 64 * 1024 * 1024);
|
||||
if(output_limit > 0 && out_len > output_limit)
|
||||
{
|
||||
mz_free(out);
|
||||
|
||||
Reference in New Issue
Block a user