Skip to content

Commit

Permalink
Support digest conversions also from arrays of integers of smaller si…
Browse files Browse the repository at this point in the history
…zes 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<uint64_t, 2>`, or to express the result
of OpenSSL-based digesters as `std::array<uint64_t, size>` instead of
`std::array<char, size * sizeof(uint64_t)>`.

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
  • Loading branch information
QrczakMK committed Nov 5, 2024
1 parent 837942c commit 70ef923
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 12 deletions.
1 change: 1 addition & 0 deletions riegeli/digests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
109 changes: 97 additions & 12 deletions riegeli/digests/digest_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -48,6 +49,17 @@ struct HasDigestConverterImpl<
absl::void_t<decltype(DigestConverterImpl<From, To>::Convert(
std::declval<From>()))>> : std::true_type {};

// `DigestConverterImpl<From, To>` 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 <typename From, typename To>
Expand All @@ -63,29 +75,33 @@ struct DigestConverterImpl<
std::enable_if_t<!std::is_lvalue_reference<DependentFrom>::value,
int> = 0>
static To Convert(From&& digest) {
return To(std::move(digest));
return To(std::forward<From>(digest));
}
};

// `std::array<char, size>` can be converted to `std::string`.
template <size_t size, typename To>
struct DigestConverterImpl<
std::array<char, size>, To,
std::enable_if_t<HasDigestConverterImpl<std::string, To>::value>> {
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<To, std::array<char, size>>>,
HasDigestConverterImpl<std::string, To>>::value>> {
static To Convert(const std::array<char, size>& digest) {
return DigestConverterImpl<std::string, To>::Convert(
std::string(digest.data(), digest.size()));
}
};

// `uint32_t`, `uint64_t`, and `absl::uint128` can be converted to
// `std::array<char, sizeof(T)>`.
// `std::array<char, sizeof(T)>`, using Big Endian.

template <typename To>
struct DigestConverterImpl<
uint32_t, To,
std::enable_if_t<HasDigestConverterImpl<std::array<char, sizeof(uint32_t)>,
To>::value>> {
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<To, uint32_t>>,
HasDigestConverterImpl<std::array<char, sizeof(uint32_t)>,
To>>::value>> {
static To Convert(uint32_t digest) {
std::array<char, sizeof(uint32_t)> result;
riegeli::WriteBigEndian32(digest, result.data());
Expand All @@ -97,8 +113,10 @@ struct DigestConverterImpl<
template <typename To>
struct DigestConverterImpl<
uint64_t, To,
std::enable_if_t<HasDigestConverterImpl<std::array<char, sizeof(uint64_t)>,
To>::value>> {
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<To, uint64_t>>,
HasDigestConverterImpl<std::array<char, sizeof(uint64_t)>,
To>>::value>> {
static To Convert(uint64_t digest) {
std::array<char, sizeof(uint64_t)> result;
riegeli::WriteBigEndian64(digest, result.data());
Expand All @@ -110,8 +128,10 @@ struct DigestConverterImpl<
template <typename To>
struct DigestConverterImpl<
absl::uint128, To,
std::enable_if_t<HasDigestConverterImpl<
std::array<char, sizeof(absl::uint128)>, To>::value>> {
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<To, absl::uint128>>,
HasDigestConverterImpl<std::array<char, sizeof(absl::uint128)>,
To>>::value>> {
static To Convert(absl::uint128 digest) {
std::array<char, sizeof(absl::uint128)> result;
riegeli::WriteBigEndian128(digest, result.data());
Expand All @@ -120,13 +140,56 @@ struct DigestConverterImpl<
}
};

// `std::array<char, sizeof(T)>` 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 <typename To>
struct DigestConverterImpl<std::array<char, sizeof(uint32_t)>, To,
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<
To, std::array<char, sizeof(uint32_t)>>>,
std::is_constructible<To, uint32_t>>::value>> {
static To Convert(std::array<char, sizeof(uint32_t)> digest) {
return To(riegeli::ReadBigEndian32(digest.data()));
}
};

template <typename To>
struct DigestConverterImpl<std::array<char, sizeof(uint64_t)>, To,
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<
To, std::array<char, sizeof(uint64_t)>>>,
std::is_constructible<To, uint64_t>>::value>> {
static To Convert(std::array<char, sizeof(uint64_t)> digest) {
return To(riegeli::ReadBigEndian64(digest.data()));
}
};

template <typename To>
struct DigestConverterImpl<
std::array<char, sizeof(absl::uint128)>, To,
std::enable_if_t<
absl::conjunction<absl::negation<std::is_constructible<
To, std::array<char, sizeof(absl::uint128)>>>,
std::is_constructible<To, absl::uint128>>::value>> {
static To Convert(std::array<char, sizeof(absl::uint128)> digest) {
return To(riegeli::ReadBigEndian128(digest.data()));
}
};

// `std::array<uint64_t, size>` can be converted to
// `std::array<char, size * sizeof(uint64_t)>`.
// `std::array<char, size * sizeof(uint64_t)>`, using Big Endian.
template <size_t size, typename To>
struct DigestConverterImpl<
std::array<uint64_t, size>, To,
std::enable_if_t<HasDigestConverterImpl<
std::array<char, size * sizeof(uint64_t)>, To>::value>> {
std::enable_if_t<absl::conjunction<
absl::negation<std::is_constructible<To, std::array<uint64_t, size>>>,
HasDigestConverterImpl<std::array<char, size * sizeof(uint64_t)>,
To>>::value>> {
static To Convert(const std::array<uint64_t, size>& digest) {
std::array<char, size * sizeof(uint64_t)> result;
riegeli::WriteBigEndian64s(absl::MakeConstSpan(digest.data(), size),
Expand All @@ -136,6 +199,28 @@ struct DigestConverterImpl<
}
};

// `std::array<char, size * sizeof(uint64_t)>` can be converted to
// `std::array<uint64_t, size>`, using Big Endian.
//
// To prevent infinite recursion, further conversions using
// `DigestConverterImpl` are not considered, only types which can be natively
// explicitly converted.
template <size_t size, typename To>
struct DigestConverterImpl<
std::array<char, size>, To,
std::enable_if_t<absl::conjunction<
std::integral_constant<bool, size % sizeof(uint64_t) == 0>,
absl::negation<std::is_constructible<To, std::array<char, size>>>,
std::is_constructible<
To, std::array<uint64_t, size / sizeof(uint64_t)>>>::value>> {
static To Convert(const std::array<char, size>& digest) {
std::array<uint64_t, size / sizeof(uint64_t)> result;
riegeli::ReadBigEndian64s(
digest.data(), absl::MakeSpan(result.data(), size / sizeof(uint64_t)));
return To(result);
}
};

// `DigestConverter<From, To>` extends `DigestConverterImpl<From, To>` with the
// case of `To` being a reference.

Expand Down

0 comments on commit 70ef923

Please sign in to comment.