libdpf/include/dpf/dpf_key.hpp

1316 lines
50 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// @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__