libdpf/include/dpf/dpf_key.hpp

1317 lines
50 KiB
C++
Raw Normal View History

/// @file dpf/dpf_key.hpp
/// @brief
/// @details
/// @author Ryan Henry <ryan.henry@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_DPF_KEY_HPP__
#define LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__
#include "hedley/hedley.h"
#include <cstddef>
#include <utility>
#include <tuple>
#include <array>
#include <bitset>
#include <atomic>
#include "dpf/prg_aes.hpp"
#include "dpf/wildcard.hpp"
#include "dpf/twiddle.hpp"
#include "dpf/leaf_node.hpp"
#include "dpf/offset_wrapper.hpp"
#include "dpf/leaf_wrapper.hpp"
#include "dpf/emplace.hpp"
#include "dpf/placement.hpp"
#include "dpf/dcf.hpp"
namespace dpf
{
#ifdef LIBDPF_HAS_ASIO
namespace asio
{
template <std::size_t I,
typename PeerT,
typename DpfKey,
typename OutputType,
typename CompletionToken>
auto async_assign_wildcard_output(PeerT & peer, DpfKey & dpf,
OutputType && output_share, CompletionToken && token);
}
#endif
template <typename InputT,
typename OutputT = dpf::bit,
typename ...OutputTs>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpfargs(InputT && x, OutputT && y = dpf::bit::one, OutputTs && ...ys);
template <typename InputT,
typename OutputT,
typename ...OutputTs>
struct dpfargs final
{
using input_type = InputT;
using output_type = std::tuple<OutputT, OutputTs...>;
dpfargs() = delete;
dpfargs(dpfargs &&) = default;
dpfargs(const dpfargs &) = default;
input_type x;
output_type y;
private:
dpfargs(input_type x_, output_type y_) { x = x_; y = y_; }
template <typename I, typename O, typename ...Os>
friend auto make_dpfargs(I &&, O &&, Os && ...);
// friend auto make_dpfargs(InputT && x, OutputT && y, OutputTs && ...ys)
};
template <typename InputT,
typename OutputT,
typename ...OutputTs>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_WARN_UNUSED_RESULT
auto make_dpfargs(InputT && x, OutputT && y, OutputTs && ...ys)
{
return dpfargs<std::decay_t<InputT>,
std::decay_t<OutputT>,
std::decay_t<OutputTs>...>
{ std::forward<InputT>(x),
std::make_tuple(std::forward<OutputT>(y),
std::forward<OutputTs>(ys)...) };
}
template <typename InteriorPRG>
using root_sampler_t = std::add_pointer_t<typename InteriorPRG::block_type()>;
namespace detail
{
/// Classic single-level DPF key body (all outputs bare, at full input width,
/// equal widths, no comparison channel). `Derived` is the public `dpf_key`
/// specialization that inherits this body — threaded through only so that
/// `emplace`/`emplace_back` construct the public key type.
template <typename Derived,
typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
struct classic_dpf_key_impl
{
public:
using interior_prg = InteriorPRG;
using interior_node = typename InteriorPRG::block_type;
using exterior_prg = ExteriorPRG;
using exterior_node = typename ExteriorPRG::block_type;
using input_type = dpf::concrete_type_t<InputT>;
using raw_input_type = InputT;
using integral_type = utils::integral_type_from_bitlength_t<utils::bitlength_of_v<input_type>, utils::bitlength_of_v<std::size_t>>;
using outputs_tuple = std::tuple<OutputT, OutputTs...>;
template <std::size_t I>
using output_type_t = std::tuple_element_t<I, outputs_tuple>;
using concrete_outputs_tuple
= std::tuple<concrete_type_t<OutputT>, concrete_type_t<OutputTs>...>;
template <std::size_t I>
using concrete_output_type = std::tuple_element_t<I, concrete_outputs_tuple>;
using offset_type = offset_wrapper<InputT>; // N.B.: `InputT`, not `input_type`
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
using leaf_tuple = dpf::leaf_tuple_t<exterior_node, OutputT, OutputTs...>;
using beaver_tuple = dpf::beaver_tuple_t<exterior_node, OutputT, OutputTs...>;
using leaf_wrapper_tuple = std::tuple<dpf::leaf_wrapper<OutputT, exterior_node>, dpf::leaf_wrapper<OutputTs, exterior_node>...>;
HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr std::size_t outputs_per_leaf = dpf::outputs_per_leaf_v<OutputT, exterior_node>;
static constexpr std::size_t lg_outputs_per_leaf = dpf::lg_outputs_per_leaf_v<OutputT, exterior_node>;
static constexpr std::size_t depth
= utils::bitlength_of_v<input_type> - lg_outputs_per_leaf;
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
// -----------------------------------------------------------------------
// Slot-meta foundation (unified with the multi-level `incr_key_base`).
// Every key carries a `slot_meta` table; for a classic (single-level,
// equal-width, no-cmp) pack all slots sit at `prefix == bitlen` and the
// packing positions match the classic `leaf_prg` layout. These are all
// `static constexpr`, so the object layout is unchanged.
// -----------------------------------------------------------------------
static constexpr std::size_t num_outputs = 1 + sizeof...(OutputTs);
static constexpr std::size_t cmp_depth = 0;
static constexpr std::size_t cmp_out_bits = 0;
static constexpr std::size_t cmp_block = 0;
static constexpr bool cmp_idcf = false;
static constexpr std::size_t cmp_q = 0;
static constexpr std::size_t cmp_h = 0;
static constexpr std::size_t cmp_checkpoints = 0;
static constexpr std::size_t cmp_tail = 0;
/// Classic keys are single-level; the unified eval surface keeps routing
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
static constexpr bool is_multilevel = false;
private:
using meta_placed_tuple = std::tuple<
dpf::detail::incr::placed<utils::bitlength_of_v<input_type>, OutputT>,
dpf::detail::incr::placed<utils::bitlength_of_v<input_type>,
OutputTs>...>;
public:
using meta_array = std::array<dpf::detail::incr::slot_meta, num_outputs>;
static constexpr meta_array meta =
dpf::detail::incr::build_meta<exterior_node, meta_placed_tuple>();
static constexpr std::size_t deepest_output = 0;
template <std::size_t I>
static constexpr std::size_t lg_outputs_per_leaf_of = meta[I].lg_opl;
template <std::size_t I>
static constexpr std::size_t outputs_per_leaf_of =
std::size_t{1} << lg_outputs_per_leaf_of<I>;
using correction_words_array = std::array<interior_node, depth>;
using correction_advice_array = std::array<psnip_uint8_t, depth>;
template <typename Emplaceable>
HEDLEY_ALWAYS_INLINE
static void emplace(Emplaceable & output,
const interior_node & root,
const correction_words_array & correction_words,
const correction_advice_array & correction_advice,
const leaf_tuple & leaves,
const beaver_tuple & beavers,
const input_type & offset_share)
{
utils::dpf_emplacer<Derived, Emplaceable>::emplace(output, root, correction_words, correction_advice, leaves, beavers, offset_share);
}
template <typename EmplaceableContainer>
HEDLEY_ALWAYS_INLINE
static void emplace_back(EmplaceableContainer & output,
const interior_node & root,
const correction_words_array & correction_words,
const correction_advice_array & correction_advice,
const leaf_tuple & leaves,
const beaver_tuple & beavers,
const input_type & offset_share)
{
utils::dpf_back_emplacer<Derived, EmplaceableContainer>::emplace_back(output, root, correction_words, correction_advice, leaves, beavers, offset_share);
}
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
static_assert(((dpf::utils::bitlength_of_output_v<OutputT, exterior_node>
== dpf::utils::bitlength_of_output_v<OutputTs, exterior_node>) && ...),
"all output types must be the same length");
HEDLEY_PRAGMA(GCC diagnostic pop)
static_assert(std::conjunction_v<std::is_trivially_copyable<OutputT>,
std::is_trivially_copyable<OutputTs>...>,
"all output types must be trivially copyable");
static_assert(std::conjunction_v<std::is_standard_layout<OutputT>,
std::is_standard_layout<OutputTs>...>,
"all output types must be standard layout");
// static_assert(std::has_unique_object_representations_v<input_type>);
HEDLEY_ALWAYS_INLINE
constexpr classic_dpf_key_impl(interior_node root,
const correction_words_array & correction_words,
const correction_advice_array & correction_advice,
const leaf_tuple & leaves,
const beaver_tuple & beavers,
input_type offset_share)
: root_{root},
correction_words_{correction_words},
correction_advice_{correction_advice},
mutable_wildcard_mask_{dpf::utils::make_bitset(dpf::is_wildcard_v<OutputT>,
dpf::is_wildcard_v<OutputTs>...)},
leaf_nodes(get_wrappers(leaves, beavers)),
common_part_hash_{utils::get_common_part_hash(correction_words_, correction_advice_, leaf_nodes, wildcard_mask)},
offset_x{offset_share}
{ }
classic_dpf_key_impl(const classic_dpf_key_impl &) = default;
classic_dpf_key_impl(classic_dpf_key_impl &&) = default;
classic_dpf_key_impl & operator=(const classic_dpf_key_impl &) = default;
classic_dpf_key_impl & operator=(classic_dpf_key_impl &&) = default;
const interior_node & root() const { return root_; }
const correction_words_array & correction_words() const { return correction_words_; }
const correction_advice_array & correction_advice() const { return correction_advice_; }
const digest_type & common_part_hash() const { return common_part_hash_; }
std::string wildcard_bitmask() const
{
return mutable_wildcard_mask_.to_string();
}
HEDLEY_ALWAYS_INLINE
const interior_node & correction_word(std::size_t level) const
{
return correction_words_[level];
}
HEDLEY_ALWAYS_INLINE
psnip_uint8_t correction_advice(std::size_t level) const
{
return correction_advice_[level];
}
HEDLEY_ALWAYS_INLINE
auto correction_word(std::size_t level, bool direction) const
{
return set_lo_bit(correction_word(level),
(correction_advice_[level] >> direction) & 1);
}
template <std::size_t I = 0>
HEDLEY_ALWAYS_INLINE
const auto & leaf() const
{
if constexpr (dpf::is_wildcard_v<output_type_t<I>>)
{
return std::get<I>(leaf_nodes).raw_leaf();
}
else
{
return std::get<I>(leaf_nodes).get();
}
}
template <std::size_t I = 0>
HEDLEY_ALWAYS_INLINE
const auto & beaver() const
{
return std::get<I>(leaf_nodes).beaver();
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr bool is_wildcard(std::size_t i) const noexcept
{
return wildcard_mask[i];
}
#ifdef LIBDPF_HAS_ASIO
template <std::size_t I = 0,
typename PeerT,
typename OutputType,
typename CompletionToken>
auto async_assign_leaf(PeerT & peer, OutputType && output_share,
CompletionToken && token)
{
return dpf::asio::async_assign_wildcard_output<I>(
peer, *this, std::forward<OutputType>(output_share),
std::forward<CompletionToken>(token));
}
#endif
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior(const interior_node & node,
const interior_node & cw, bool dir) noexcept
{
return dpf::xor_if_lo_bit(
interior_prg::eval(unset_lo_2bits(node), dir), cw, node);
}
/// Expand both children of `node` with one pipelined `eval01`.
/// Equivalent to `traverse_interior(node, cw0, 0)` and
/// `traverse_interior(node, cw1, 1)`, but the two AES-128 blocks share
/// a round loop. Full-domain interval eval uses this at almost every
/// interior parent.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior01(const interior_node & node,
const interior_node & cw0, const interior_node & cw1) noexcept
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto kids = interior_prg::eval01(unset_lo_2bits(node));
return std::array<interior_node, 2>{
dpf::xor_if_lo_bit(kids[0], cw0, node),
dpf::xor_if_lo_bit(kids[1], cw1, node)
};
HEDLEY_PRAGMA(GCC diagnostic pop)
}
/// Four independent `traverse_interior01` via `InteriorPRG::eval01_x4`.
/// `left[i]` / `right[i]` are the children of `parents[i]`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void traverse_interior01_x4(const interior_node * HEDLEY_RESTRICT parents,
const interior_node & cw0, const interior_node & cw1,
interior_node * HEDLEY_RESTRICT left,
interior_node * HEDLEY_RESTRICT right) noexcept
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
alignas(interior_node) interior_node seeds[4];
DPF_UNROLL_LOOP
for (std::size_t i = 0; i < 4; ++i)
{
seeds[i] = unset_lo_2bits(parents[i]);
}
interior_prg::eval01_x4(seeds, left, right);
DPF_UNROLL_LOOP
for (std::size_t i = 0; i < 4; ++i)
{
left[i] = dpf::xor_if_lo_bit(left[i], cw0, parents[i]);
right[i] = dpf::xor_if_lo_bit(right[i], cw1, parents[i]);
}
HEDLEY_PRAGMA(GCC diagnostic pop)
}
template <std::size_t I = 0,
typename LeafT>
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_exterior(const interior_node & node,
const LeafT & correction_word) noexcept
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
using output_type = std::tuple_element_t<I, concrete_outputs_tuple>;
// Subtractive share: CW_if_t − mask so reconstruct(y0, y1) = y0 − y1 = β.
return dpf::subtract_leaf<output_type>(
dpf::get_if_lo_bit(correction_word, node),
make_leaf_mask_inner<exterior_prg, I, concrete_outputs_tuple>(unset_lo_2bits(node)));
HEDLEY_PRAGMA(GCC diagnostic pop)
}
template <std::size_t I = 0>
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
auto traverse_exterior(const interior_node & node) const noexcept
{
return traverse_exterior<I>(node, std::get<I>(leaf_nodes).get());
}
leaf_wrapper_tuple leaf_nodes;
offset_type offset_x;
static constexpr std::array<bool, sizeof...(OutputTs)+1> wildcard_mask{dpf::is_wildcard_v<OutputT>,
dpf::is_wildcard_v<OutputTs>...};
private:
static auto get_wrappers(const leaf_tuple & leaves,
const beaver_tuple & beavers)
{
outputs_tuple tmp{};
return std::apply([&beavers, &tmp](auto & ...leaf)
{
return std::apply([&leaf..., &tmp](auto & ...beaver)
{
return std::apply([&leaf..., &beaver...](auto & ...foo)
{
return std::make_tuple(
dpf::leaf_wrapper<std::decay_t<decltype(foo)>, exterior_node>(leaf, beaver)...
);
}, tmp);
}, beavers);
}, leaves);
}
interior_node root_;
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
correction_words_array correction_words_;
HEDLEY_PRAGMA(GCC diagnostic pop)
correction_advice_array correction_advice_;
std::bitset<sizeof...(OutputTs)+1> mutable_wildcard_mask_;
digest_type common_part_hash_;
}; // struct classic_dpf_key_impl
} // namespace detail
namespace detail
{
namespace incr
{
/// Comparison-channel storage. Value CWs, `cw_last`, and the `cmp_addend`
/// share are held at the comparison group width (`ValueCwWord`), not a full
/// padded `uint64_t` per level: a bit comparison carries 1 byte/level, a
/// `uint16_t` payload 2 bytes/level, etc. Arithmetic still runs in `uint64_t`
/// (masked); the narrow word is only the on-key / on-wire representation.
/// Extra per-level δ-coefficients kept only for wildcard comparison payloads
/// (empty for concrete cmp keys, so their layout is unchanged). The value CWs
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
/// place with no tree re-walk / re-PRG.
template <std::size_t Depth, typename ValueCwWord, bool Wild,
std::size_t TailLen = 0, bool Idcf = false>
struct cmp_wild_state { };
template <std::size_t Depth, typename ValueCwWord, std::size_t TailLen, bool Idcf>
struct cmp_wild_state<Depth, ValueCwWord, true, TailLen, Idcf>
{
std::array<ValueCwWord, Depth> value_cw_coeff{};
ValueCwWord cw_last_coeff{0};
std::array<ValueCwWord, TailLen> tail_coeff{};
std::array<ValueCwWord, Idcf ? Depth + 1 : 0> prefix_cw_coeff{};
bool assigned{false};
};
template <std::size_t Depth, typename ValueCwWord, bool Wild = false,
std::size_t TailLen = 0, bool Blocked = false, bool Idcf = false>
struct cmp_storage
{
using value_cw_word = ValueCwWord;
using value_cw_array = std::array<value_cw_word, Depth>;
using tail_array = std::array<value_cw_word, TailLen>;
static constexpr std::size_t prefix_cw_len = Idcf ? Depth + 1 : 0;
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
cmp_storage() = default;
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
tail_array tail = {}, tail_array tail_coeff = {},
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
{
if constexpr (Wild && Blocked)
wild_.tail_coeff = tail_coeff;
else
(void)tail_coeff;
if constexpr (Wild && Idcf)
wild_.prefix_cw_coeff = prefix_coeff;
else
(void)prefix_coeff;
}
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
value_cw_array coeff, value_cw_word cw_last_coeff,
tail_array tail = {}, tail_array tail_coeff = {},
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
{
if constexpr (Wild)
{
wild_.value_cw_coeff = coeff;
wild_.cw_last_coeff = cw_last_coeff;
if constexpr (Blocked)
wild_.tail_coeff = tail_coeff;
if constexpr (Idcf)
wild_.prefix_cw_coeff = prefix_coeff;
}
else
{
(void)coeff;
(void)cw_last_coeff;
(void)tail_coeff;
(void)prefix_coeff;
}
}
const value_cw_array & value_cw() const { return value_cw_; }
uint64_t value_cw(std::size_t level) const
{
return static_cast<uint64_t>(value_cw_[level]);
}
HEDLEY_NO_THROW
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
HEDLEY_NO_THROW
const tail_array & tail_cw() const noexcept { return tail_; }
uint64_t tail_cw(std::size_t i) const
{
return static_cast<uint64_t>(tail_[i]);
}
HEDLEY_NO_THROW
uint64_t cmp_addend() const noexcept
{
return static_cast<uint64_t>(cmp_addend_);
}
HEDLEY_NO_THROW
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
HEDLEY_NO_THROW
bool has_cmp() const noexcept { return cmp_.active; }
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws() const noexcept { return prefix_cw_; }
uint64_t prefix_cw(std::size_t i) const
{
return static_cast<uint64_t>(prefix_cw_[i]);
}
static constexpr bool is_wildcard = Wild;
HEDLEY_NO_THROW
bool cmp_assigned() const noexcept
{
if constexpr (Wild)
return wild_.assigned;
else
return true;
}
/// Patch the (public) value CWs / `cw_last` in place for a resolved δ and
/// install this party's `cmp_addend` share. No-op on the CWs when there is
/// no wildcard coefficient table (trivial domain-edge cmp).
void assign_cmp_delta(uint64_t delta, uint64_t addend_share)
{
static_assert(Wild,
"assign_cmp on a key whose comparison payload is not a wildcard");
if constexpr (Wild)
{
const uint64_t mask = cmp_.mask;
for (std::size_t i = 0; i < Depth; ++i)
{
const uint64_t base = static_cast<uint64_t>(value_cw_[i]);
const uint64_t c = static_cast<uint64_t>(wild_.value_cw_coeff[i]);
value_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
if constexpr (Blocked)
{
for (std::size_t i = 0; i < TailLen; ++i)
{
const uint64_t base = static_cast<uint64_t>(tail_[i]);
const uint64_t c = static_cast<uint64_t>(wild_.tail_coeff[i]);
tail_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
}
const uint64_t lbase = static_cast<uint64_t>(cw_last_);
const uint64_t lc = static_cast<uint64_t>(wild_.cw_last_coeff);
cw_last_ = static_cast<value_cw_word>((lbase + lc * delta) & mask);
if constexpr (Idcf)
{
for (std::size_t i = 0; i < prefix_cw_len; ++i)
{
const uint64_t base = static_cast<uint64_t>(prefix_cw_[i]);
const uint64_t c = static_cast<uint64_t>(wild_.prefix_cw_coeff[i]);
prefix_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
}
cmp_addend_ = static_cast<value_cw_word>(addend_share & mask);
wild_.assigned = true;
}
}
private:
detail::cmp_meta cmp_{};
value_cw_array value_cw_{};
value_cw_word cw_last_{0};
value_cw_word cmp_addend_{0};
tail_array tail_{};
prefix_cw_array prefix_cw_{};
cmp_wild_state<Depth, value_cw_word, Wild, TailLen, Idcf> wild_{};
};
/// Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth = 0,
std::size_t CmpOutBits = 0, bool CmpWild = false,
std::size_t CmpBlock = 0, bool CmpIdcf = false>
struct incr_key_base
{
public:
using interior_prg = InteriorPRG;
using exterior_prg = ExteriorPRG;
using interior_node = typename InteriorPRG::block_type;
using exterior_node = typename ExteriorPRG::block_type;
using input_type = dpf::concrete_type_t<InputT>;
using placed_tuple = PlacedTuple;
using node_type = exterior_node;
static constexpr std::size_t cmp_depth = CmpDepth;
/// Comparison output group width in bits (0 when there is no cmp channel).
static constexpr std::size_t cmp_out_bits = CmpOutBits;
/// True when the comparison payload is an unassigned wildcard.
static constexpr bool cmp_is_wildcard = CmpWild;
/// 0 = per-level path-sum. `B >= 1` = blocked checkpoints of width `B`.
static constexpr std::size_t cmp_block = CmpBlock;
static constexpr bool cmp_idcf = CmpIdcf;
static constexpr std::size_t max_output_level =
detail::incr::max_tree_level_v<node_type, PlacedTuple>;
/// Residual tail width. 2 only when dropping those levels does not cut an
/// output and the comparison itself is what sets the tree height.
static constexpr std::size_t cmp_q = [] {
if (CmpBlock == 0 || CmpDepth <= 2)
return std::size_t{0};
if (max_output_level > CmpDepth - 2)
return std::size_t{0};
return std::size_t{2};
}();
static constexpr std::size_t cmp_h =
(CmpBlock == 0) ? CmpDepth : (CmpDepth - cmp_q);
static constexpr std::size_t cmp_checkpoints =
(CmpBlock == 0 || cmp_h == 0) ? 0 : (cmp_h + CmpBlock - 1) / CmpBlock;
static constexpr std::size_t cmp_tail =
(CmpBlock == 0 || cmp_q == 0) ? 0 : (std::size_t{1} << cmp_q);
/// Multi-level / comparison keys route through the slot-aware eval path.
static constexpr bool is_multilevel = true;
/// Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
/// bit / ≤8-bit payload, 2 for ≤16, 4 for ≤32, 8 for ≤64). Value CWs and
/// the addend share are stored in this word.
using value_cw_word = utils::integral_type_from_bitlength_t<
(CmpOutBits == 0 ? std::size_t{1} : CmpOutBits)>;
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
static constexpr std::size_t depth = std::max(max_output_level,
(CmpBlock == 0) ? CmpDepth : cmp_h);
static constexpr std::size_t value_cw_len =
(CmpBlock == 0) ? depth
: (cmp_checkpoints == 0 ? std::size_t{1} : cmp_checkpoints);
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
using integral_type = utils::integral_type_from_bitlength_t<
input_bits, utils::bitlength_of_v<std::size_t>>;
static_assert(num_outputs > 0 || CmpDepth > 0,
"incremental DPF needs at least one output or a comparison channel");
static_assert(detail::incr::all_prefixes_ok_v<node_type, PlacedTuple>,
"at<N> is shorter than the packing lanes required by an output");
using correction_words_array = std::array<interior_node, depth>;
using correction_advice_array = std::array<psnip_uint8_t, depth>;
using value_cw_array = std::array<value_cw_word, value_cw_len>;
using tail_array = std::array<value_cw_word, cmp_tail>;
static constexpr std::size_t prefix_cw_len = CmpIdcf ? depth + 1 : 0;
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
using meta_array = std::array<detail::incr::slot_meta, num_outputs>;
static constexpr meta_array meta =
detail::incr::build_meta<node_type, PlacedTuple>();
template <std::size_t I, typename = void>
struct output_type_at
{
using type = void;
};
template <std::size_t I>
struct output_type_at<I, std::enable_if_t<(I < num_outputs)>>
{
using type = typename std::tuple_element_t<I, PlacedTuple>::output_type;
};
template <std::size_t I>
using output_type_t = typename output_type_at<I>::type;
template <std::size_t I>
using concrete_output_type = concrete_type_t<output_type_t<I>>;
template <std::size_t I>
static constexpr std::size_t lg_outputs_per_leaf_of =
(num_outputs > 0) ? meta[I].lg_opl : 0;
template <std::size_t I>
static constexpr std::size_t outputs_per_leaf_of = std::size_t{1}
<< lg_outputs_per_leaf_of<I>;
private:
template <std::size_t... Is>
static auto wrapper_tuple_t(std::index_sequence<Is...>)
-> std::tuple<
dpf::leaf_wrapper<output_type_t<Is>, exterior_node>...>;
template <std::size_t... Is>
static auto leaf_tuple_type(std::index_sequence<Is...>)
-> std::tuple<
dpf::leaf_node_t<exterior_node, concrete_output_type<Is>>...>;
public:
using leaf_wrapper_tuple = decltype(wrapper_tuple_t(
std::make_index_sequence<num_outputs>{}));
/// Raw leaf shares (pre-wrapper), matching classic `leaf_tuple` for asio.
using leaf_tuple = decltype(leaf_tuple_type(
std::make_index_sequence<num_outputs>{}));
using offset_type = offset_wrapper<InputT>;
template <std::size_t... Is>
static constexpr auto wildcard_mask_tuple(std::index_sequence<Is...>)
{
return std::make_tuple(dpf::is_wildcard_v<output_type_t<Is>>...);
}
static constexpr auto wildcard_mask =
wildcard_mask_tuple(std::make_index_sequence<num_outputs>{});
static constexpr std::size_t deepest_prefix = [] {
if constexpr (num_outputs == 0)
return CmpDepth;
else
{
std::size_t m = 0;
for (std::size_t i = 0; i < num_outputs; ++i)
m = std::max(m, meta[i].prefix);
return m;
}
}();
/// First output (source order) whose prefix equals `deepest_prefix`.
static constexpr std::size_t deepest_output = [] {
if constexpr (num_outputs == 0)
return std::size_t{0};
else
{
for (std::size_t i = 0; i < num_outputs; ++i)
{
if (meta[i].prefix == deepest_prefix)
return i;
}
return std::size_t{0};
}
}();
/// Classic-shaped packing traits for deepest-group interval/sequence APIs.
static constexpr std::size_t outputs_per_leaf =
(num_outputs > 0) ? outputs_per_leaf_of<deepest_output> : 1;
static constexpr std::size_t lg_outputs_per_leaf =
(num_outputs > 0) ? lg_outputs_per_leaf_of<deepest_output> : 0;
template <std::size_t... Is>
static auto addend_tuple_t(std::index_sequence<Is...>)
-> std::tuple<output_type_t<Is>...>;
using addend_tuple = decltype(addend_tuple_t(
std::make_index_sequence<num_outputs>{}));
incr_key_base(interior_node root,
const correction_words_array & correction_words,
const correction_advice_array & correction_advice,
leaf_wrapper_tuple leaves, input_type offset_share,
detail::cmp_meta cmp = {}, value_cw_array value_cws = {},
uint64_t cw_last_in = 0, uint64_t cmp_addend_in = 0,
addend_tuple addends = {}, value_cw_array value_cw_coeff = {},
uint64_t cw_last_coeff_in = 0, tail_array tail_in = {},
tail_array tail_coeff_in = {}, prefix_cw_array prefix_in = {},
prefix_cw_array prefix_coeff_in = {})
: leaf_nodes{std::move(leaves)},
offset_x{offset_share},
cmp_store_{cmp, value_cws,
static_cast<value_cw_word>(cw_last_in),
static_cast<value_cw_word>(cmp_addend_in),
value_cw_coeff,
static_cast<value_cw_word>(cw_last_coeff_in),
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
public_addends{std::move(addends)},
root_{root},
correction_words_{correction_words},
correction_advice_{correction_advice},
common_part_hash_{utils::get_common_part_hash(correction_words_,
correction_advice_, leaf_nodes, wildcard_mask)}
{ }
incr_key_base(const incr_key_base &) = default;
incr_key_base(incr_key_base &&) = default;
incr_key_base & operator=(const incr_key_base &) = default;
incr_key_base & operator=(incr_key_base &&) = default;
const interior_node & root() const { return root_; }
const correction_words_array & correction_words() const
{
return correction_words_;
}
const correction_advice_array & correction_advice() const
{
return correction_advice_;
}
const value_cw_array & value_cw() const { return cmp_store_.value_cw(); }
HEDLEY_NO_THROW
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws() const noexcept
{
return cmp_store_.prefix_cws();
}
uint64_t prefix_cw(std::size_t i) const { return cmp_store_.prefix_cw(i); }
/// Party-local share of the constant absorb (`if_false`, or
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
HEDLEY_NO_THROW
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
HEDLEY_NO_THROW
const detail::cmp_meta & cmp() const noexcept { return cmp_store_.cmp(); }
const digest_type & common_part_hash() const { return common_part_hash_; }
const leaf_wrapper_tuple & leaves() const { return leaf_nodes; }
const interior_node & correction_word(std::size_t level) const
{
return correction_words_[level];
}
psnip_uint8_t correction_advice(std::size_t level) const
{
return correction_advice_[level];
}
auto correction_word(std::size_t level, bool direction) const
{
return set_lo_bit(correction_word(level),
(correction_advice_[level] >> direction) & 1);
}
uint64_t value_cw(std::size_t level) const { return cmp_store_.value_cw(level); }
const tail_array & tail_cw() const { return cmp_store_.tail_cw(); }
uint64_t tail_cw(std::size_t i) const { return cmp_store_.tail_cw(i); }
template <std::size_t I = 0>
const auto & leaf() const
{
static_assert(num_outputs > 0, "cmp-only key has no leaves");
if constexpr (dpf::is_wildcard_v<output_type_t<I>>)
return std::get<I>(leaf_nodes).raw_leaf();
else
return std::get<I>(leaf_nodes).get();
}
template <std::size_t I = 0>
const auto & beaver() const
{
static_assert(num_outputs > 0, "cmp-only key has no beavers");
return std::get<I>(leaf_nodes).beaver();
}
#ifdef LIBDPF_HAS_ASIO
template <std::size_t I = 0,
typename PeerT,
typename OutputType,
typename CompletionToken>
auto async_assign_leaf(PeerT & peer, OutputType && output_share,
CompletionToken && token)
{
static_assert(num_outputs > 0, "cmp-only key has no leaves");
static_assert(dpf::is_wildcard_v<output_type_t<I>>,
"async_assign_leaf requires a wildcard output slot");
return dpf::asio::async_assign_wildcard_output<I>(
peer, *this, std::forward<OutputType>(output_share),
std::forward<CompletionToken>(token));
}
#endif
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior(const interior_node & node,
const interior_node & cw, bool dir) noexcept
{
return dpf::xor_if_lo_bit(
interior_prg::eval(unset_lo_2bits(node), dir), cw, node);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior01(const interior_node & node,
const interior_node & cw0, const interior_node & cw1) noexcept
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto kids = interior_prg::eval01(unset_lo_2bits(node));
return std::array<interior_node, 2>{
dpf::xor_if_lo_bit(kids[0], cw0, node),
dpf::xor_if_lo_bit(kids[1], cw1, node)};
HEDLEY_PRAGMA(GCC diagnostic pop)
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void traverse_interior01_x4(const interior_node * HEDLEY_RESTRICT parents,
const interior_node & cw0, const interior_node & cw1,
interior_node * HEDLEY_RESTRICT left,
interior_node * HEDLEY_RESTRICT right) noexcept
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
alignas(interior_node) interior_node seeds[4];
DPF_UNROLL_LOOP
for (std::size_t i = 0; i < 4; ++i)
seeds[i] = unset_lo_2bits(parents[i]);
interior_prg::eval01_x4(seeds, left, right);
DPF_UNROLL_LOOP
for (std::size_t i = 0; i < 4; ++i)
{
left[i] = dpf::xor_if_lo_bit(left[i], cw0, parents[i]);
right[i] = dpf::xor_if_lo_bit(right[i], cw1, parents[i]);
}
HEDLEY_PRAGMA(GCC diagnostic pop)
}
template <std::size_t I = 0>
HEDLEY_NO_THROW
auto traverse_exterior(const interior_node & node) const noexcept
{
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
using Out = concrete_output_type<I>;
constexpr auto pos =
meta[I].pos_base + meta[I].index_in_group * meta[I].block_len;
constexpr auto count = meta[I].block_len;
using leaf_type = dpf::leaf_node_t<exterior_node, Out>;
leaf_type mask{};
auto seed_ =
utils::to_exterior_node<exterior_node>(unset_lo_2bits(node));
exterior_prg::eval(seed_, leaf_blocks<exterior_node>(mask),
static_cast<psnip_uint32_t>(count),
static_cast<psnip_uint32_t>(pos));
// Subtractive share: CW_if_t − mask so reconstruct(y0, y1) = y0 − y1 = β.
return dpf::subtract_leaf<Out>(
dpf::get_if_lo_bit(std::get<I>(leaf_nodes).get(), node), mask);
}
leaf_wrapper_tuple leaf_nodes;
offset_type offset_x;
/// Public `if_false` addends for `eq` / `eq_at` slots.
addend_tuple public_addends{};
HEDLEY_NO_THROW
bool has_cmp() const noexcept { return cmp_store_.has_cmp(); }
/// True once a wildcard comparison payload has been assigned (always true
/// for concrete cmp keys and for keys without a comparison channel).
HEDLEY_NO_THROW
bool cmp_assigned() const noexcept { return cmp_store_.cmp_assigned(); }
/// Patch the value CWs / `cw_last` for a resolved payload δ and install
/// this party's `cmp_addend` share. Only valid for wildcard cmp keys; see
/// the free `dpf::assign_cmp`. No tree re-walk / re-PRG.
void assign_cmp_delta(uint64_t delta, uint64_t addend_share)
{
cmp_store_.assign_cmp_delta(delta, addend_share);
}
private:
cmp_storage<value_cw_len, value_cw_word, CmpWild, cmp_tail, (CmpBlock > 0),
CmpIdcf>
cmp_store_{};
interior_node root_;
correction_words_array correction_words_;
correction_advice_array correction_advice_;
digest_type common_part_hash_;
}; // struct incr_key_base
} // namespace incr
} // namespace detail
// ---------------------------------------------------------------------------
// Unified `dpf_key`: one key type for classic, multi-level (`at<N>`), and
// comparison (`cmp_channel_tag`) packs. Each of OutputT/OutputTs is one of:
// - a bare output type (planted at full input bitlength)
// - a `detail::incr::placed<N,T>` (from `at<N>`)
// - a `cmp_channel_tag<Depth>` (phantom: sets the comparison channel depth)
// Classic-shaped packs (all bare) keep the byte-identical single-level layout.
// ---------------------------------------------------------------------------
namespace detail
{
template <typename Derived, typename InteriorPRG, typename ExteriorPRG,
typename InputT, typename OutputT, typename ...OutputTs>
using dpf_key_base_t = std::conditional_t<
dpf::detail::incr::is_classic_pack_v<OutputT, OutputTs...>,
classic_dpf_key_impl<Derived, InteriorPRG, ExteriorPRG, InputT,
OutputT, OutputTs...>,
dpf::detail::incr::incr_key_base<InteriorPRG, ExteriorPRG, InputT,
typename dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::placed_tuple,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_depth,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_out_bits,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_wild,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_block,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_idcf>>;
} // namespace detail
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
struct dpf_key
: detail::dpf_key_base_t<
dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>
{
using base_type = detail::dpf_key_base_t<
dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>;
using base_type::base_type;
};
namespace detail
{
namespace incr
{
// Assemble the public dpf_key type for a (PlacedTuple, CmpDepth) pair by
// expanding the placed slots into the output pack and appending the phantom
// cmp tag when a comparison channel is present.
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
typename ExteriorPRG, typename InputT, typename ...Ps>
struct assemble_key
{
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>>;
};
template <std::size_t CmpOutBits, bool CmpWild, std::size_t CmpBlock,
bool CmpIdcf, typename InteriorPRG,
typename ExteriorPRG, typename InputT, typename ...Ps>
struct assemble_key<0, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, InteriorPRG,
ExteriorPRG, InputT, Ps...>
{
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...>;
};
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
struct incr_dpf_key_of;
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
bool CmpWild, std::size_t CmpBlock, bool CmpIdcf>
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>
{
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock,
CmpIdcf, InteriorPRG, ExteriorPRG, InputT, Ps...>::type;
};
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>::type;
} // namespace incr
} // namespace detail
template <typename PRG>
struct pseudorandom_root_sampler
{
using root_type = typename PRG::block_type;
pseudorandom_root_sampler(
root_type && seed = dpf::uniform_sample<root_type>())
: seed_{seed}, counter_{0} { }
root_type operator()(psnip_uint32_t i) const
{
return PRG::eval(seed_, i);
}
root_type operator()()
{
return this->operator()(counter_.fetch_add(1));
}
const root_type & seed() const { return seed_; }
psnip_uint32_t count() const { return counter_; }
private:
root_type seed_;
std::atomic_uint32_t counter_;
};
namespace utils
{
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
struct dpf_type
{
using type = dpf_key<InteriorPRG, ExteriorPRG,
std::decay_t<InputT>,
std::decay_t<OutputT>,
std::decay_t<OutputTs>...>;
};
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
using dpf_type_t = typename dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>::type;
} // namespace utils
namespace detail
{
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
auto make_dpf_impl(dpfargs<InputT, OutputT, OutputTs...> args, root_sampler_t<InteriorPRG> && root_sampler = dpf::uniform_sample<typename InteriorPRG::block_type>)
{
using dpf_type = utils::dpf_type_t<InteriorPRG, ExteriorPRG, InputT,
OutputT, OutputTs...>;
using interior_node = typename dpf_type::interior_node;
using input_type = typename dpf_type::input_type;
using correction_words_array = typename dpf_type::correction_words_array;
using correction_advice_array = typename dpf_type::correction_advice_array;
constexpr auto depth = dpf_type::depth;
auto mask = dpf_type::msb_mask;
input_type x, x0{}, x1{};
if constexpr (dpf::is_wildcard_v<InputT>)
{
auto sampled = args.x();
x = std::get<0>(sampled);
x0 = std::get<1>(sampled).raw();
x1 = std::get<2>(sampled).raw();
}
else
{
x = args.x;
}
utils::flip_msb_if_signed_integral(x);
const interior_node root[2] = {
dpf::unset_lo_bit(root_sampler()),
dpf::set_lo_bit(root_sampler())
};
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
correction_words_array correction_words;
HEDLEY_PRAGMA(GCC diagnostic pop)
correction_advice_array correction_advice;
interior_node parent[2] = { root[0], root[1] };
bool advice[2];
for (std::size_t level = 0; level < depth; ++level, mask >>= 1)
{
bool bit = !!(mask & x);
advice[0] = dpf::get_lo_bit_and_clear_lo_2bits(parent[0]);
advice[1] = dpf::get_lo_bit_and_clear_lo_2bits(parent[1]);
auto child0 = InteriorPRG::eval01(parent[0]);
auto child1 = InteriorPRG::eval01(parent[1]);
interior_node child[2] = {
child0[0] ^ child1[0],
child0[1] ^ child1[1]
};
bool t[2] = {
static_cast<bool>(dpf::get_lo_bit(child[0]) ^ !bit),
static_cast<bool>(dpf::get_lo_bit(child[1]) ^ bit)
};
auto cw = dpf::set_lo_bit(child[!bit], t[bit]);
parent[0] = dpf::xor_if(child0[bit], cw, advice[0]);
parent[1] = dpf::xor_if(child1[bit], cw, advice[1]);
correction_words[level] = child[!bit];
correction_advice[level] = static_cast<psnip_uint8_t>(t[1] << 1) | t[0];
}
bool sign0 = dpf::get_lo_bit(parent[0]);
// bool sign1 = dpf::get_lo_bit(parent[1]);
auto [pair0, pair1] = std::apply([&x, &parent, &sign0](auto && ...ys)
{
return dpf::make_leaves<ExteriorPRG>(x,
dpf::unset_lo_2bits(parent[0]),
dpf::unset_lo_2bits(parent[1]),
sign0, std::size_t{0}, ys...); }, args.y);
auto && [leaves0, beavers0] = pair0;
auto && [leaves1, beavers1] = pair1;
return std::make_tuple(correction_words, correction_advice,
std::make_tuple(root[0], leaves0, beavers0, x0),
std::make_tuple(root[1], leaves1, beavers1, x1));
} // make_dpf_impl
} // namespace detail
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT = dpf::bit,
typename ...OutputTs>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf(dpfargs<InputT, OutputT, OutputTs...> args, root_sampler_t<InteriorPRG> && root_sampler = dpf::uniform_sample<typename InteriorPRG::block_type>)
{
static_assert(!is_secret_share_v<InputT>,
"make_dpf: domain point must be plaintext");
static_assert(!is_secret_share_v<OutputT>
&& (!is_secret_share_v<OutputTs> && ...),
"make_dpf: payloads must be plaintext");
using dpf_type = utils::dpf_type_t<InteriorPRG, ExteriorPRG, InputT,
OutputT, OutputTs...>;
auto [correction_words, correction_advice,
tuple0, tuple1] = detail::make_dpf_impl<InteriorPRG, ExteriorPRG>(args,
std::forward<root_sampler_t<InteriorPRG>>(root_sampler));
auto & [root0, leaves0, beavers0, offset0] = tuple0;
auto & [root1, leaves1, beavers1, offset1] = tuple1;
return dpf::make_party_key_pair(
dpf_type{root0, correction_words, correction_advice,
leaves0, beavers0, offset0},
dpf_type{root1, correction_words, correction_advice,
leaves1, beavers1, offset1});
} // make_dpf
// Convenience `make_dpf(x, y...)` lives in incremental.hpp so `at<>` and
// mixed-width packs share one entry point with the classic path.
namespace detail
{
template <typename DpfKey,
std::size_t ...Is>
auto make_dpf_random_point_impl(std::index_sequence<Is...>)
{
using input_type = typename DpfKey::input_type;
using interior_prg = typename DpfKey::interior_prg;
using exterior_prg = typename DpfKey::exterior_prg;
input_type x = dpf::uniform_sample<input_type>();
input_type x0 = dpf::uniform_sample<input_type>();
input_type x1 = static_cast<input_type>(x - x0);
auto keys = make_dpf<interior_prg, exterior_prg>(
x, typename DpfKey::concrete_output_type<Is>(1)...);
return std::make_tuple(std::move(keys.first), std::move(keys.second),
x0, x1);
}
} // namespace detail
template <typename DpfKey>
auto make_dpf_random_point()
{
return detail::make_dpf_random_point_impl<DpfKey>(
std::make_index_sequence<
std::tuple_size_v<typename DpfKey::outputs_tuple>>{});
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT = dpf::bit,
typename ...OutputTs>
auto deduce_dpf_type(InputT x, OutputT y = dpf::bit::one, OutputTs ...ys)
{
return utils::dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>{};
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT = dpf::bit,
typename ...OutputTs>
auto deduce_dpf_type(dpf::dpfargs<InputT, OutputT, OutputTs...> args)
{
return utils::dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>{};
}
#define DEDUCE_DPF_TYPE_T(...) typename decltype(dpf::deduce_dpf_type(__VA_ARGS__))::type;
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__