Initial import of libdpf.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
e4e666f459
4563 changed files with 1690372 additions and 0 deletions
465
include/dpf/output_buffer.hpp
Normal file
465
include/dpf/output_buffer.hpp
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
/// @file dpf/output_buffer.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_OUTPUT_BUFFER_HPP__
|
||||
#define LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
#include <limits>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "dpf/aligned_allocator.hpp"
|
||||
#include "dpf/leaf_node.hpp"
|
||||
#include "dpf/utils.hpp"
|
||||
#include "dpf/bit.hpp"
|
||||
#include "dpf/bit_array.hpp"
|
||||
#include "dpf/packed_array.hpp"
|
||||
#include "dpf/secret_share.hpp"
|
||||
#include "dpf/sequence_recipe.hpp"
|
||||
#include "dpf/sequence_utils.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
|
||||
/// Buffer element type for leaf eval of `KeyT`: party-tagged subtractive
|
||||
/// share when `KeyT` is a `party_key`, otherwise the concrete output.
|
||||
template <typename KeyT, typename OutputT, bool = is_party_key_v<KeyT>>
|
||||
struct leaf_buffer_elem
|
||||
{
|
||||
using type = OutputT;
|
||||
};
|
||||
template <typename KeyT, typename OutputT>
|
||||
struct leaf_buffer_elem<KeyT, OutputT, true>
|
||||
{
|
||||
using type = subtractive_share<OutputT, party_of_v<KeyT>>;
|
||||
};
|
||||
template <typename KeyT, typename OutputT>
|
||||
using leaf_buffer_elem_t = typename leaf_buffer_elem<KeyT, OutputT>::type;
|
||||
|
||||
/// Buffer element type for comparison eval of `KeyT`.
|
||||
template <typename KeyT, typename Beta, bool = is_party_key_v<KeyT>>
|
||||
struct cmp_buffer_elem
|
||||
{
|
||||
using type = Beta;
|
||||
};
|
||||
template <typename KeyT, typename Beta>
|
||||
struct cmp_buffer_elem<KeyT, Beta, true>
|
||||
{
|
||||
using type = additive_share<Beta, party_of_v<KeyT>>;
|
||||
};
|
||||
template <typename KeyT, typename Beta>
|
||||
using cmp_buffer_elem_t = typename cmp_buffer_elem<KeyT, Beta>::type;
|
||||
|
||||
/// `std::vector(n)` value-initializes every slot. Interval / full eval
|
||||
/// overwrites the whole buffer, so skip default-construction for trivial
|
||||
/// `T`. Non-trivial outputs still run their default constructor.
|
||||
template <typename T,
|
||||
std::size_t Alignment>
|
||||
class output_buffer_allocator : public aligned_allocator<T, Alignment>
|
||||
{
|
||||
public:
|
||||
using is_always_equal = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = output_buffer_allocator<U, Alignment>;
|
||||
};
|
||||
|
||||
output_buffer_allocator() noexcept = default;
|
||||
output_buffer_allocator(const output_buffer_allocator &) noexcept = default;
|
||||
template <typename U>
|
||||
output_buffer_allocator(const output_buffer_allocator<U, Alignment> &) noexcept {}
|
||||
|
||||
template <typename U>
|
||||
void construct(U * p)
|
||||
noexcept(std::is_nothrow_default_constructible_v<U>)
|
||||
{
|
||||
if constexpr (!std::is_trivially_default_constructible_v<U>)
|
||||
{
|
||||
::new (static_cast<void *>(p)) U();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, typename A0, typename ...Args>
|
||||
void construct(U * p, A0 && a0, Args && ...args)
|
||||
{
|
||||
::new (static_cast<void *>(p)) U(std::forward<A0>(a0),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void destroy(U * p) noexcept
|
||||
{
|
||||
if constexpr (!std::is_trivially_destructible_v<U>)
|
||||
{
|
||||
p->~U();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t A, typename U, std::size_t B>
|
||||
constexpr bool operator==(const output_buffer_allocator<T, A> &,
|
||||
const output_buffer_allocator<U, B> &) noexcept
|
||||
{
|
||||
return A == B;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t A, typename U, std::size_t B>
|
||||
constexpr bool operator!=(const output_buffer_allocator<T, A> & lhs,
|
||||
const output_buffer_allocator<U, B> & rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
std::size_t Alignment = utils::max_align_v>
|
||||
class output_buffer final
|
||||
: private std::vector<T, dpf::output_buffer_allocator<T, Alignment>>
|
||||
{
|
||||
private:
|
||||
using vector = std::vector<T, dpf::output_buffer_allocator<T, Alignment>>;
|
||||
public:
|
||||
using value_type = typename vector::value_type;
|
||||
using iterator = typename vector::iterator;
|
||||
using const_iterator = typename vector::const_iterator;
|
||||
using size_type = typename vector::size_type;
|
||||
output_buffer() noexcept = default;
|
||||
explicit output_buffer(size_type size) : vector(size) { }
|
||||
output_buffer(output_buffer &&) noexcept = default;
|
||||
output_buffer(const output_buffer &) = delete;
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||
output_buffer & operator=(const output_buffer &) = delete;
|
||||
~output_buffer() = default;
|
||||
|
||||
// "selectively public" inheritance
|
||||
using vector::at;
|
||||
using vector::operator[];
|
||||
using vector::data;
|
||||
using vector::begin;
|
||||
using vector::cbegin;
|
||||
using vector::end;
|
||||
using vector::cend;
|
||||
using vector::size;
|
||||
};
|
||||
|
||||
template <>
|
||||
class output_buffer<dpf::bit> : public dpf::dynamic_bit_array<>
|
||||
{
|
||||
private:
|
||||
using size_type = typename dpf::dynamic_bit_array<>::size_type;
|
||||
public:
|
||||
explicit output_buffer(size_type size) : dynamic_bit_array(size) { }
|
||||
output_buffer(output_buffer &&) noexcept = default;
|
||||
output_buffer(const output_buffer &) = delete;
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||
output_buffer & operator=(const output_buffer &) = delete;
|
||||
~output_buffer() = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
class output_buffer<dpf::twobit> : public dpf::dynamic_packed_array<dpf::twobit>
|
||||
{
|
||||
using base = dpf::dynamic_packed_array<dpf::twobit>;
|
||||
public:
|
||||
using size_type = typename base::size_type;
|
||||
explicit output_buffer(size_type size) : base(size) { }
|
||||
output_buffer(output_buffer &&) noexcept = default;
|
||||
output_buffer(const output_buffer &) = delete;
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||
output_buffer & operator=(const output_buffer &) = delete;
|
||||
~output_buffer() = default;
|
||||
};
|
||||
|
||||
template <>
|
||||
class output_buffer<dpf::nyble> : public dpf::dynamic_packed_array<dpf::nyble>
|
||||
{
|
||||
using base = dpf::dynamic_packed_array<dpf::nyble>;
|
||||
public:
|
||||
using size_type = typename base::size_type;
|
||||
explicit output_buffer(size_type size) : base(size) { }
|
||||
output_buffer(output_buffer &&) noexcept = default;
|
||||
output_buffer(const output_buffer &) = delete;
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default;
|
||||
output_buffer & operator=(const output_buffer &) = delete;
|
||||
~output_buffer() = default;
|
||||
};
|
||||
|
||||
#define LIBDPF_PACKED_SHARE_BUFFER(LANE, PARTY) \
|
||||
template <> \
|
||||
class output_buffer<subtractive_share<LANE, PARTY>> \
|
||||
: public packed_share_output<LANE, PARTY> \
|
||||
{ \
|
||||
using base = packed_share_output<LANE, PARTY>; \
|
||||
public: \
|
||||
using size_type = typename base::size_type; \
|
||||
explicit output_buffer(size_type size) : base(size) {} \
|
||||
output_buffer(output_buffer &&) noexcept = default; \
|
||||
output_buffer(const output_buffer &) = delete; \
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
||||
output_buffer & operator=(const output_buffer &) = delete; \
|
||||
~output_buffer() = default; \
|
||||
};
|
||||
|
||||
LIBDPF_PACKED_SHARE_BUFFER(dpf::twobit, 0);
|
||||
LIBDPF_PACKED_SHARE_BUFFER(dpf::twobit, 1);
|
||||
LIBDPF_PACKED_SHARE_BUFFER(dpf::nyble, 0);
|
||||
LIBDPF_PACKED_SHARE_BUFFER(dpf::nyble, 1);
|
||||
#undef LIBDPF_PACKED_SHARE_BUFFER
|
||||
|
||||
/// Packed bit share buffers reuse the bit-array image; iterators yield shares.
|
||||
#define LIBDPF_BIT_SHARE_BUFFER(PARTY) \
|
||||
template <> \
|
||||
class output_buffer<subtractive_share<dpf::bit, PARTY>> \
|
||||
: public dpf::dynamic_bit_array<> \
|
||||
{ \
|
||||
private: \
|
||||
using size_type = typename dpf::dynamic_bit_array<>::size_type; \
|
||||
public: \
|
||||
explicit output_buffer(size_type size) : dynamic_bit_array(size) {} \
|
||||
output_buffer(output_buffer &&) noexcept = default; \
|
||||
output_buffer(const output_buffer &) = delete; \
|
||||
output_buffer & operator=(output_buffer &&) noexcept = default; \
|
||||
output_buffer & operator=(const output_buffer &) = delete; \
|
||||
~output_buffer() = default; \
|
||||
};
|
||||
LIBDPF_BIT_SHARE_BUFFER(0);
|
||||
LIBDPF_BIT_SHARE_BUFFER(1);
|
||||
#undef LIBDPF_BIT_SHARE_BUFFER
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I = 0,
|
||||
typename InputT>
|
||||
auto make_output_buffer_for_interval(InputT from, InputT to)
|
||||
{
|
||||
using dpf_type = DpfKey;
|
||||
using output_type = typename DpfKey::concrete_output_type<I>;
|
||||
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
|
||||
|
||||
utils::flip_msb_if_signed_integral(from);
|
||||
utils::flip_msb_if_signed_integral(to);
|
||||
|
||||
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
|
||||
return dpf::output_buffer<buffer_elem>(nodes_in_interval*dpf_type::outputs_per_leaf);
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename InputT>
|
||||
auto make_output_buffer_for_interval(InputT from, InputT to)
|
||||
{
|
||||
return std::make_tuple(
|
||||
make_output_buffer_for_interval<DpfKey, I0>(from, to),
|
||||
make_output_buffer_for_interval<DpfKey, I1>(from, to),
|
||||
make_output_buffer_for_interval<DpfKey, Is>(from, to)...);
|
||||
}
|
||||
|
||||
template <std::size_t I = 0,
|
||||
typename DpfKey,
|
||||
typename InputT>
|
||||
inline auto make_output_buffer_for_interval(const DpfKey &, InputT from, InputT to)
|
||||
{
|
||||
return make_output_buffer_for_interval<DpfKey, I>(from, to);
|
||||
}
|
||||
|
||||
template <std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename DpfKey,
|
||||
typename InputT>
|
||||
inline auto make_output_buffer_for_interval(const DpfKey &, InputT from, InputT to)
|
||||
{
|
||||
return make_output_buffer_for_interval<DpfKey, I0, I1, Is...>(from, to);
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I = 0>
|
||||
auto make_output_buffer_for_full()
|
||||
{
|
||||
using dpf_type = DpfKey;
|
||||
using input_type = typename dpf_type::input_type;
|
||||
|
||||
return make_output_buffer_for_interval<dpf_type, I>(
|
||||
std::numeric_limits<input_type>::min(),
|
||||
std::numeric_limits<input_type>::max());
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is>
|
||||
auto make_output_buffer_for_full()
|
||||
{
|
||||
return std::make_tuple(
|
||||
make_output_buffer_for_full<DpfKey, I0>(),
|
||||
make_output_buffer_for_full<DpfKey, I1>(),
|
||||
make_output_buffer_for_full<DpfKey, Is>()...);
|
||||
}
|
||||
|
||||
template <std::size_t I = 0,
|
||||
typename DpfKey>
|
||||
inline auto make_output_buffer_for_full(const DpfKey &)
|
||||
{
|
||||
return make_output_buffer_for_full<DpfKey, I>();
|
||||
}
|
||||
|
||||
template <std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename DpfKey>
|
||||
inline auto make_output_buffer_for_full(const DpfKey &)
|
||||
{
|
||||
return make_output_buffer_for_full<DpfKey, I0, I1, Is...>();
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I = 0,
|
||||
typename ForwardIterator,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
auto make_output_buffer_for_subsequence(ForwardIterator begin, ForwardIterator end, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
using dpf_type = DpfKey;
|
||||
using output_type = typename DpfKey::concrete_output_type<I>;
|
||||
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
|
||||
std::size_t points_in_sequence = std::distance(begin, end);
|
||||
|
||||
static_assert(std::is_same_v<ReturnType, return_entire_node_tag_> ||
|
||||
std::is_same_v<ReturnType, return_output_only_tag_>);
|
||||
if constexpr(std::is_same_v<ReturnType, return_entire_node_tag_>)
|
||||
{
|
||||
return dpf::output_buffer<buffer_elem>(points_in_sequence*dpf_type::outputs_per_leaf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr(std::is_same_v<typename DpfKey::concrete_output_type<0>, dpf::bit>)
|
||||
{
|
||||
auto tmp = dpf::output_buffer<buffer_elem>(points_in_sequence);
|
||||
tmp.unset();
|
||||
return std::move(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return dpf::output_buffer<buffer_elem>(points_in_sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename ForwardIterator,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
auto make_output_buffer_for_subsequence(ForwardIterator begin, ForwardIterator end, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return std::make_tuple(
|
||||
make_output_buffer_for_subsequence<DpfKey, I0>(begin, end, return_type),
|
||||
make_output_buffer_for_subsequence<DpfKey, I1>(begin, end, return_type),
|
||||
make_output_buffer_for_subsequence<DpfKey, Is>(begin, end, return_type)...);
|
||||
}
|
||||
|
||||
template <std::size_t I = 0,
|
||||
typename DpfKey,
|
||||
typename ForwardIterator,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
inline auto make_output_buffer_for_subsequence(const DpfKey &, ForwardIterator begin, ForwardIterator end, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return make_output_buffer_for_subsequence<DpfKey, I>(begin, end, return_type);
|
||||
}
|
||||
|
||||
template <std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename DpfKey,
|
||||
typename ForwardIterator,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
inline auto make_output_buffer_for_subsequence(const DpfKey &, ForwardIterator begin, ForwardIterator end, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return make_output_buffer_for_subsequence<DpfKey, I0, I1, Is...>(begin, end, return_type);
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I = 0,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
auto make_output_buffer_for_recipe_subsequence(const sequence_recipe & recipe, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
using dpf_type = DpfKey;
|
||||
using output_type = typename DpfKey::concrete_output_type<I>;
|
||||
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
|
||||
|
||||
static_assert(std::is_same_v<ReturnType, return_entire_node_tag_> ||
|
||||
std::is_same_v<ReturnType, return_output_only_tag_>);
|
||||
if constexpr(std::is_same_v<ReturnType, return_entire_node_tag_>)
|
||||
{
|
||||
return dpf::output_buffer<buffer_elem>(recipe.num_leaf_nodes()*dpf_type::outputs_per_leaf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr(std::is_same_v<typename DpfKey::concrete_output_type<0>, dpf::bit>)
|
||||
{
|
||||
auto tmp = dpf::output_buffer<buffer_elem>(recipe.output_indices().size());
|
||||
tmp.unset();
|
||||
return std::move(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return dpf::output_buffer<buffer_elem>(recipe.output_indices().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DpfKey,
|
||||
std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
inline auto make_output_buffer_for_recipe_subsequence(const sequence_recipe & recipe, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return std::make_tuple(
|
||||
make_output_buffer_for_recipe_subsequence<DpfKey, I0>(recipe, return_type),
|
||||
make_output_buffer_for_recipe_subsequence<DpfKey, I1>(recipe, return_type),
|
||||
make_output_buffer_for_recipe_subsequence<DpfKey, Is>(recipe, return_type)...);
|
||||
}
|
||||
|
||||
template <std::size_t I = 0,
|
||||
typename DpfKey,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
inline auto make_output_buffer_for_recipe_subsequence(const DpfKey &, const sequence_recipe & recipe, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return make_output_buffer_for_recipe_subsequence<DpfKey, I>(recipe, return_type);
|
||||
}
|
||||
|
||||
template <std::size_t I0,
|
||||
std::size_t I1,
|
||||
std::size_t ...Is,
|
||||
typename DpfKey,
|
||||
typename ReturnType = return_entire_node_tag_>
|
||||
inline auto make_output_buffer_for_recipe_subsequence(const DpfKey &, const sequence_recipe & recipe, ReturnType return_type = ReturnType{})
|
||||
{
|
||||
return make_output_buffer_for_recipe_subsequence<DpfKey, I0, I1, Is...>(recipe, return_type);
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template <>
|
||||
struct is_bit_array<output_buffer<bit>> : std::true_type {};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue