mirror of https://github.com/oxen-io/oxen-mq.git
Improve on-the-fly bt deserialization interface
This separates the dict traversal into `next_...` functions that return a key-value pair, and `consume_...` that return just the value. The latter is particularly useful when using `skip_until` to position yourself at a known key.
This commit is contained in:
parent
3ff66490ad
commit
7393e8422c
|
@ -216,5 +216,14 @@ bool bt_dict_consumer::consume_key() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<string_view, string_view> bt_dict_consumer::next_string() {
|
||||||
|
if (!is_string())
|
||||||
|
throw bt_deserialize_invalid_type{"expected a string, but found "s + data.front()};
|
||||||
|
std::pair<string_view, string_view> ret;
|
||||||
|
ret.second = bt_list_consumer::consume_string();
|
||||||
|
ret.first = flush_key();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace lokimq
|
} // namespace lokimq
|
||||||
|
|
|
@ -693,12 +693,12 @@ public:
|
||||||
|
|
||||||
/// Attempt to parse the next value as a string->string pair (and advance just past it). Throws
|
/// Attempt to parse the next value as a string->string pair (and advance just past it). Throws
|
||||||
/// if the next value is not a string.
|
/// if the next value is not a string.
|
||||||
std::pair<string_view, string_view> consume_string();
|
std::pair<string_view, string_view> next_string();
|
||||||
|
|
||||||
/// Attempts to parse the next value as an string->integer pair (and advance just past it).
|
/// Attempts to parse the next value as an string->integer pair (and advance just past it).
|
||||||
/// Throws if the next value is not an integer.
|
/// Throws if the next value is not an integer.
|
||||||
template <typename IntType>
|
template <typename IntType>
|
||||||
std::pair<string_view, IntType> consume_integer() {
|
std::pair<string_view, IntType> next_integer() {
|
||||||
if (!is_integer()) throw bt_deserialize_invalid_type{"next bt dict value is not an integer"};
|
if (!is_integer()) throw bt_deserialize_invalid_type{"next bt dict value is not an integer"};
|
||||||
std::pair<string_view, IntType> ret;
|
std::pair<string_view, IntType> ret;
|
||||||
ret.second = bt_list_consumer::consume_integer<IntType>();
|
ret.second = bt_list_consumer::consume_integer<IntType>();
|
||||||
|
@ -711,7 +711,7 @@ public:
|
||||||
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
|
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
|
||||||
/// used).
|
/// used).
|
||||||
template <typename T = bt_list>
|
template <typename T = bt_list>
|
||||||
std::pair<string_view, T> consume_list() {
|
std::pair<string_view, T> next_list() {
|
||||||
std::pair<string_view, T> pair;
|
std::pair<string_view, T> pair;
|
||||||
pair.first = consume_list(pair.second);
|
pair.first = consume_list(pair.second);
|
||||||
return pair;
|
return pair;
|
||||||
|
@ -719,7 +719,7 @@ public:
|
||||||
|
|
||||||
/// Same as above, but takes a pre-existing list-like data type. Returns the key.
|
/// Same as above, but takes a pre-existing list-like data type. Returns the key.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
string_view consume_list(T& list) {
|
string_view next_list(T& list) {
|
||||||
if (!is_list()) throw bt_deserialize_invalid_type{"next bt value is not a list"};
|
if (!is_list()) throw bt_deserialize_invalid_type{"next bt value is not a list"};
|
||||||
bt_list_consumer::consume_list(list);
|
bt_list_consumer::consume_list(list);
|
||||||
return flush_key();
|
return flush_key();
|
||||||
|
@ -730,7 +730,7 @@ public:
|
||||||
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
|
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
|
||||||
/// used).
|
/// used).
|
||||||
template <typename T = bt_dict>
|
template <typename T = bt_dict>
|
||||||
std::pair<string_view, T> consume_dict() {
|
std::pair<string_view, T> next_dict() {
|
||||||
std::pair<string_view, T> pair;
|
std::pair<string_view, T> pair;
|
||||||
pair.first = consume_dict(pair.second);
|
pair.first = consume_dict(pair.second);
|
||||||
return pair;
|
return pair;
|
||||||
|
@ -738,7 +738,7 @@ public:
|
||||||
|
|
||||||
/// Same as above, but takes a pre-existing dict-like data type. Returns the key.
|
/// Same as above, but takes a pre-existing dict-like data type. Returns the key.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
string_view consume_dict(T& dict) {
|
string_view next_dict(T& dict) {
|
||||||
if (!is_dict()) throw bt_deserialize_invalid_type{"next bt value is not a dict"};
|
if (!is_dict()) throw bt_deserialize_invalid_type{"next bt value is not a dict"};
|
||||||
bt_list_consumer::consume_dict(dict);
|
bt_list_consumer::consume_dict(dict);
|
||||||
return flush_key();
|
return flush_key();
|
||||||
|
@ -748,20 +748,26 @@ public:
|
||||||
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
|
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
|
||||||
/// quite inefficient for large, nested structures (unless the values only need to be skipped
|
/// quite inefficient for large, nested structures (unless the values only need to be skipped
|
||||||
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
|
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
|
||||||
std::pair<string_view, string_view> consume_list_data() {
|
std::pair<string_view, string_view> next_list_data() {
|
||||||
if (data.size() < 2 || !is_list()) throw bt_deserialize_invalid_type{"next bt dict value is not a list"};
|
if (data.size() < 2 || !is_list()) throw bt_deserialize_invalid_type{"next bt dict value is not a list"};
|
||||||
return {flush_key(), bt_list_consumer::consume_list_data()};
|
return {flush_key(), bt_list_consumer::consume_list_data()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as next_list_data(), but wraps the value in a bt_list_consumer for convenience
|
||||||
|
std::pair<string_view, bt_list_consumer> next_list_consumer() { return next_list_data(); }
|
||||||
|
|
||||||
/// Attempts to parse the next value as a string->dict pair and returns the string_view that
|
/// Attempts to parse the next value as a string->dict pair and returns the string_view that
|
||||||
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
|
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
|
||||||
/// quite inefficient for large, nested structures (unless the values only need to be skipped
|
/// quite inefficient for large, nested structures (unless the values only need to be skipped
|
||||||
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
|
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
|
||||||
std::pair<string_view, string_view> consume_dict_data() {
|
std::pair<string_view, string_view> next_dict_data() {
|
||||||
if (data.size() < 2 || !is_dict()) throw bt_deserialize_invalid_type{"next bt dict value is not a dict"};
|
if (data.size() < 2 || !is_dict()) throw bt_deserialize_invalid_type{"next bt dict value is not a dict"};
|
||||||
return {flush_key(), bt_list_consumer::consume_dict_data()};
|
return {flush_key(), bt_list_consumer::consume_dict_data()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as next_dict_data(), but wraps the value in a bt_dict_consumer for convenience
|
||||||
|
std::pair<string_view, bt_dict_consumer> next_dict_consumer() { return next_dict_data(); }
|
||||||
|
|
||||||
/// Skips ahead until we find the first key >= the given key or reach the end of the dict.
|
/// Skips ahead until we find the first key >= the given key or reach the end of the dict.
|
||||||
/// Returns true if we found an exact match, false if we reached some greater value or the end.
|
/// Returns true if we found an exact match, false if we reached some greater value or the end.
|
||||||
/// If we didn't hit the end, the next `consumer_*()` call will return the key-value pair we
|
/// If we didn't hit the end, the next `consumer_*()` call will return the key-value pair we
|
||||||
|
@ -782,6 +788,38 @@ public:
|
||||||
}
|
}
|
||||||
return key_ == find;
|
return key_ == find;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `consume_*` functions are wrappers around next_whatever that discard the returned key.
|
||||||
|
///
|
||||||
|
/// Intended for use with skip_until such as:
|
||||||
|
///
|
||||||
|
/// std::string value;
|
||||||
|
/// if (d.skip_until("key"))
|
||||||
|
/// value = d.consume_string();
|
||||||
|
///
|
||||||
|
|
||||||
|
auto consume_string() { return next_string().second; }
|
||||||
|
|
||||||
|
template <typename IntType>
|
||||||
|
auto consume_integer() { return next_integer<IntType>().second; }
|
||||||
|
|
||||||
|
template <typename T = bt_list>
|
||||||
|
auto consume_list() { return next_list<T>().second; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void consume_list(T& list) { next_list(list); }
|
||||||
|
|
||||||
|
template <typename T = bt_dict>
|
||||||
|
auto consume_dict() { return next_dict<T>().second; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void consume_dict(T& dict) { next_dict(dict); }
|
||||||
|
|
||||||
|
string_view consume_list_data() { return next_list_data().second; }
|
||||||
|
string_view consume_dict_data() { return next_dict_data().second; }
|
||||||
|
|
||||||
|
bt_list_consumer consume_list_consumer() { return consume_list_data(); }
|
||||||
|
bt_dict_consumer consume_dict_consumer() { return consume_dict_data(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue