Annotate noexcept and constexpr with HEDLEY, and add interval containment, ChaCha, and the dyadic range tables.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-24 20:44:07 -06:00
parent 875f09fec1
commit 0d8a5a8131
97 changed files with 9212 additions and 1159 deletions

View file

@ -0,0 +1,245 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include "dpf.hpp"
#include "simde/simde/x86/avx2.h"
namespace
{
bool blocks_equal(simde__m128i a, simde__m128i b)
{
return simde_mm_movemask_epi8(simde_mm_cmpeq_epi8(a, b)) == 0xFFFF;
}
simde__m128i block_from_lanes(std::uint64_t lo, std::uint64_t hi)
{
simde__m128i x;
std::uint64_t lane[2] = {lo, hi};
std::memcpy(&x, lane, sizeof(x));
return x;
}
simde__m128i block_from_bytes(const std::uint8_t * p)
{
simde__m128i x;
std::memcpy(&x, p, sizeof(x));
return x;
}
// RFC 8439 §2.3.2. Counter = 1, nonce = 00:00:00:09:00:00:00:4a:00:00:00:00.
constexpr std::uint8_t k_rfc_keystream[64] = {
0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e
};
} // namespace
TEST(ChachaPrg, Rfc8439Block)
{
const std::uint32_t key[8] = {
0x03020100u, 0x07060504u, 0x0b0a0908u, 0x0f0e0d0cu,
0x13121110u, 0x17161514u, 0x1b1a1918u, 0x1f1e1d1cu
};
const std::uint32_t nonce[3] = {0x09000000u, 0x4a000000u, 0x00000000u};
std::uint8_t out[64];
dpf::prg::chacha_detail::block<20>(key, 1, nonce, out);
EXPECT_EQ(0, std::memcmp(out, k_rfc_keystream, sizeof(out)));
std::uint8_t fewer[64];
dpf::prg::chacha_detail::block<8>(key, 1, nonce, fewer);
EXPECT_NE(0, std::memcmp(fewer, k_rfc_keystream, sizeof(fewer)));
}
TEST(ChachaPrg, WideBlockMatchesScalar)
{
std::uint32_t keys[4][8];
std::uint32_t counters[4] = {0u, 1u, 5u, 0x00fffff0u};
for (int lane = 0; lane < 4; ++lane)
{
for (int w = 0; w < 8; ++w)
{
keys[lane][w] = 0x9e3779b9u * static_cast<std::uint32_t>(lane + 1)
+ static_cast<std::uint32_t>(w) * 0x01000193u;
}
}
std::uint8_t wide[4][64];
dpf::prg::chacha_detail::block4<20>(keys, counters, wide);
for (int lane = 0; lane < 4; ++lane)
{
std::uint8_t scalar[64];
dpf::prg::chacha_detail::block<20>(keys[lane], counters[lane],
dpf::prg::chacha_detail::zero_nonce, scalar);
EXPECT_EQ(0, std::memcmp(wide[lane], scalar, 64)) << "lane=" << lane;
}
}
TEST(ChachaPrg, EvalIsKeystreamChunk)
{
using prg = dpf::prg::chacha20;
simde__m128i seed = block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull);
std::uint32_t key[8];
dpf::prg::chacha_detail::seed_key(seed, key);
for (psnip_uint32_t pos = 0; pos < 8; ++pos)
{
std::uint8_t buf[64];
dpf::prg::chacha_detail::block<20>(key, pos >> 2,
dpf::prg::chacha_detail::zero_nonce, buf);
EXPECT_TRUE(blocks_equal(prg::eval(seed, pos),
block_from_bytes(buf + 16 * (pos & 3u)))) << "pos=" << pos;
}
auto kids = prg::eval01(seed);
EXPECT_TRUE(blocks_equal(kids[0], prg::eval(seed, 0)));
EXPECT_TRUE(blocks_equal(kids[1], prg::eval(seed, 1)));
EXPECT_FALSE(blocks_equal(kids[0], kids[1]));
EXPECT_FALSE(blocks_equal(kids[0], seed));
EXPECT_FALSE(blocks_equal(dpf::prg::chacha12::eval(seed, 0), kids[0]));
EXPECT_FALSE(blocks_equal(dpf::prg::chacha8::eval(seed, 0), kids[0]));
}
TEST(ChachaPrg, BulkAndWideAgree)
{
using prg = dpf::prg::chacha20;
simde__m128i seed = block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull);
auto check_bulk = [&](psnip_uint32_t pos, psnip_uint32_t count)
{
alignas(16) simde__m128i bulk[32];
prg::eval(seed, bulk, count, pos);
for (psnip_uint32_t i = 0; i < count; ++i)
{
EXPECT_TRUE(blocks_equal(bulk[i], prg::eval(seed, pos + i)))
<< "pos=" << pos << " i=" << i;
}
};
check_bulk(0, 1);
check_bulk(0, 2);
check_bulk(0, 4);
check_bulk(0, 16);
check_bulk(1, 20);
check_bulk(3, 6);
check_bulk(4, 7);
check_bulk(0xfffffffeu, 2);
alignas(16) simde__m128i seeds[8];
alignas(16) simde__m128i out4[4];
alignas(16) simde__m128i out8[8];
alignas(16) simde__m128i left[4];
alignas(16) simde__m128i right[4];
for (int i = 0; i < 8; ++i)
{
seeds[i] = block_from_lanes(0x1000u + static_cast<unsigned>(i), 0x2000u);
}
prg::eval_x4(seeds, out4, 5);
prg::eval_x8(seeds, out8, 0);
prg::eval01_x4(seeds, left, right);
for (int i = 0; i < 4; ++i)
{
EXPECT_TRUE(blocks_equal(out4[i], prg::eval(seeds[i], 5)));
EXPECT_TRUE(blocks_equal(left[i], prg::eval(seeds[i], 0)));
EXPECT_TRUE(blocks_equal(right[i], prg::eval(seeds[i], 1)));
}
for (int i = 0; i < 8; ++i)
{
EXPECT_TRUE(blocks_equal(out8[i], prg::eval(seeds[i], 0)));
}
alignas(16) simde__m128i one[1];
EXPECT_NO_THROW(prg::eval(seed, one, 1, 0xffffffffu));
EXPECT_TRUE(blocks_equal(one[0], prg::eval(seed, 0xffffffffu)));
EXPECT_THROW(prg::eval(seed, out4, 2, 0xffffffffu), std::invalid_argument);
prg::eval(seed, out4, 0, 0xffffffffu);
}
TEST(ChachaPrg, DpfPointAndFull)
{
using prg = dpf::prg::chacha20;
const std::uint8_t x = 0x2a;
const std::uint32_t y = 0x01020304;
auto [k0, k1] = dpf::make_dpf<prg>(x, y);
for (int i = 0; i < 256; ++i)
{
auto y0 = *dpf::eval_point(k0, static_cast<std::uint8_t>(i));
auto y1 = *dpf::eval_point(k1, static_cast<std::uint8_t>(i));
auto sum = dpf::reconstruct(y0, y1);
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
}
auto [buf0, iter0] = dpf::eval_full(k0);
auto [buf1, iter1] = dpf::eval_full(k1);
(void)buf0;
(void)buf1;
std::size_t i = 0;
auto it0 = std::begin(iter0);
auto it1 = std::begin(iter1);
for (; it0 != std::end(iter0); ++it0, ++it1, ++i)
{
auto sum = dpf::reconstruct(*it0, *it1);
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
}
EXPECT_EQ(i, std::size_t{256});
}
TEST(ChachaPrg, ReducedRoundsStillCorrect)
{
using prg = dpf::prg::chacha8;
const std::uint8_t x = 0x11;
const std::uint32_t y = 0xabcdu;
auto [k0, k1] = dpf::make_dpf<prg, dpf::prg::chacha12>(x, y);
for (int i = 0; i < 256; ++i)
{
auto sum = dpf::reconstruct(
*dpf::eval_point(k0, static_cast<std::uint8_t>(i)),
*dpf::eval_point(k1, static_cast<std::uint8_t>(i)));
EXPECT_EQ(sum, static_cast<std::uint8_t>(i) == x ? y : 0u) << "i=" << i;
}
}
TEST(ChachaPrg, CounterWrapperCountsEval01)
{
using prg = dpf::prg::counter_wrapper<dpf::prg::chacha20>;
const auto before = prg::count();
simde__m128i seed = block_from_lanes(0x1111ull, 0x2222ull);
auto kids = prg::eval01(seed);
EXPECT_FALSE(blocks_equal(kids[0], kids[1]));
EXPECT_EQ(prg::count(), before + 2u);
alignas(16) simde__m128i bulk[4];
prg::eval(seed, bulk, 4, 0);
EXPECT_EQ(prg::count(), before + 2u + 4u);
}
TEST(ChachaPrg, ExpandAndBufferedReplay)
{
using prg = dpf::prg::chacha20;
simde__m128i seed = block_from_lanes(0x1111ull, 0x2222ull);
auto t0 = prg::expand<std::uint32_t, 0>(seed, 3);
auto t1 = prg::expand<std::uint32_t, 1>(seed, 3);
EXPECT_EQ(dpf::reconstruct(t0, t1), 0u);
auto u0 = prg::expand<std::uint64_t, 0>(seed, 0);
auto u1 = prg::expand<std::uint64_t, 1>(seed, 1);
EXPECT_NE(u0.raw(), u1.raw());
dpf::randomness::buffered_prg<prg, std::uint64_t, std::uint64_t> streamed(seed, 8);
auto s0 = streamed.get<0>();
auto s1 = streamed.get<1>();
dpf::randomness::buffered_prg<prg, std::uint64_t, std::uint64_t> replay(seed, 8);
EXPECT_EQ(replay.at<0>(0), s0);
EXPECT_EQ(replay.at<1>(0), s1);
EXPECT_EQ(replay.get<0>(), s0);
EXPECT_NE(s0, streamed.get<0>());
}