aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/length-prefixed-protocol-helper.hh
diff options
context:
space:
mode:
authoreldritch horrors <pennae@lix.systems>2024-03-04 04:24:23 +0100
committereldritch horrors <pennae@lix.systems>2024-03-04 04:36:58 +0100
commit6897e238bd0c730af224b928ec8746781df67ad2 (patch)
tree50ce7ddeda203a12c7d67080ef611f56d59678c2 /src/libstore/length-prefixed-protocol-helper.hh
parentda0aa66d98b8b46253dd968cfaae61d872569c9b (diff)
Merge pull request #9099 from obsidiansystems/common-proto
Factor out bits of the worker protocol to use elsewhere (cherry picked from commit 4b1a97338f517f45e6169d3d8845c5caa5724e97) Change-Id: If93afa0f8b1cf9b0e705b34fa71e6fd708752758
Diffstat (limited to 'src/libstore/length-prefixed-protocol-helper.hh')
-rw-r--r--src/libstore/length-prefixed-protocol-helper.hh162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/libstore/length-prefixed-protocol-helper.hh b/src/libstore/length-prefixed-protocol-helper.hh
new file mode 100644
index 000000000..4061b0cd6
--- /dev/null
+++ b/src/libstore/length-prefixed-protocol-helper.hh
@@ -0,0 +1,162 @@
+#pragma once
+/**
+ * @file Reusable serialisers for serialization container types in a
+ * length-prefixed manner.
+ *
+ * Used by both the Worker and Serve protocols.
+ */
+
+#include "types.hh"
+
+namespace nix {
+
+class Store;
+
+/**
+ * Reusable serialisers for serialization container types in a
+ * length-prefixed manner.
+ *
+ * @param T The type of the collection being serialised
+ *
+ * @param Inner This the most important parameter; this is the "inner"
+ * protocol. The user of this will substitute `MyProtocol` or similar
+ * when making a `MyProtocol::Serialiser<Collection<T>>`. Note that the
+ * inside is allowed to call to call `Inner::Serialiser` on different
+ * types. This is especially important for `std::map` which doesn't have
+ * a single `T` but one `K` and one `V`.
+ */
+template<class Inner, typename T>
+struct LengthPrefixedProtoHelper;
+
+/*!
+ * \typedef LengthPrefixedProtoHelper::S
+ *
+ * Read this as simply `using S = Inner::Serialise;`.
+ *
+ * It would be nice to use that directly, but C++ doesn't seem to allow
+ * it. The `typename` keyword needed to refer to `Inner` seems to greedy
+ * (low precedence), and then C++ complains that `Serialise` is not a
+ * type parameter but a real type.
+ *
+ * Making this `S` alias seems to be the only way to avoid these issues.
+ */
+
+#define LENGTH_PREFIXED_PROTO_HELPER(Inner, T) \
+ struct LengthPrefixedProtoHelper< Inner, T > \
+ { \
+ static T read(const Store & store, typename Inner::ReadConn conn); \
+ static void write(const Store & store, typename Inner::WriteConn conn, const T & str); \
+ private: \
+ template<typename U> using S = typename Inner::template Serialise<U>; \
+ }
+
+template<class Inner, typename T>
+LENGTH_PREFIXED_PROTO_HELPER(Inner, std::vector<T>);
+
+template<class Inner, typename T>
+LENGTH_PREFIXED_PROTO_HELPER(Inner, std::set<T>);
+
+template<class Inner, typename... Ts>
+LENGTH_PREFIXED_PROTO_HELPER(Inner, std::tuple<Ts...>);
+
+template<class Inner, typename K, typename V>
+#define _X std::map<K, V>
+LENGTH_PREFIXED_PROTO_HELPER(Inner, _X);
+#undef _X
+
+template<class Inner, typename T>
+std::vector<T>
+LengthPrefixedProtoHelper<Inner, std::vector<T>>::read(
+ const Store & store, typename Inner::ReadConn conn)
+{
+ std::vector<T> resSet;
+ auto size = readNum<size_t>(conn.from);
+ while (size--) {
+ resSet.push_back(S<T>::read(store, conn));
+ }
+ return resSet;
+}
+
+template<class Inner, typename T>
+void
+LengthPrefixedProtoHelper<Inner, std::vector<T>>::write(
+ const Store & store, typename Inner::WriteConn conn, const std::vector<T> & resSet)
+{
+ conn.to << resSet.size();
+ for (auto & key : resSet) {
+ S<T>::write(store, conn, key);
+ }
+}
+
+template<class Inner, typename T>
+std::set<T>
+LengthPrefixedProtoHelper<Inner, std::set<T>>::read(
+ const Store & store, typename Inner::ReadConn conn)
+{
+ std::set<T> resSet;
+ auto size = readNum<size_t>(conn.from);
+ while (size--) {
+ resSet.insert(S<T>::read(store, conn));
+ }
+ return resSet;
+}
+
+template<class Inner, typename T>
+void
+LengthPrefixedProtoHelper<Inner, std::set<T>>::write(
+ const Store & store, typename Inner::WriteConn conn, const std::set<T> & resSet)
+{
+ conn.to << resSet.size();
+ for (auto & key : resSet) {
+ S<T>::write(store, conn, key);
+ }
+}
+
+template<class Inner, typename K, typename V>
+std::map<K, V>
+LengthPrefixedProtoHelper<Inner, std::map<K, V>>::read(
+ const Store & store, typename Inner::ReadConn conn)
+{
+ std::map<K, V> resMap;
+ auto size = readNum<size_t>(conn.from);
+ while (size--) {
+ auto k = S<K>::read(store, conn);
+ auto v = S<V>::read(store, conn);
+ resMap.insert_or_assign(std::move(k), std::move(v));
+ }
+ return resMap;
+}
+
+template<class Inner, typename K, typename V>
+void
+LengthPrefixedProtoHelper<Inner, std::map<K, V>>::write(
+ const Store & store, typename Inner::WriteConn conn, const std::map<K, V> & resMap)
+{
+ conn.to << resMap.size();
+ for (auto & i : resMap) {
+ S<K>::write(store, conn, i.first);
+ S<V>::write(store, conn, i.second);
+ }
+}
+
+template<class Inner, typename... Ts>
+std::tuple<Ts...>
+LengthPrefixedProtoHelper<Inner, std::tuple<Ts...>>::read(
+ const Store & store, typename Inner::ReadConn conn)
+{
+ return std::tuple<Ts...> {
+ S<Ts>::read(store, conn)...,
+ };
+}
+
+template<class Inner, typename... Ts>
+void
+LengthPrefixedProtoHelper<Inner, std::tuple<Ts...>>::write(
+ const Store & store, typename Inner::WriteConn conn, const std::tuple<Ts...> & res)
+{
+ std::apply([&]<typename... Us>(const Us &... args) {
+ (S<Us>::write(store, conn, args), ...);
+ }, res);
+}
+
+}