Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
505 lines
15 KiB
C++
505 lines
15 KiB
C++
/// @file dpf/packed_array.hpp
|
|
/// @brief Dynamic array of `dpf::twobit` or `dpf::nyble` lanes.
|
|
/// @details Storage is the same bit layout as a packed leaf, so
|
|
/// `store_leaf_bytes` can memcpy a node at lane `index`.
|
|
/// Iterators hold a pointer to that storage and stay valid when
|
|
/// the owning buffer is moved.
|
|
|
|
#ifndef LIBDPF_INCLUDE_DPF_PACKED_ARRAY_HPP__
|
|
#define LIBDPF_INCLUDE_DPF_PACKED_ARRAY_HPP__
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <new>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "hedley/hedley.h"
|
|
|
|
#include "dpf/aligned_allocator.hpp"
|
|
#include "dpf/nyble.hpp"
|
|
#include "dpf/secret_share.hpp"
|
|
#include "dpf/twobit.hpp"
|
|
#include "dpf/utils.hpp"
|
|
|
|
namespace dpf
|
|
{
|
|
|
|
template <typename LaneT>
|
|
class dynamic_packed_array
|
|
{
|
|
static constexpr std::size_t lane_bits = utils::packed_lane_bits_v<LaneT>;
|
|
static_assert(lane_bits == 1 || lane_bits == 2 || lane_bits == 4,
|
|
"dynamic_packed_array lanes are 1, 2, or 4 bits");
|
|
static constexpr std::size_t lanes_per_word = 64u / lane_bits;
|
|
static constexpr unsigned lane_mask = (1u << lane_bits) - 1u;
|
|
|
|
public:
|
|
using value_type = LaneT;
|
|
using size_type = std::size_t;
|
|
using difference_type = std::ptrdiff_t;
|
|
using word_type = std::uint64_t;
|
|
|
|
private:
|
|
using allocator = aligned_allocator<word_type, utils::max_align_v>;
|
|
using unique_ptr = typename allocator::unique_ptr;
|
|
|
|
class lane_ref
|
|
{
|
|
public:
|
|
HEDLEY_NO_THROW
|
|
lane_ref(word_type * word, unsigned shift) noexcept
|
|
: word_{word}, shift_{shift} {}
|
|
|
|
HEDLEY_NO_THROW
|
|
lane_ref(const lane_ref &) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
operator LaneT() const noexcept
|
|
{
|
|
return static_cast<LaneT>((*word_ >> shift_) & lane_mask);
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
HEDLEY_ALWAYS_INLINE
|
|
lane_ref & operator=(LaneT value) noexcept
|
|
{
|
|
const unsigned bits = [](LaneT v) {
|
|
if constexpr (std::is_enum_v<LaneT> || std::is_integral_v<LaneT>)
|
|
return static_cast<unsigned>(v);
|
|
else
|
|
return static_cast<unsigned>(
|
|
static_cast<typename LaneT::integral_type>(v));
|
|
}(value);
|
|
const auto val = static_cast<word_type>(bits & lane_mask);
|
|
*word_ = (*word_ & ~(static_cast<word_type>(lane_mask) << shift_))
|
|
| (val << shift_);
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
lane_ref & operator=(const lane_ref & other) noexcept
|
|
{
|
|
return (*this = static_cast<LaneT>(other));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
friend bool operator==(lane_ref lhs, LaneT rhs) noexcept
|
|
{
|
|
return static_cast<LaneT>(lhs) == rhs;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator==(LaneT lhs, lane_ref rhs) noexcept
|
|
{
|
|
return rhs == lhs;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator!=(lane_ref lhs, LaneT rhs) noexcept
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator!=(LaneT lhs, lane_ref rhs) noexcept
|
|
{
|
|
return !(rhs == lhs);
|
|
}
|
|
|
|
private:
|
|
word_type * word_;
|
|
unsigned shift_;
|
|
};
|
|
|
|
public:
|
|
using reference = lane_ref;
|
|
|
|
class iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
using value_type = LaneT;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = void;
|
|
using reference = lane_ref;
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator() noexcept = default;
|
|
HEDLEY_NO_THROW
|
|
iterator(word_type * data, size_type index) noexcept
|
|
: data_{data}, index_{index} {}
|
|
|
|
HEDLEY_NO_THROW
|
|
lane_ref operator*() const noexcept { return ref_at(index_); }
|
|
HEDLEY_NO_THROW
|
|
lane_ref operator[](difference_type n) const noexcept
|
|
{
|
|
return ref_at(static_cast<size_type>(
|
|
static_cast<difference_type>(index_) + n));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator & operator++() noexcept { ++index_; return *this; }
|
|
HEDLEY_NO_THROW
|
|
iterator operator++(int) noexcept
|
|
{
|
|
iterator prev = *this;
|
|
++*this;
|
|
return prev;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
iterator & operator--() noexcept { --index_; return *this; }
|
|
HEDLEY_NO_THROW
|
|
iterator operator--(int) noexcept
|
|
{
|
|
iterator prev = *this;
|
|
--*this;
|
|
return prev;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator & operator+=(difference_type n) noexcept
|
|
{
|
|
index_ = static_cast<size_type>(
|
|
static_cast<difference_type>(index_) + n);
|
|
return *this;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
iterator & operator-=(difference_type n) noexcept
|
|
{
|
|
return *this += -n;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator+(iterator it, difference_type n) noexcept
|
|
{
|
|
it += n;
|
|
return it;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator+(difference_type n, iterator it) noexcept
|
|
{
|
|
return it + n;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator-(iterator it, difference_type n) noexcept
|
|
{
|
|
it -= n;
|
|
return it;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend difference_type operator-(iterator a, iterator b) noexcept
|
|
{
|
|
return static_cast<difference_type>(a.index_)
|
|
- static_cast<difference_type>(b.index_);
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
friend bool operator==(iterator a, iterator b) noexcept
|
|
{
|
|
return a.index_ == b.index_;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator!=(iterator a, iterator b) noexcept
|
|
{
|
|
return !(a == b);
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator<(iterator a, iterator b) noexcept
|
|
{
|
|
return a.index_ < b.index_;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
|
|
|
private:
|
|
HEDLEY_NO_THROW
|
|
lane_ref ref_at(size_type index) const noexcept
|
|
{
|
|
const size_type bit = index * lane_bits;
|
|
return lane_ref(data_ + (bit / 64u),
|
|
static_cast<unsigned>(bit % 64u));
|
|
}
|
|
|
|
word_type * data_ = nullptr;
|
|
size_type index_ = 0;
|
|
};
|
|
|
|
using const_iterator = iterator;
|
|
|
|
explicit dynamic_packed_array(size_type nlanes)
|
|
: nlanes_{nlanes},
|
|
nwords_{utils::quotient_ceiling(nlanes, lanes_per_word)}
|
|
{
|
|
if (nwords_ == 0)
|
|
{
|
|
return;
|
|
}
|
|
data_ = allocator{}.allocate_unique_ptr(nwords_);
|
|
if (HEDLEY_UNLIKELY(data_ == nullptr))
|
|
{
|
|
throw std::bad_alloc{};
|
|
}
|
|
std::fill_n(data_.get(), nwords_, word_type{0});
|
|
}
|
|
|
|
dynamic_packed_array(const dynamic_packed_array &) = delete;
|
|
dynamic_packed_array & operator=(const dynamic_packed_array &) = delete;
|
|
|
|
HEDLEY_NO_THROW
|
|
dynamic_packed_array(dynamic_packed_array && other) noexcept
|
|
: nlanes_{std::exchange(other.nlanes_, 0)},
|
|
nwords_{std::exchange(other.nwords_, 0)},
|
|
data_{std::move(other.data_)}
|
|
{}
|
|
|
|
HEDLEY_NO_THROW
|
|
dynamic_packed_array & operator=(dynamic_packed_array && other) noexcept
|
|
{
|
|
if (this != &other)
|
|
{
|
|
nlanes_ = std::exchange(other.nlanes_, 0);
|
|
nwords_ = std::exchange(other.nwords_, 0);
|
|
data_ = std::move(other.data_);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
~dynamic_packed_array() = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
size_type size() const noexcept { return nlanes_; }
|
|
HEDLEY_NO_THROW
|
|
bool empty() const noexcept { return nlanes_ == 0; }
|
|
HEDLEY_NO_THROW
|
|
size_type data_length() const noexcept { return nwords_; }
|
|
|
|
HEDLEY_NO_THROW
|
|
word_type * data() noexcept { return data_.get(); }
|
|
HEDLEY_NO_THROW
|
|
const word_type * data() const noexcept { return data_.get(); }
|
|
|
|
HEDLEY_NO_THROW
|
|
LaneT operator[](size_type i) const noexcept
|
|
{
|
|
assert(i < nlanes_);
|
|
const size_type bit = i * lane_bits;
|
|
const unsigned shift = static_cast<unsigned>(bit % 64u);
|
|
return static_cast<LaneT>((data_[bit / 64u] >> shift) & lane_mask);
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
lane_ref operator[](size_type i) noexcept
|
|
{
|
|
assert(i < nlanes_);
|
|
const size_type bit = i * lane_bits;
|
|
return lane_ref(data_.get() + (bit / 64u),
|
|
static_cast<unsigned>(bit % 64u));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator begin() noexcept { return iterator{data(), 0}; }
|
|
HEDLEY_NO_THROW
|
|
iterator end() noexcept { return iterator{data(), nlanes_}; }
|
|
HEDLEY_NO_THROW
|
|
iterator begin() const noexcept { return iterator{data_.get(), 0}; }
|
|
HEDLEY_NO_THROW
|
|
iterator end() const noexcept { return iterator{data_.get(), nlanes_}; }
|
|
HEDLEY_NO_THROW
|
|
iterator cbegin() const noexcept { return begin(); }
|
|
HEDLEY_NO_THROW
|
|
iterator cend() const noexcept { return end(); }
|
|
|
|
private:
|
|
size_type nlanes_ = 0;
|
|
size_type nwords_ = 0;
|
|
unique_ptr data_{};
|
|
};
|
|
|
|
/// @brief Packed lane storage whose iterators yield `subtractive_share<LaneT, Party>`.
|
|
/// @details The bytes are the leaf image (`store_leaf_bytes`); each lane is one share.
|
|
/// @tparam LaneT lane type
|
|
/// @tparam Party party index, `0` or `1`
|
|
template <typename LaneT, std::size_t Party>
|
|
class packed_share_output : public dynamic_packed_array<LaneT>
|
|
{
|
|
using lanes = dynamic_packed_array<LaneT>;
|
|
using share_type = subtractive_share<LaneT, Party>;
|
|
|
|
public:
|
|
using value_type = share_type;
|
|
using size_type = typename lanes::size_type;
|
|
using difference_type = typename lanes::difference_type;
|
|
|
|
class reference
|
|
{
|
|
public:
|
|
HEDLEY_NO_THROW
|
|
explicit reference(typename lanes::reference lane) noexcept : lane_{lane} {}
|
|
|
|
HEDLEY_NO_THROW
|
|
reference(const reference &) noexcept = default;
|
|
|
|
HEDLEY_NO_THROW
|
|
operator share_type() const noexcept
|
|
{
|
|
return share_type::from_raw(static_cast<LaneT>(lane_));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
reference & operator=(const share_type & share) noexcept
|
|
{
|
|
lane_ = share.raw();
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
reference & operator=(LaneT value) noexcept
|
|
{
|
|
lane_ = value;
|
|
return *this;
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
reference & operator=(const reference & other) noexcept
|
|
{
|
|
return (*this = static_cast<share_type>(other));
|
|
}
|
|
|
|
private:
|
|
typename lanes::reference lane_;
|
|
};
|
|
|
|
class iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
using value_type = share_type;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = void;
|
|
using reference = share_type;
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator() noexcept = default;
|
|
HEDLEY_NO_THROW
|
|
explicit iterator(typename lanes::iterator it) noexcept : it_{it} {}
|
|
|
|
HEDLEY_NO_THROW
|
|
share_type operator*() const noexcept
|
|
{
|
|
return share_type::from_raw(static_cast<LaneT>(*it_));
|
|
}
|
|
HEDLEY_NO_THROW
|
|
share_type operator[](difference_type n) const noexcept
|
|
{
|
|
return share_type::from_raw(static_cast<LaneT>(it_[n]));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator & operator++() noexcept { ++it_; return *this; }
|
|
HEDLEY_NO_THROW
|
|
iterator operator++(int) noexcept { iterator p = *this; ++*this; return p; }
|
|
HEDLEY_NO_THROW
|
|
iterator & operator--() noexcept { --it_; return *this; }
|
|
HEDLEY_NO_THROW
|
|
iterator operator--(int) noexcept { iterator p = *this; --*this; return p; }
|
|
HEDLEY_NO_THROW
|
|
iterator & operator+=(difference_type n) noexcept { it_ += n; return *this; }
|
|
HEDLEY_NO_THROW
|
|
iterator & operator-=(difference_type n) noexcept { it_ -= n; return *this; }
|
|
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator+(iterator it, difference_type n) noexcept
|
|
{
|
|
it += n;
|
|
return it;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator+(difference_type n, iterator it) noexcept
|
|
{
|
|
return it + n;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend iterator operator-(iterator it, difference_type n) noexcept
|
|
{
|
|
it -= n;
|
|
return it;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend difference_type operator-(iterator a, iterator b) noexcept
|
|
{
|
|
return a.it_ - b.it_;
|
|
}
|
|
HEDLEY_NO_THROW
|
|
friend bool operator==(iterator a, iterator b) noexcept { return a.it_ == b.it_; }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator!=(iterator a, iterator b) noexcept { return !(a == b); }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator<(iterator a, iterator b) noexcept { return a.it_ < b.it_; }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
|
|
HEDLEY_NO_THROW
|
|
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
|
|
|
|
private:
|
|
typename lanes::iterator it_{};
|
|
};
|
|
|
|
using const_iterator = iterator;
|
|
|
|
explicit packed_share_output(size_type nlanes) : lanes(nlanes) {}
|
|
|
|
packed_share_output(const packed_share_output &) = delete;
|
|
packed_share_output & operator=(const packed_share_output &) = delete;
|
|
HEDLEY_NO_THROW
|
|
packed_share_output(packed_share_output &&) noexcept = default;
|
|
HEDLEY_NO_THROW
|
|
packed_share_output & operator=(packed_share_output &&) noexcept = default;
|
|
~packed_share_output() = default;
|
|
|
|
using lanes::data;
|
|
using lanes::empty;
|
|
using lanes::size;
|
|
|
|
HEDLEY_NO_THROW
|
|
reference operator[](size_type i) noexcept
|
|
{
|
|
return reference{lanes::operator[](i)};
|
|
}
|
|
HEDLEY_NO_THROW
|
|
share_type operator[](size_type i) const noexcept
|
|
{
|
|
return share_type::from_raw(lanes::operator[](i));
|
|
}
|
|
|
|
HEDLEY_NO_THROW
|
|
iterator begin() noexcept { return iterator{lanes::begin()}; }
|
|
HEDLEY_NO_THROW
|
|
iterator end() noexcept { return iterator{lanes::end()}; }
|
|
HEDLEY_NO_THROW
|
|
iterator begin() const noexcept
|
|
{
|
|
return iterator{typename lanes::iterator{this->data(), 0}};
|
|
}
|
|
HEDLEY_NO_THROW
|
|
iterator end() const noexcept
|
|
{
|
|
return iterator{typename lanes::iterator{this->data(), this->size()}};
|
|
}
|
|
HEDLEY_NO_THROW
|
|
iterator cbegin() const noexcept { return begin(); }
|
|
HEDLEY_NO_THROW
|
|
iterator cend() const noexcept { return end(); }
|
|
};
|
|
|
|
} // namespace dpf
|
|
|
|
#endif // LIBDPF_INCLUDE_DPF_PACKED_ARRAY_HPP__
|