From 70ef92367da8ccbc6b4b3c13e2e92ecdd9a47dfe Mon Sep 17 00:00:00 2001 From: Marcin Kowalczyk Date: Tue, 5 Nov 2024 14:27:30 +0100 Subject: [PATCH] Support digest conversions also from arrays of integers of smaller sizes to integers or arrays of integers of larger sizes. To prevent infinite recursion, in these cases further conversions using `DigestConverterImpl` are not considered, only types which can be natively explicitly converted. This is useful to express the result of `HighwayHash128Digester` as `absl::uint128` instead of `std::array`, or to express the result of OpenSSL-based digesters as `std::array` instead of `std::array`. Also, ensure that if a digest is already natively convertible to the target type, then `DigestConverterImpl` does not consider any other conversion first, which would be ambiguous given that the native conversion is available too. PiperOrigin-RevId: 693311655 --- riegeli/digests/BUILD | 1 + riegeli/digests/digest_converter.h | 109 +++++++++++++++++++++++++---- 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/riegeli/digests/BUILD b/riegeli/digests/BUILD index d938e54c..de07adbb 100644 --- a/riegeli/digests/BUILD +++ b/riegeli/digests/BUILD @@ -9,6 +9,7 @@ cc_library( name = "digest_converter", hdrs = ["digest_converter.h"], deps = [ + "//riegeli/endian:endian_reading", "//riegeli/endian:endian_writing", "@com_google_absl//absl/meta:type_traits", "@com_google_absl//absl/numeric:int128", diff --git a/riegeli/digests/digest_converter.h b/riegeli/digests/digest_converter.h index 6869da8e..2443e9ba 100644 --- a/riegeli/digests/digest_converter.h +++ b/riegeli/digests/digest_converter.h @@ -25,6 +25,7 @@ #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" +#include "riegeli/endian/endian_reading.h" #include "riegeli/endian/endian_writing.h" namespace riegeli { @@ -48,6 +49,17 @@ struct HasDigestConverterImpl< absl::void_t::Convert( std::declval()))>> : std::true_type {}; +// `DigestConverterImpl` tries to transform `From` from types with +// more information to types with less information, or from integers or arrays +// of integers of larger sizes to arrays of integers of smaller sizes, and then +// considers further transformations, until reaching a type which can be +// natively explicitly converted to `To`. +// +// To prevent infinite recursion, in conversions from arrays of integers of +// smaller sizes to integers or arrays of integers of larger sizes, further +// conversions using `DigestConverterImpl` are not considered, only types which +// can be natively explicitly converted. + // A digest can be converted if its type can be natively explicitly converted. // This includes the case when `To` is the same as `From`. template @@ -63,7 +75,7 @@ struct DigestConverterImpl< std::enable_if_t::value, int> = 0> static To Convert(From&& digest) { - return To(std::move(digest)); + return To(std::forward(digest)); } }; @@ -71,7 +83,9 @@ struct DigestConverterImpl< template struct DigestConverterImpl< std::array, To, - std::enable_if_t::value>> { + std::enable_if_t>>, + HasDigestConverterImpl>::value>> { static To Convert(const std::array& digest) { return DigestConverterImpl::Convert( std::string(digest.data(), digest.size())); @@ -79,13 +93,15 @@ struct DigestConverterImpl< }; // `uint32_t`, `uint64_t`, and `absl::uint128` can be converted to -// `std::array`. +// `std::array`, using Big Endian. template struct DigestConverterImpl< uint32_t, To, - std::enable_if_t, - To>::value>> { + std::enable_if_t>, + HasDigestConverterImpl, + To>>::value>> { static To Convert(uint32_t digest) { std::array result; riegeli::WriteBigEndian32(digest, result.data()); @@ -97,8 +113,10 @@ struct DigestConverterImpl< template struct DigestConverterImpl< uint64_t, To, - std::enable_if_t, - To>::value>> { + std::enable_if_t>, + HasDigestConverterImpl, + To>>::value>> { static To Convert(uint64_t digest) { std::array result; riegeli::WriteBigEndian64(digest, result.data()); @@ -110,8 +128,10 @@ struct DigestConverterImpl< template struct DigestConverterImpl< absl::uint128, To, - std::enable_if_t, To>::value>> { + std::enable_if_t>, + HasDigestConverterImpl, + To>>::value>> { static To Convert(absl::uint128 digest) { std::array result; riegeli::WriteBigEndian128(digest, result.data()); @@ -120,13 +140,56 @@ struct DigestConverterImpl< } }; +// `std::array` can be converted to `uint32_t`, `uint64_t`, +// and `absl::uint128`, using Big Endian. +// +// To prevent infinite recursion, further conversions using +// `DigestConverterImpl` are not considered, only types which can be natively +// explicitly converted. + +template +struct DigestConverterImpl, To, + std::enable_if_t>>, + std::is_constructible>::value>> { + static To Convert(std::array digest) { + return To(riegeli::ReadBigEndian32(digest.data())); + } +}; + +template +struct DigestConverterImpl, To, + std::enable_if_t>>, + std::is_constructible>::value>> { + static To Convert(std::array digest) { + return To(riegeli::ReadBigEndian64(digest.data())); + } +}; + +template +struct DigestConverterImpl< + std::array, To, + std::enable_if_t< + absl::conjunction>>, + std::is_constructible>::value>> { + static To Convert(std::array digest) { + return To(riegeli::ReadBigEndian128(digest.data())); + } +}; + // `std::array` can be converted to -// `std::array`. +// `std::array`, using Big Endian. template struct DigestConverterImpl< std::array, To, - std::enable_if_t, To>::value>> { + std::enable_if_t>>, + HasDigestConverterImpl, + To>>::value>> { static To Convert(const std::array& digest) { std::array result; riegeli::WriteBigEndian64s(absl::MakeConstSpan(digest.data(), size), @@ -136,6 +199,28 @@ struct DigestConverterImpl< } }; +// `std::array` can be converted to +// `std::array`, using Big Endian. +// +// To prevent infinite recursion, further conversions using +// `DigestConverterImpl` are not considered, only types which can be natively +// explicitly converted. +template +struct DigestConverterImpl< + std::array, To, + std::enable_if_t, + absl::negation>>, + std::is_constructible< + To, std::array>>::value>> { + static To Convert(const std::array& digest) { + std::array result; + riegeli::ReadBigEndian64s( + digest.data(), absl::MakeSpan(result.data(), size / sizeof(uint64_t))); + return To(result); + } +}; + // `DigestConverter` extends `DigestConverterImpl` with the // case of `To` being a reference.