Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

To proto #291

Merged
merged 7 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 274 additions & 0 deletions iguana/pb_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,247 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) {
encode_numeric_field<key, omit_default_val>(t, it);
}
}

#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T>
IGUANA_INLINE constexpr std::string_view get_type_string() {
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<T, bool>) {
return "bool";
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_unsigned_v<T>) {
return "uint32";
}
else {
return "int32";
}
}
else {
if constexpr (std::is_unsigned_v<T>) {
return "uint64";
}
else {
return "int64";
}
}
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
return "string";
}
else if constexpr (std::is_floating_point_v<T>) {
return type_string<T>();
}
else {
constexpr auto str_type_name = type_string<T>();
constexpr size_t pos = str_type_name.rfind("::");
if constexpr (pos != std::string_view::npos) {
constexpr size_t pos = str_type_name.rfind("::") + 2;
if constexpr (detail::is_signed_varint_v<T> || detail::is_fixed_v<T>) {
return str_type_name.substr(pos, str_type_name.size() - pos - 2);
}
else {
return str_type_name.substr(pos);
}
}
else {
return str_type_name;
}
}
}

template <typename T, typename Stream>
IGUANA_INLINE void numeric_to_proto(Stream& out, std::string_view field_name,
uint32_t field_no) {
constexpr auto name = get_type_string<T>();
out.append(name).append(" ");
out.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}

template <size_t space_count = 2, typename Stream>
IGUANA_INLINE void build_proto_field(Stream& out, std::string_view str_type,
std::string_view field_name,
uint32_t field_no) {
for (size_t i = 0; i < space_count; i++) {
out.append(" ");
}

if (!str_type.empty()) {
out.append(str_type);
}

out.append(" ")
.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}

template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str);

template <typename Type, typename Stream>
IGUANA_INLINE void to_proto_impl(
Stream& out, std::unordered_map<std::string_view, std::string>& map,
std::string_view field_name = "", uint32_t field_no = 0) {
std::string sub_str;
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
constexpr auto name = get_name<T>();
out.append("message ").append(name).append(" {\n");
static constexpr auto tuple = get_members_tuple<T>();
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;

for_each_n(
[&out, &sub_str, &map](auto i) mutable {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
constexpr auto value = std::get<decltype(i)::value>(tuple);

using U = typename field_type::value_type;
if constexpr (is_reflection_v<U>) {
constexpr auto str_type = get_type_string<U>();
build_proto_field(
out, str_type,
{value.field_name.data(), value.field_name.size()},
value.field_no);

build_sub_proto<U>(map, str_type, sub_str);
}
else if constexpr (variant_v<U>) {
constexpr size_t var_size = std::variant_size_v<U>;
using sub_type = typename field_type::sub_type;

constexpr auto offset =
get_variant_index<U, sub_type, var_size - 1>();

if (offset == 0) {
out.append(" oneof ");
out.append(value.field_name.data(), value.field_name.size())
.append(" {\n");
}

constexpr auto str_type = get_type_string<sub_type>();
std::string field_name = " one_of_";
field_name.append(str_type);

out.append(" ");
build_proto_field(out, str_type, field_name, value.field_no);

if constexpr (is_reflection_v<sub_type>) {
build_sub_proto<sub_type>(map, str_type, sub_str);
}

if (offset == var_size - 1) {
out.append(" }\n");
}
}
else {
to_proto_impl<U>(out, map,
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
},
std::make_index_sequence<SIZE>{});
out.append("}\r\n\r\n");
}
else if constexpr (is_sequence_container<T>::value) {
out.append(" repeated");
using item_type = typename T::value_type;

if constexpr (is_lenprefix_v<item_type>) {
// non-packed
if constexpr (is_reflection_v<item_type>) {
constexpr auto str_type = get_type_string<item_type>();
build_proto_field(out, str_type, field_name, field_no);

build_sub_proto<item_type>(map, str_type, sub_str);
}
else {
to_proto_impl<item_type>(out, map, field_name, field_no);
}
}
else {
out.append(" ");
numeric_to_proto<item_type>(out, field_name, field_no);
}
}
else if constexpr (is_map_container<T>::value) {
out.append(" map<");
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;

constexpr auto str_first = get_type_string<first_type>();
constexpr auto str_second = get_type_string<second_type>();
out.append(str_first).append(", ").append(str_second).append(">");

build_proto_field<1>(out, "", field_name, field_no);

if constexpr (is_reflection_v<second_type>) {
constexpr auto str_type = get_type_string<second_type>();
build_sub_proto<second_type>(map, str_type, sub_str);
}
}
else if constexpr (optional_v<T>) {
to_proto_impl<typename T::value_type>(
out, map, {field_name.data(), field_name.size()}, field_no);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
build_proto_field(out, "string ", field_name, field_no);
}
else if constexpr (enum_v<T>) {
constexpr auto str_type = get_type_string<T>();
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
build_proto_field(out, "int32", field_name, field_no);
}
else {
static_assert(enum_to_str.size() > 0, "empty enum not allowed");
static_assert((int)(enum_to_str.begin()->first) == 0,
"the first enum value must be zero in proto3");
build_proto_field(out, str_type, field_name, field_no);
if (map.find(str_type) == map.end()) {
sub_str.append("enum ").append(str_type).append(" {\n");
for (auto& [k, field_name] : enum_to_str) {
std::string_view name{field_name.data(), field_name.size()};
size_t pos = name.rfind("::");
if (pos != std::string_view::npos) {
name = name.substr(pos + 2);
}
sub_str.append(" ")
.append(name)
.append(" = ")
.append(std::to_string(static_cast<std::underlying_type_t<T>>(k)))
.append(";\n");
}
sub_str.append("}\r\n\r\n");
map.emplace(str_type, std::move(sub_str));
}
}
}
else {
out.append(" ");
numeric_to_proto<Type>(out, field_name, field_no);
}
}

