Initial import of libdpf.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
e4e666f459
4563 changed files with 1690372 additions and 0 deletions
289
include/dpf/path_memoizer.hpp
Normal file
289
include/dpf/path_memoizer.hpp
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/// @file dpf/path_memoizer.hpp
|
||||
/// @brief
|
||||
/// @details
|
||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
|
||||
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
|
||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||
/// see [LICENSE.md](@ref license) for details.
|
||||
|
||||
#ifndef LIBDPF_INCLUDE_DPF_PATH_MEMOIZER_HPP__
|
||||
#define LIBDPF_INCLUDE_DPF_PATH_MEMOIZER_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "dpf/utils.hpp"
|
||||
#include "dpf/secret_share.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
|
||||
/// Path memoizers key on the underlying DPF key type. `party_key` wrappers
|
||||
/// share the same tree layout, so a memoizer built for party 0 also accepts
|
||||
/// party 1 (and bare keys).
|
||||
template <typename T>
|
||||
using path_memoizer_key_t = unwrap_party_key_t<T>;
|
||||
|
||||
template <typename DpfKey,
|
||||
typename ReturnT = const typename DpfKey::interior_node *>
|
||||
struct path_memoizer_base
|
||||
{
|
||||
public:
|
||||
using dpf_type = DpfKey;
|
||||
using input_type = typename DpfKey::input_type;
|
||||
using node_type = typename DpfKey::interior_node;
|
||||
using return_type = ReturnT;
|
||||
using iterator_type = return_type;
|
||||
|
||||
virtual std::size_t assign_x(const dpf_type &, input_type) noexcept = 0;
|
||||
virtual node_type & operator[](std::size_t) noexcept = 0;
|
||||
|
||||
virtual return_type begin() const noexcept = 0;
|
||||
virtual return_type end() const noexcept = 0;
|
||||
};
|
||||
|
||||
template <typename DpfKey>
|
||||
struct alignas(alignof(typename path_memoizer_key_t<DpfKey>::interior_node))
|
||||
basic_path_memoizer final
|
||||
: public path_memoizer_base<path_memoizer_key_t<DpfKey>>
|
||||
{
|
||||
public:
|
||||
using dpf_type = path_memoizer_key_t<DpfKey>;
|
||||
using input_type = typename dpf_type::input_type;
|
||||
using node_type = typename dpf_type::interior_node;
|
||||
HEDLEY_PRAGMA(GCC diagnostic push)
|
||||
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||
using return_type = std::add_pointer_t<std::add_const_t<node_type>>;
|
||||
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||
using iterator_type = return_type;
|
||||
static constexpr auto depth = dpf_type::depth;
|
||||
|
||||
basic_path_memoizer()
|
||||
: dpf_{std::nullopt}, x_{std::nullopt}, filled_to_{0} { }
|
||||
basic_path_memoizer(basic_path_memoizer &&) noexcept = default;
|
||||
basic_path_memoizer(const basic_path_memoizer &) = default;
|
||||
basic_path_memoizer & operator=(basic_path_memoizer &&) noexcept = default;
|
||||
basic_path_memoizer & operator=(const basic_path_memoizer &) = default;
|
||||
~basic_path_memoizer() = default;
|
||||
|
||||
std::size_t assign_x(const dpf_type & dpf, input_type new_x) noexcept override
|
||||
{
|
||||
static constexpr auto clz_xor = utils::countl_zero_symmetric_difference<input_type>{};
|
||||
if (dpf_.has_value() == true && std::memcmp(&dpf_root_, &dpf.root(), sizeof(node_type)) == 0
|
||||
&& std::memcmp(&dpf_common_part_hash_, &dpf.common_part_hash(), sizeof(digest_type)) == 0)
|
||||
{
|
||||
static constexpr auto complement_of = std::bit_not{};
|
||||
input_type old_x = x_.value_or(complement_of(new_x));
|
||||
x_ = new_x;
|
||||
const std::size_t resume = clz_xor(old_x, new_x) + 1;
|
||||
// Path slots below `resume` remain valid; invalidate the rest.
|
||||
if (filled_to_ >= resume)
|
||||
filled_to_ = resume - 1;
|
||||
return resume;
|
||||
}
|
||||
|
||||
this->operator[](0) = dpf.root();
|
||||
dpf_ = std::cref(dpf);
|
||||
dpf_root_ = dpf.root();
|
||||
dpf_common_part_hash_ = dpf.common_part_hash();
|
||||
x_ = new_x;
|
||||
filled_to_ = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
node_type & operator[](std::size_t i) noexcept override
|
||||
{
|
||||
return arr_[i];
|
||||
}
|
||||
|
||||
return_type begin() const noexcept override
|
||||
{
|
||||
if (x_.has_value() == true)
|
||||
{
|
||||
return std::addressof(arr_[depth]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return end();
|
||||
}
|
||||
}
|
||||
|
||||
return_type end() const noexcept override
|
||||
{
|
||||
return std::addressof(arr_[depth+1]);
|
||||
}
|
||||
|
||||
/// Inclusive high-water: `arr_[0..filled_to_]` are valid for the current x.
|
||||
std::size_t filled_to() const noexcept { return filled_to_; }
|
||||
|
||||
void note_filled(std::size_t level) noexcept
|
||||
{
|
||||
if (level > filled_to_)
|
||||
filled_to_ = level;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<std::reference_wrapper<const dpf_type>> dpf_;
|
||||
node_type dpf_root_;
|
||||
digest_type dpf_common_part_hash_;
|
||||
std::optional<input_type> x_;
|
||||
std::size_t filled_to_;
|
||||
HEDLEY_PRAGMA(GCC diagnostic push)
|
||||
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||
std::array<node_type, depth+1> arr_;
|
||||
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||
};
|
||||
|
||||
template <typename DpfKey>
|
||||
struct nonmemoizing_path_memoizer final
|
||||
: public path_memoizer_base<path_memoizer_key_t<DpfKey>>
|
||||
{
|
||||
public:
|
||||
using dpf_type = path_memoizer_key_t<DpfKey>;
|
||||
using input_type = typename dpf_type::input_type;
|
||||
using node_type = typename dpf_type::interior_node;
|
||||
HEDLEY_PRAGMA(GCC diagnostic push)
|
||||
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
||||
using return_type = std::add_pointer_t<std::add_const_t<node_type>>;
|
||||
HEDLEY_PRAGMA(GCC diagnostic pop)
|
||||
using iterator_type = return_type;
|
||||
|
||||
nonmemoizing_path_memoizer()
|
||||
: dpf_{std::nullopt} { }
|
||||
nonmemoizing_path_memoizer(nonmemoizing_path_memoizer &&) noexcept = default;
|
||||
nonmemoizing_path_memoizer(const nonmemoizing_path_memoizer &) = default;
|
||||
nonmemoizing_path_memoizer & operator=(nonmemoizing_path_memoizer &&) noexcept = default;
|
||||
nonmemoizing_path_memoizer & operator=(const nonmemoizing_path_memoizer &) = default;
|
||||
~nonmemoizing_path_memoizer() = default;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
std::size_t assign_x(const dpf_type & dpf, input_type) noexcept override
|
||||
{
|
||||
dpf_ = dpf;
|
||||
v = dpf.root();
|
||||
return 1;
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
node_type & operator[](std::size_t) noexcept override
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
return_type begin() const noexcept override
|
||||
{
|
||||
return std::addressof(v);
|
||||
}
|
||||
|
||||
return_type end() const noexcept override
|
||||
{
|
||||
return std::addressof(v) + 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<std::reference_wrapper<const dpf_type>> dpf_;
|
||||
node_type v;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename MemoizerT>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
auto make_path_memoizer()
|
||||
{
|
||||
return MemoizerT();
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_path_high_water : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_path_high_water<T,
|
||||
std::void_t<decltype(std::declval<T &>().filled_to()),
|
||||
decltype(std::declval<T &>().note_filled(std::size_t{}))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename DpfKey, typename PathMemoizer>
|
||||
std::size_t path_resume_for_level(PathMemoizer & path, const DpfKey & dpf,
|
||||
typename DpfKey::input_type x, std::size_t to_level)
|
||||
{
|
||||
auto resume = path.assign_x(dpf, x);
|
||||
if constexpr (has_path_high_water<PathMemoizer>::value)
|
||||
{
|
||||
resume = std::min(resume, path.filled_to() + 1);
|
||||
if (resume == 0)
|
||||
resume = 1;
|
||||
}
|
||||
else if (resume > to_level)
|
||||
{
|
||||
resume = 1;
|
||||
}
|
||||
return resume;
|
||||
}
|
||||
|
||||
template <typename PathMemoizer>
|
||||
void path_note_filled_to(PathMemoizer & path, std::size_t level)
|
||||
{
|
||||
if constexpr (has_path_high_water<PathMemoizer>::value)
|
||||
path.note_filled(level);
|
||||
}
|
||||
|
||||
/// Walk interior nodes so `path[0..to_level]` is valid for `x`.
|
||||
template <typename DpfKey, typename PathMemoizer>
|
||||
void ensure_level(const DpfKey & dpf, typename DpfKey::input_type x,
|
||||
PathMemoizer & path, std::size_t to_level)
|
||||
{
|
||||
auto level_index = path_resume_for_level(path, dpf, x, to_level);
|
||||
DPF_UNROLL_LOOP
|
||||
for (auto mask = dpf.msb_mask >> (level_index - 1);
|
||||
level_index <= to_level; ++level_index, mask >>= 1)
|
||||
{
|
||||
bool bit = !!(mask & x);
|
||||
auto cw = dpf.correction_word(level_index - 1, bit);
|
||||
path[level_index] =
|
||||
DpfKey::traverse_interior(path[level_index - 1], cw, bit);
|
||||
}
|
||||
path_note_filled_to(path, to_level);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename DpfKey>
|
||||
auto make_basic_path_memoizer()
|
||||
{
|
||||
return detail::make_path_memoizer<basic_path_memoizer<DpfKey>>();
|
||||
}
|
||||
|
||||
template <typename DpfKey>
|
||||
auto make_basic_path_memoizer(const DpfKey &)
|
||||
{
|
||||
return make_basic_path_memoizer<DpfKey>();
|
||||
}
|
||||
|
||||
template <typename DpfKey>
|
||||
auto make_nonmemoizing_path_memoizer()
|
||||
{
|
||||
return detail::make_path_memoizer<nonmemoizing_path_memoizer<DpfKey>>();
|
||||
}
|
||||
|
||||
template <typename DpfKey>
|
||||
auto make_nonmemoizing_path_memoizer(const DpfKey &)
|
||||
{
|
||||
return make_nonmemoizing_path_memoizer<DpfKey>();
|
||||
}
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_PATH_MEMOIZER_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue