From b49bccc534a40e08d8186edf28f9adb429df2a51 Mon Sep 17 00:00:00 2001 From: Marcin Kowalczyk Date: Fri, 28 Jun 2024 15:03:35 +0200 Subject: [PATCH] Add `DigesterHandle::SetWriteSizeHint()`, using `SetWriteSizeHint()` of the `Digester` if present. Let `DigestingWriter` and `DigestFrom()` call `SetWriteSizeHint()`. PiperOrigin-RevId: 647652531 --- riegeli/digests/BUILD | 2 ++ riegeli/digests/digester_handle.h | 55 +++++++++++++++++++++++++++-- riegeli/digests/digesting_writer.h | 35 ++++++++++++++++++ riegeli/digests/wrapping_digester.h | 5 +++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/riegeli/digests/BUILD b/riegeli/digests/BUILD index 83c0e5de..a90d1bf1 100644 --- a/riegeli/digests/BUILD +++ b/riegeli/digests/BUILD @@ -52,6 +52,7 @@ cc_library( "@com_google_absl//absl/meta:type_traits", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/types:optional", ], ) @@ -110,6 +111,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:cord", "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/utility", ], ) diff --git a/riegeli/digests/digester_handle.h b/riegeli/digests/digester_handle.h index 1518b28d..68cfd1b1 100644 --- a/riegeli/digests/digester_handle.h +++ b/riegeli/digests/digester_handle.h @@ -28,6 +28,7 @@ #include "absl/status/status.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "absl/types/span.h" #include "riegeli/base/any.h" #include "riegeli/base/chain.h" @@ -62,6 +63,16 @@ struct IsValidDigesterBaseTarget< // // All of the following methods returning `bool` return `true` on success. // // They may also return `void` which is treated as `true`. // +// // If `write_size_hint` is not `absl::nullopt`, hints that this amount of +// // data will be written from the current position. This may improve +// // performance and memory usage. +// // +// // If the hint turns out to not match reality, nothing breaks. It is better +// // if `write_size_hint` is slightly too large than slightly too small. +// // +// // Optional. If absent, does nothing. +// void SetWriteSizeHint(absl::optional write_size_hint); +// // // Called with consecutive fragments of data. // // // // Precondition: the digester is open. @@ -132,6 +143,16 @@ class DigesterBaseHandle { // Allow Nullability annotations on `DigesterBaseHandle`. using absl_nullability_compatible = void; + // If `write_size_hint` is not `absl::nullopt`, hints that this amount of data + // will be written from the current position. This may improve performance and + // memory usage. + // + // If the hint turns out to not match reality, nothing breaks. It is better if + // `write_size_hint` is slightly too large than slightly too small. + void SetWriteSizeHint(absl::optional write_size_hint) { + methods_->set_write_size_hint(target_, write_size_hint); + } + // Called with consecutive fragments of data. // // Precondition: the digester is open. @@ -231,6 +252,14 @@ class DigesterBaseHandle { return true; } + template + struct DigesterTargetHasSetWriteSizeHint : std::false_type {}; + + template + struct DigesterTargetHasSetWriteSizeHint< + T, absl::void_t().SetWriteSizeHint( + std::declval>()))>> : std::true_type {}; + template struct DigesterTargetHasWriteChain : std::false_type {}; @@ -272,6 +301,20 @@ class DigesterBaseHandle { decltype(std::declval().status()), absl::Status>::value>> : std::true_type {}; + template < + typename T, + std::enable_if_t::value, int> = 0> + static void SetWriteSizeHintMethod(void* target, + absl::optional write_size_hint) { + static_cast(target)->SetWriteSizeHint(write_size_hint); + } + template < + typename T, + std::enable_if_t::value, int> = 0> + static void SetWriteSizeHintMethod( + ABSL_ATTRIBUTE_UNUSED void* target, + ABSL_ATTRIBUTE_UNUSED absl::optional write_size_hint) {} + template static auto RawWriteMethod(void* target, absl::string_view src) { return static_cast(target)->Write(src); @@ -364,6 +407,8 @@ class DigesterBaseHandle { protected: struct Methods { + void (*set_write_size_hint)(void* target, + absl::optional write_size_hint); bool (*write)(void* target, absl::string_view src); bool (*write_chain)(void* target, const Chain& src); bool (*write_cord)(void* target, const absl::Cord& src); @@ -373,9 +418,13 @@ class DigesterBaseHandle { }; template - static constexpr Methods kMethods = {WriteMethod, WriteChainMethod, - WriteCordMethod, WriteZerosMethod, - CloseMethod, StatusMethod}; + static constexpr Methods kMethods = {SetWriteSizeHintMethod, + WriteMethod, + WriteChainMethod, + WriteCordMethod, + WriteZerosMethod, + CloseMethod, + StatusMethod}; template explicit DigesterBaseHandle(const Methods* methods, T* target) diff --git a/riegeli/digests/digesting_writer.h b/riegeli/digests/digesting_writer.h index 0c3843f0..7006aba8 100644 --- a/riegeli/digests/digesting_writer.h +++ b/riegeli/digests/digesting_writer.h @@ -28,6 +28,8 @@ #include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/utility/utility.h" +#include "riegeli/base/arithmetic.h" #include "riegeli/base/assert.h" #include "riegeli/base/chain.h" #include "riegeli/base/dependency.h" @@ -348,7 +350,14 @@ void DigestingWriter::SetWriteSizeHintImpl( if (dest_.IsOwning()) { if (ABSL_PREDICT_FALSE(!SyncBuffer(*dest_))) return; dest_->SetWriteSizeHint(write_size_hint); + if (digester_.IsOwning()) digester_.get().SetWriteSizeHint(write_size_hint); MakeBuffer(*dest_); + } else if (digester_.IsOwning()) { + digester_.get().SetWriteSizeHint( + write_size_hint == absl::nullopt + ? absl::nullopt + : absl::make_optional(SaturatingAdd(Position{start_to_cursor()}, + *write_size_hint))); } } @@ -368,6 +377,22 @@ namespace digesting_writer_internal { ABSL_ATTRIBUTE_COLD absl::Status FailedStatus(DigesterBaseHandle digester); +template ...>::value, int> = 0> +ABSL_ATTRIBUTE_ALWAYS_INLINE inline void SetWriteSizeHint( + DigesterOrWriter&& dest, const Srcs&... srcs) { + std::forward(dest).SetWriteSizeHint( + SaturatingAdd(riegeli::StringifiedSize(srcs)...)); +} + +template ...>::value, int> = 0> +ABSL_ATTRIBUTE_ALWAYS_INLINE inline void SetWriteSizeHint( + ABSL_ATTRIBUTE_UNUSED DigesterOrWriter&& dest, + ABSL_ATTRIBUTE_UNUSED const Srcs&... srcs) {} + template struct SupportedByDigesterHandle : std::false_type {}; @@ -400,6 +425,13 @@ inline DesiredDigestType DigestFromImpl(std::tuple srcs, Digester&& digester) { Dependency digester_dep( std::forward(digester)); + if (digester_dep.IsOwning()) { + absl::apply( + [&](const Srcs&... srcs) { + SetWriteSizeHint(digester_dep.get(), srcs...); + }, + srcs); + } bool ok = WriteTuple<0>(srcs, digester_dep.get()); if (digester_dep.IsOwning()) { if (ABSL_PREDICT_FALSE(!digester_dep.get().Close())) ok = false; @@ -416,6 +448,9 @@ inline DesiredDigestType DigestFromImpl(std::tuple srcs, Digester&& digester) { DigestingWriter writer( riegeli::Maker(), std::forward(digester)); + absl::apply( + [&](const Srcs&... srcs) { SetWriteSizeHint(writer.get(), srcs...); }, + srcs); writer.WriteTuple(srcs); RIEGELI_CHECK(writer.Close()) << writer.status(); return writer.template Digest(); diff --git a/riegeli/digests/wrapping_digester.h b/riegeli/digests/wrapping_digester.h index e0849403..ac64b5e8 100644 --- a/riegeli/digests/wrapping_digester.h +++ b/riegeli/digests/wrapping_digester.h @@ -24,6 +24,7 @@ #include "absl/meta/type_traits.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "riegeli/base/chain.h" #include "riegeli/base/dependency.h" #include "riegeli/base/maker.h" @@ -100,6 +101,10 @@ class WrappingDigester { base_.Reset(riegeli::Maker(std::forward(args)...)); } + void SetWriteSizeHint(absl::optional write_size_hint) { + if (base_.IsOwning()) base_.get().SetWriteSizeHint(write_size_hint); + } + bool Write(absl::string_view src) { return base_.get().Write(src); } bool Write(const Chain& src) { return base_.get().Write(src); } bool Write(const absl::Cord& src) { return base_.get().Write(src); }