template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str) {
if (map.find(str_type) == map.end()) {
to_proto_impl<T>(sub_str, map);
map.emplace(str_type, std::move(sub_str));
}
}
#endif
} // namespace detail

template <typename T, typename Stream>
Expand All @@ -216,6 +457,39 @@ IGUANA_INLINE void to_pb(T& t, Stream& out) {
detail::to_pb_impl<0>(t, &out[0], sz_ptr);
}

#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto(Stream& out, std::string_view ns = "") {
if (gen_header) {
constexpr std::string_view crlf = "\r\n\r\n";
out.append(R"(syntax = "proto3";)").append(crlf);
if (!ns.empty()) {
out.append("package ").append(ns).append(";").append(crlf);
}

out.append(R"(option optimize_for = SPEED;)").append(crlf);
out.append(R"(option cc_enable_arenas = true;)").append(crlf);
}

std::unordered_map<std::string_view, std::string> map;
detail::to_proto_impl<T>(out, map);
for (auto& [k, s] : map) {
out.append(s);
}
}

template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto_file(Stream& stream, std::string_view ns = "") {
if (!stream.is_open()) {
return;
}
std::string out;
to_proto<T, gen_header>(out, ns);
stream.write(out.data(), out.size());
}
#endif

template <typename T, typename Stream>
IGUANA_INLINE void to_pb_adl(iguana_adl_t* p, T& t, Stream& out) {
to_pb(t, out);
Expand Down
2 changes: 1 addition & 1 deletion iguana/reflection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ constexpr inline auto build_fields(T t, S &s, uint32_t &index) {
return build_variant_fields(t, s, I + 1, std::make_index_sequence<Size>{});
}
else {
uint32_t field_no = (I == index) ? (I + 1) : (I + index);
uint32_t field_no = (I == index) ? (I + 1) : (2 + index);
index++;
return std::tuple(field_t{t, field_no, s});
}
Expand Down
13 changes: 13 additions & 0 deletions test/proto/unittest_proto3.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,19 @@ REFLECTION(bench_int32, a, b, c, d);

} // namespace stpb

namespace iguana {
template <>
struct enum_value<stpb::Color> {
constexpr static std::array<int, 3> value = {0, 2, 4};
};

template <>
struct enum_value<stpb::Enum> {
constexpr static std::array<int, 5> value = {0, 1, 2, 123456, -1};
};

} // namespace iguana

inline auto create_person() {
stpb::person p{432798, std::string(1024, 'A'), 24, 65536.42};
return p;
Expand Down
Loading
Loading