libdpf/include/dpf/json.hpp
Ryan Henry e4e666f459 Initial import of libdpf.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 14:08:32 -06:00

253 lines
8.4 KiB
C++

/// @file dpf/json.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_JSON_HPP__
#define LIBDPF_INCLUDE_DPF_JSON_HPP__
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <array>
#include <string>
#include <bitset>
#include <type_traits>
#include <utility>
#include "json/include/nlohmann/json.hpp"
#include "portable-snippets/exact-int/exact-int.h"
#include "dpf/dpf_key.hpp"
namespace nlohmann
{
template <typename NodeT,
typename OutputT>
struct adl_serializer<dpf::beaver<true, NodeT, OutputT>>
{
static void from_json(const nlohmann::json & j, dpf::beaver<true, NodeT, OutputT> & beaver) // NOLINT(runtime/references)
{
j.get_to(beaver.output_blind);
j.get_to(beaver.vector_blind);
j.get_to(beaver.blinded_vector);
}
static void to_json(nlohmann::json & j, const dpf::beaver<true, NodeT, OutputT> & beaver) // NOLINT(runtime/references)
{
j = nlohmann::json{
{"output_blind", beaver.output_blind},
{"vector_blind", beaver.vector_blind},
{"blinded_vector", beaver.blinded_vector}
};
}
};
template <>
struct adl_serializer<simde__m128i>
{
static void from_json(const nlohmann::json & j, simde__m128i & a) // NOLINT(runtime/references)
{
std::array<psnip_uint64_t, 2> A;
j.get_to(A);
a = simde_mm_set_epi64x(A[1], A[0]);
}
static void to_json(nlohmann::json & j, const simde__m128i & a) // NOLINT(runtime/references)
{
j = nlohmann::json{a[0], a[1]};
}
};
template <>
struct adl_serializer<simde__m256i>
{
static void from_json(const nlohmann::json & j, simde__m256i & a) // NOLINT(runtime/references)
{
std::array<psnip_uint64_t, 4> A;
j.get_to(A);
a = simde_mm256_set_epi64x(A[3], A[2], A[1], A[0]);
}
static void to_json(nlohmann::json & j, const simde__m256i & a) // NOLINT(runtime/references)
{
j = nlohmann::json{a[0], a[1], a[2], a[3]};
}
};
template <>
struct adl_serializer<dpf::detail::cmp_meta>
{
static void from_json(const nlohmann::json & j, dpf::detail::cmp_meta & c) // NOLINT(runtime/references)
{
j.at("nbits").get_to(c.nbits);
j.at("mask").get_to(c.mask);
c.kind = static_cast<dpf::cmp_kind>(j.at("kind").get<psnip_uint8_t>());
c.trivial =
static_cast<dpf::cmp_trivial>(j.at("trivial").get<psnip_uint8_t>());
j.at("eval_as_ge").get_to(c.eval_as_ge);
j.at("include_eq").get_to(c.include_eq);
j.at("active").get_to(c.active);
}
static void to_json(nlohmann::json & j, const dpf::detail::cmp_meta & c) // NOLINT(runtime/references)
{
j = nlohmann::json{
{"nbits", c.nbits},
{"mask", c.mask},
{"kind", static_cast<psnip_uint8_t>(c.kind)},
{"trivial", static_cast<psnip_uint8_t>(c.trivial)},
{"eval_as_ge", c.eval_as_ge},
{"include_eq", c.include_eq},
{"active", c.active}
};
}
};
// Classic single-level key (no `at<>` / no comparison channel).
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
std::enable_if_t<!dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT,
OutputTs...>::is_multilevel>>
{
using dpf_type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>;
using interior_node = typename dpf_type::interior_node;
using leaf_tuple = typename dpf_type::leaf_tuple;
using beaver_tuple = typename dpf_type::beaver_tuple;
static dpf_type from_json(const nlohmann::json & j)
{
interior_node root;
j.at("root").get_to(root);
std::array<interior_node, dpf_type::depth> correction_words;
j.at("correction_words").get_to(correction_words);
std::array<psnip_uint8_t, dpf_type::depth> correction_advice;
j.at("correction_advice").get_to(correction_advice);
leaf_tuple leaves;
j.at("leaves").get_to(leaves);
std::string wildcard_mask_str;
j.at("wildcards").get_to(wildcard_mask_str);
beaver_tuple beavers;
j.at("beavers").get_to(beavers);
return dpf_type{
root,
correction_words,
correction_advice,
leaves,
std::bitset<std::tuple_size_v<leaf_tuple>>(wildcard_mask_str),
beavers
};
}
static void to_json(nlohmann::json & j, const dpf_type & dpf) // NOLINT(runtime/references)
{
j = nlohmann::json{
{"root", dpf.root()},
{"correction_words", dpf.correction_words()},
{"correction_advice", dpf.correction_advice()},
{"leaves", dpf.mutable_leaf_tuple()},
{"wildcards", dpf.mutable_wildcard_mask()},
{"beavers", dpf.mutable_beaver_tuple()}
};
}
};
// Multi-level / comparison key (`at<>` and/or a `cmp` channel). Round-trips
// the public tree (root, CWs, advice) and the comparison channel (cmp meta,
// value CWs, `cw_last`, and this party's `cmp_addend` share). Leaf outputs are
// not yet serialized here, so this path currently supports comparison-only
// keys (`num_outputs == 0`, e.g. `make_dpf(x, dpf::lt(...))`).
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename ...OutputTs>
struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
std::enable_if_t<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT,
OutputTs...>::is_multilevel>>
{
using dpf_type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>;
using interior_node = typename dpf_type::interior_node;
using input_type = typename dpf_type::input_type;
static dpf_type from_json(const nlohmann::json & j)
{
static_assert(dpf_type::num_outputs == 0,
"dpf::json round-trip currently supports comparison-only "
"multi-level keys (no leaf outputs)");
interior_node root;
j.at("root").get_to(root);
typename dpf_type::correction_words_array correction_words;
j.at("correction_words").get_to(correction_words);
typename dpf_type::correction_advice_array correction_advice;
j.at("correction_advice").get_to(correction_advice);
dpf::detail::cmp_meta cmp;
j.at("cmp").get_to(cmp);
typename dpf_type::value_cw_array value_cws;
j.at("value_cw").get_to(value_cws);
uint64_t cw_last = j.at("cw_last").template get<uint64_t>();
uint64_t cmp_addend = j.at("cmp_addend").template get<uint64_t>();
typename dpf_type::leaf_wrapper_tuple leaves{};
input_type offset_share{};
typename dpf_type::addend_tuple addends{};
return dpf_type{root, correction_words, correction_advice,
std::move(leaves), offset_share, cmp, value_cws, cw_last,
cmp_addend, addends};
}
static void to_json(nlohmann::json & j, const dpf_type & dpf) // NOLINT(runtime/references)
{
static_assert(dpf_type::num_outputs == 0,
"dpf::json round-trip currently supports comparison-only "
"multi-level keys (no leaf outputs)");
j = nlohmann::json{
{"root", dpf.root()},
{"correction_words", dpf.correction_words()},
{"correction_advice", dpf.correction_advice()},
{"cmp", dpf.cmp()},
{"value_cw", dpf.value_cw()},
{"cw_last", static_cast<uint64_t>(dpf.cw_last())},
{"cmp_addend", static_cast<uint64_t>(dpf.cmp_addend())}
};
}
};
} // namespace nlohmann
namespace dpf
{
namespace json
{
template <typename DpfKey>
static std::string to_json(const DpfKey & dpf)
{
nlohmann::json json = dpf;
return json.dump();
}
template <typename DpfType>
static auto from_json(const std::string & json_string)
{
nlohmann::json json = nlohmann::json::parse(json_string);
return static_cast<DpfType>(json);
}
} // namespace json
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_JSON_HPP__