1192 lines
44 KiB
C++
1192 lines
44 KiB
C++
/// @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;
|
||
/// 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_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>
|
||
struct cmp_wild_state { };
|
||
template <std::size_t Depth, typename ValueCwWord>
|
||
struct cmp_wild_state<Depth, ValueCwWord, true>
|
||
{
|
||
std::array<ValueCwWord, Depth> value_cw_coeff{};
|
||
ValueCwWord cw_last_coeff{0};
|
||
bool assigned{false};
|
||
};
|
||
|
||
template <std::size_t Depth, typename ValueCwWord, bool Wild = false>
|
||
struct cmp_storage
|
||
{
|
||
using value_cw_word = ValueCwWord;
|
||
using value_cw_array = std::array<value_cw_word, Depth>;
|
||
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)
|
||
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
||
cmp_addend_{cmp_addend_in} { }
|
||
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)
|
||
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
||
cmp_addend_{cmp_addend_in}
|
||
{
|
||
if constexpr (Wild)
|
||
{
|
||
wild_.value_cw_coeff = coeff;
|
||
wild_.cw_last_coeff = cw_last_coeff;
|
||
}
|
||
else
|
||
{
|
||
(void)coeff;
|
||
(void)cw_last_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]);
|
||
}
|
||
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
|
||
uint64_t cmp_addend() const noexcept
|
||
{
|
||
return static_cast<uint64_t>(cmp_addend_);
|
||
}
|
||
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
|
||
bool has_cmp() const noexcept { return cmp_.active; }
|
||
|
||
static constexpr bool is_wildcard = Wild;
|
||
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);
|
||
}
|
||
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);
|
||
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};
|
||
cmp_wild_state<Depth, value_cw_word, Wild> 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>
|
||
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;
|
||
/// 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(
|
||
detail::incr::max_tree_level_v<node_type, PlacedTuple>, CmpDepth);
|
||
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, depth>;
|
||
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)
|
||
: 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)},
|
||
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(); }
|
||
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
|
||
/// Party-local share of the constant absorb (`if_false`, or
|
||
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
|
||
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
|
||
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); }
|
||
|
||
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>
|
||
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{};
|
||
|
||
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).
|
||
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<depth, value_cw_word, CmpWild> 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>>;
|
||
|
||
} // 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,
|
||
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>>;
|
||
};
|
||
template <std::size_t CmpOutBits, bool CmpWild, typename InteriorPRG,
|
||
typename ExteriorPRG, typename InputT, typename ...Ps>
|
||
struct assemble_key<0, CmpOutBits, CmpWild, 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>
|
||
struct incr_dpf_key_of;
|
||
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
||
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
|
||
bool CmpWild>
|
||
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
|
||
CmpDepth, CmpOutBits, CmpWild>
|
||
{
|
||
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild,
|
||
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>
|
||
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
|
||
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild>::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__
|