Make conversion from `{String,Bytes,Path}Ref` to `absl::string_view` explicit.
Same for conversion from `{Bytes,Path}Ref` to `StringRef`. Conversions in the
other direction remain implicit.

Mutual implicit conversions are prone to ambiguities, e.g. in heterogeneous
calls to `==`.

Comparisons are especially problematic, as subtleties of overload resolution
depend on whether secondary comparison operators are derived automatically
in C++20 or emulated in C++17 with `WithEqual` and `WithCompare`. If primary
comparison operators have concrete parameter types, then C++20 provides
secondary comparison operators with the same parameter types, for which argument
conversions happen in the caller, while C++17 emulataion provides templates for
which argument conversions happen in the callee.

Types `StringRef` etc. are designed for function parameters. It is important
that they accept any suitable argument, i.e. conversions to them are important
to be implicit. OTOH consuming these parameters can be less convenient, and
conversions from them can be explicit.

Let `BytesRef` and `PathRef` derive from `StringRefBase` extracted from
`StringRef` rather than from `StringRef` itself. The latter would make
`BytesRef` implicitly convertible to `StringRef`, while we prefer
the other direction: `BytesRef` accepts more types than `StringRef`, e.g.
`Span<const char>`, so `BytesRef` should accept `StringRef` instead of
the other way around.

Make `CStringRef` convertible from types convertible to `std::string`, e.g.
`StringInitializer`. This improves compatibility with `StringRef`.

Polish constructors of `StringRef` etc.:

* Accept view types by value. This lets `ABSL_ATTRIBUTE_LIFETIME_BOUND` apply
  only to what the view is constructed from, but not to the view object itself.
  The view can safely be a temporary. Also, this reduces template instantiations
  in common usages.

* Remove `ABSL_ATTRIBUTE_LIFETIME_BOUND` where the parameter reference is not
  actually retained because its data have been just copied to `std::string`.

* Constrain templates by types being convertible to `absl::string_view` rather
  than to `StringRef` etc. The latter conversions may keep data in defaulted
  arguments, which do not survive forwarding, so they should not be used inside
  implementations of other conversions to view types.

Coalesce comments about the constructors. A single comment states the goal of
multiple constructors, rather than details of the order of conversions and
materialization by each constructor. The details are not that important,
and the expectations about arguments are already stated in class comments.

Make `BytesRef` comparable only against itself, and make `CompactString`
and `OptionalCompactString` comparable against `absl::string_view` but not
`BytesRef`. While this disallows direct comparisons between e.g. `CompactString`
and `std::array<char, length>`, this reduces ambiguities in cases like
`CompactString == StringRef`. Arbitrary types convertible to `BytesRef`
are not necessarily comparable anyway, e.g. `std::string` against
`std::array<char, length>`, so `CompactString` does not need to cover that.

`BytesRef` remains implicitly convertible both from and to
`absl::Span<const char>`. This is hard to avoid because `absl::Span<const char>`
is convertible from any type providing suitable `data()` and `size()`,
which includes `BytesRef`.

`BytesRef` cannot be reliably compared against `absl::Span<const char>` in C++17
mode. This is so because `absl::Span` provides a comparison against any type
convertible to `absl::Span`, which is ambiguous wrt. comparisons provided by
`WithCompare<BytesRef>`. It is comparable in C++20 mode though, because overload
resolution prefers templated operators provided by `Span` over converting `Span`
to `BytesRef` on the caller side.

PiperOrigin-RevId: 900626152
diff --git a/riegeli/base/BUILD b/riegeli/base/BUILD
index 5be6bb4..4a8299a 100644
--- a/riegeli/base/BUILD
+++ b/riegeli/base/BUILD
@@ -264,7 +264,6 @@
         ":compare",
         ":initializer",
         ":type_traits",
-        "@com_google_absl//absl/base:config",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/base:nullability",
         "@com_google_absl//absl/strings:string_view",
@@ -626,7 +625,6 @@
         ":external_data",
         ":new_aligned",
         ":null_safe_memcpy",
-        ":type_traits",
         "@com_google_absl//absl/base:config",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/hash",
@@ -643,7 +641,6 @@
         ":compact_string",
         ":compare",
         ":iterable",
-        ":type_traits",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/strings:string_view",
     ],
diff --git a/riegeli/base/bytes_ref.h b/riegeli/base/bytes_ref.h
index d8d43a1..93ef921 100644
--- a/riegeli/base/bytes_ref.h
+++ b/riegeli/base/bytes_ref.h
@@ -44,93 +44,83 @@
 // an `absl::string_view`, and the caller might have another representation
 // of the string.
 //
-// It is convertible from:
+// It is implicitly convertible from:
 //  * types convertible to `absl::string_view`
 //  * types convertible to `std::string`, e.g. `BytesInitializer`
 //  * types convertible to `absl::Span<const char>`,
 //    e.g. `std::vector<char>` or `std::array<char, length>`.
+//  * `StringRef`
+//
+// It is explicitly convertible to `absl::string_view`, `std::string`, or
+// `StringRef`.
 //
 // `BytesRef` does not own string contents and is efficiently copyable.
-class BytesRef : public StringRef, public WithCompare<BytesRef> {
+class BytesRef : public StringRefBase, public WithCompare<BytesRef> {
  public:
   // Stores an empty `absl::string_view`.
   BytesRef() = default;
 
   // Stores `str` converted to `absl::string_view`.
+
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   /*implicit*/ BytesRef(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(absl::string_view(str)) {}
+      : StringRefBase(str) {}
 
-  // Stores `str` converted to `StringRef` and then to `absl::string_view`.
+  /*implicit*/ BytesRef(absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(str) {}
+
+  /*implicit*/ BytesRef(StringRef str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(absl::string_view(str)) {}
+
+  /*implicit*/ BytesRef(absl::Span<char> str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(absl::string_view(str.data(), str.size())) {}
+
+  /*implicit*/ BytesRef(
+      absl::Span<const char> str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(absl::string_view(str.data(), str.size())) {}
+
   template <typename T,
             std::enable_if_t<
                 std::conjunction_v<NotSameRef<BytesRef, T>,
                                    std::is_convertible<T&&, absl::string_view>>,
                 int> = 0>
   /*implicit*/ BytesRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(std::forward<T>(str)) {}
+      : StringRefBase(std::forward<T>(str)) {}
 
-  // Stores `str` converted to `absl::string_view`.
-  /*implicit*/ BytesRef(
-      absl::Span<const char> str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(absl::string_view(str.data(), str.size())) {}
-
-  // Stores `str` materialized, then converted to `StringRef` and then to
-  // `absl::string_view`.
   template <typename T,
             std::enable_if_t<
                 std::conjunction_v<
                     NotSameRef<BytesRef, T>,
                     std::negation<std::is_convertible<T&&, absl::string_view>>,
-                    std::is_convertible<T&&, std::string>>,
+                    std::is_convertible<T&&, absl::Span<const char>>>,
                 int> = 0>
-  /*implicit*/ BytesRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
-                        TemporaryStorage<std::string>&& storage
-                            ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : StringRef(std::forward<T>(str), std::move(storage)) {}
-
-  // Stores `str` converted to `absl::Span<const char>` and then to
-  // `absl::string_view`.
-  template <
-      typename T,
-      std::enable_if_t<
-          std::conjunction_v<NotSameRef<BytesRef, T>,
-                             std::negation<std::is_convertible<T&&, StringRef>>,
-                             NotSameRef<absl::Span<const char>, T>,
-                             std::is_convertible<T&&, absl::Span<const char>>>,
-          int> = 0>
   /*implicit*/ BytesRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : BytesRef(absl::Span<const char>(std::forward<T>(str))) {}
 
+  template <
+      typename T,
+      std::enable_if_t<
+          std::conjunction_v<
+              NotSameRef<BytesRef, T>,
+              std::negation<std::is_convertible<T&&, absl::string_view>>,
+              std::negation<std::is_convertible<T&&, absl::Span<const char>>>,
+              std::is_convertible<T&&, std::string>>,
+          int> = 0>
+  /*implicit*/ BytesRef(T&& str, TemporaryStorage<std::string>&& storage
+                                     ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : StringRefBase(std::move(storage).emplace(std::forward<T>(str))) {}
+
   BytesRef(const BytesRef& that) = default;
   BytesRef& operator=(const BytesRef&) = delete;
 
+  explicit operator StringRef() const { return absl::string_view(*this); }
+
   friend bool operator==(BytesRef a, BytesRef b) {
     return absl::string_view(a) == absl::string_view(b);
   }
   friend riegeli::StrongOrdering RIEGELI_COMPARE(BytesRef a, BytesRef b) {
     return riegeli::Compare(absl::string_view(a), absl::string_view(b));
   }
-
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<BytesRef, T>,
-                                          std::is_convertible<T&&, StringRef>>,
-                       int> = 0>
-  friend bool operator==(BytesRef a, T&& b) {
-    return a == BytesRef(std::forward<T>(b));
-  }
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<BytesRef, T>,
-                                          std::is_convertible<T&&, StringRef>>,
-                       int> = 0>
-  friend riegeli::StrongOrdering RIEGELI_COMPARE(BytesRef a, T&& b) {
-    return riegeli::Compare(a, BytesRef(std::forward<T>(b)));
-  }
-
-  // `absl::Span<const char>` is already comparable against types convertible to
-  // `absl::Span<const char>`, which includes `BytesRef`.
 };
 
 // `BytesInitializer` is convertible from the same types as `BytesRef`,
@@ -141,14 +131,39 @@
  public:
   BytesInitializer() = default;
 
-  // Stores `str` converted to `absl::string_view` and then to `std::string`.
+  // Stores `str` converted to `std::string`.
+
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   /*implicit*/ BytesInitializer(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND,
                                 TemporaryStorage<MakerType<absl::string_view>>&&
                                     storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(std::move(storage).emplace(absl::string_view(str))) {}
+      : BytesInitializer(absl::string_view(str), std::move(storage)) {}
 
-  // Stores `str` converted to `std::string`.
+  /*implicit*/ BytesInitializer(
+      absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : Initializer(std::move(storage).emplace(str)) {}
+
+  /*implicit*/ BytesInitializer(StringRef str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+                                TemporaryStorage<MakerType<absl::string_view>>&&
+                                    storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : BytesInitializer(absl::string_view(str), std::move(storage)) {}
+
+  /*implicit*/ BytesInitializer(
+      absl::Span<char> str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : BytesInitializer(absl::string_view(str.data(), str.size()),
+                         std::move(storage)) {}
+
+  /*implicit*/ BytesInitializer(
+      absl::Span<const char> str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : BytesInitializer(absl::string_view(str.data(), str.size()),
+                         std::move(storage)) {}
+
   template <typename T,
             std::enable_if_t<
                 std::conjunction_v<NotSameRef<BytesInitializer, T>,
@@ -157,20 +172,32 @@
   /*implicit*/ BytesInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : Initializer(std::forward<T>(str)) {}
 
-  // Stores `str` converted to `BytesRef`, then to `absl::string_view`, and then
-  // to `std::string`.
   template <
       typename T,
       std::enable_if_t<std::conjunction_v<
                            NotSameRef<BytesInitializer, T>,
                            std::negation<std::is_convertible<T&&, std::string>>,
-                           std::is_convertible<T&&, BytesRef>>,
+                           std::is_convertible<T&&, absl::string_view>>,
                        int> = 0>
   /*implicit*/ BytesInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
                                 TemporaryStorage<MakerType<absl::string_view>>&&
                                     storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(
-            std::move(storage).emplace(BytesRef(std::forward<T>(str)))) {}
+      : BytesInitializer(absl::string_view(std::forward<T>(str)),
+                         std::move(storage)) {}
+
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<
+                    NotSameRef<BytesInitializer, T>,
+                    std::negation<std::is_convertible<T&&, std::string>>,
+                    std::negation<std::is_convertible<T&&, absl::string_view>>,
+                    std::is_convertible<T&&, absl::Span<const char>>>,
+                int> = 0>
+  /*implicit*/ BytesInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+                                TemporaryStorage<MakerType<absl::string_view>>&&
+                                    storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : BytesInitializer(absl::Span<const char>(std::forward<T>(str)),
+                         std::move(storage)) {}
 
   BytesInitializer(BytesInitializer&& that) = default;
   BytesInitializer& operator=(BytesInitializer&&) = delete;
diff --git a/riegeli/base/c_string_ref.h b/riegeli/base/c_string_ref.h
index bef5588..8ce4564 100644
--- a/riegeli/base/c_string_ref.h
+++ b/riegeli/base/c_string_ref.h
@@ -39,11 +39,12 @@
 // a C-style NUL-terminated string, and the caller might have another
 // representation of the string.
 //
-// It is convertible from:
+// It is implicitly convertible from:
 //  * `std::nullptr_t`
 //  * types convertible to `const char*`
 //  * types supporting `c_str()`, e.g. `std::string` or mutable `CompactString`
 //  * types convertible to `absl::string_view`
+//  * types convertible to `std::string`, e.g. `StringInitializer`
 //
 // It copies string contents when this is needed for NUL-termination,
 // e.g. for types convertible to `absl::string_view` excluding `std::string`
@@ -66,51 +67,64 @@
   CStringRef() = default;
   /*implicit*/ CStringRef(std::nullptr_t) {}
 
-  // Stores `str`.
+  // Stores a pointer to NUL-terminated contents of `str`, copied if needed.
+
   /*implicit*/ CStringRef(
       const char* absl_nullable str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : c_str_(str) {}
 
-  // Stores `str` converted to `const char*`.
+  /*implicit*/ CStringRef(absl::string_view str,
+                          TemporaryStorage<std::string>&& storage
+                              ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : CStringRef(std::move(storage).emplace(str)) {}
+
+  /*implicit*/ CStringRef(StringRef str, TemporaryStorage<std::string>&& storage
+                                             ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : CStringRef(absl::string_view(str), std::move(storage)) {}
+
   template <typename T,
             std::enable_if_t<
                 std::conjunction_v<NotSameRef<CStringRef, T>,
-                                   NotSameRef<std::nullptr_t, T>,
-                                   NotSameRef<const char*, T>,
                                    std::is_convertible<T&&, const char*>>,
                 int> = 0>
   /*implicit*/ CStringRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : c_str_(std::forward<T>(str)) {}
 
-  // Stores `str.c_str()`. This applies e.g. to `std::string` and
-  // mutable `CompactString`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<
-                    NotSameRef<CStringRef, T>, NotSameRef<std::nullptr_t, T>,
-                    std::negation<std::is_convertible<T&&, const char*>>,
-                    HasCStr<T&&>>,
-                int> = 0>
+  template <
+      typename T,
+      std::enable_if_t<std::conjunction_v<
+                           NotSameRef<CStringRef, T>,
+                           std::negation<std::is_convertible<T&&, const char*>>,
+                           HasCStr<T&&>>,
+                       int> = 0>
   /*implicit*/ CStringRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : c_str_(std::forward<T>(str).c_str()) {}
 
-  // Stores a pointer to the first character of a NUL-terminated copy of `str`
-  // converted to `StringRef` and then to `absl::string_view`.
-  //
-  // The string is stored in a storage object passed as a default argument to
-  // this constructor.
   template <
       typename T,
-      std::enable_if_t<
-          std::conjunction_v<
-              NotSameRef<CStringRef, T>, NotSameRef<std::nullptr_t, T>,
-              std::negation<std::is_convertible<T&&, const char*>>,
-              std::negation<HasCStr<T&&>>, std::is_convertible<T&&, StringRef>>,
-          int> = 0>
+      std::enable_if_t<std::conjunction_v<
+                           NotSameRef<CStringRef, T>,
+                           std::negation<std::is_convertible<T&&, const char*>>,
+                           std::negation<HasCStr<T&&>>,
+                           std::is_convertible<T&&, absl::string_view>>,
+                       int> = 0>
   /*implicit*/ CStringRef(T&& str, TemporaryStorage<std::string>&& storage
                                        ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : CStringRef(std::move(storage).emplace(
-            absl::string_view(StringRef(std::forward<T>(str))))) {}
+      : CStringRef(absl::string_view(std::forward<T>(str)),
+                   std::move(storage)) {}
+
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<
+                    NotSameRef<CStringRef, T>,
+                    std::negation<std::is_convertible<T&&, const char*>>,
+                    std::negation<HasCStr<T&&>>,
+                    std::negation<std::is_convertible<T&&, absl::string_view>>,
+                    std::is_convertible<T&&, std::string>>,
+                int> = 0>
+  /*implicit*/ CStringRef(T&& str, TemporaryStorage<std::string>&& storage
+                                       ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : CStringRef(std::move(storage).emplace(std::forward<T>(str))) {}
 
   CStringRef(const CStringRef& that) = default;
   CStringRef& operator=(const CStringRef&) = delete;
diff --git a/riegeli/base/chain.cc b/riegeli/base/chain.cc
index d19394f..17d917d 100644
--- a/riegeli/base/chain.cc
+++ b/riegeli/base/chain.cc
@@ -377,7 +377,7 @@
     Append(src, Options().set_size_hint(src.size()));
     return;
   }
-  Initialize(src);
+  Initialize(absl::string_view(src));
 }
 
 void Chain::Reset(Block src) {
diff --git a/riegeli/base/chain_details.h b/riegeli/base/chain_details.h
index d48c477..432dfd2 100644
--- a/riegeli/base/chain_details.h
+++ b/riegeli/base/chain_details.h
@@ -518,7 +518,7 @@
 inline Chain::RawBlock::RawBlock(Initializer<T> object) {
   external_.methods = &ExternalMethodsFor<T>::kMethods;
   new (&unchecked_external_object<T>()) T(std::move(object));
-  substr_ = BytesRef(unchecked_external_object<T>());
+  substr_ = absl::string_view(BytesRef(unchecked_external_object<T>()));
   RIEGELI_ASSERT(is_external()) << "A RawBlock with allocated_end_ == nullptr "
                                    "should be considered external";
 }
@@ -934,7 +934,7 @@
   return RawBlock::kExternalAllocatedSize<T>();
 }
 
-inline Chain::Chain(BytesRef src) { Initialize(src); }
+inline Chain::Chain(BytesRef src) { Initialize(absl::string_view(src)); }
 
 inline Chain::Chain(ExternalRef src) { std::move(src).InitializeTo(*this); }
 
diff --git a/riegeli/base/compact_string.h b/riegeli/base/compact_string.h
index a7079b3..66542c8 100644
--- a/riegeli/base/compact_string.h
+++ b/riegeli/base/compact_string.h
@@ -21,7 +21,6 @@
 #include <cstring>
 #include <iosfwd>
 #include <limits>
-#include <type_traits>
 #include <utility>
 
 #include "absl/base/attributes.h"
@@ -36,7 +35,6 @@
 #include "riegeli/base/external_data.h"
 #include "riegeli/base/new_aligned.h"
 #include "riegeli/base/null_safe_memcpy.h"
-#include "riegeli/base/type_traits.h"
 
 namespace riegeli {
 
@@ -89,13 +87,15 @@
   explicit CompactString(size_t size) : repr_(MakeRepr(size)) {}
 
   // Creates a `CompactString` which holds a copy of `src`.
-  explicit CompactString(BytesRef src) : repr_(MakeRepr(src)) {}
+  explicit CompactString(BytesRef src)
+      : repr_(MakeRepr(absl::string_view(src))) {}
   CompactString& operator=(BytesRef src);
 
   // Creates a `CompactString` which holds a copy of `src`. Reserves one extra
   // char so that `c_str()` does not need reallocation.
   static CompactString ForCStr(BytesRef src) {
-    return CompactString(FromReprTag(), MakeRepr(src, src.size() + 1));
+    return CompactString(FromReprTag(),
+                         MakeRepr(absl::string_view(src), src.size() + 1));
   }
 
   CompactString(const CompactString& that);
@@ -286,21 +286,12 @@
     return riegeli::Compare(absl::string_view(a), absl::string_view(b));
   }
 
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<CompactString, T>,
-                                          std::is_convertible<T&&, BytesRef>>,
-                       int> = 0>
-  friend bool operator==(const CompactString& a, T&& b) {
-    return absl::string_view(a) == BytesRef(std::forward<T>(b));
+  friend bool operator==(const CompactString& a, absl::string_view b) {
+    return absl::string_view(a) == b;
   }
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<CompactString, T>,
-                                          std::is_convertible<T&&, BytesRef>>,
-                       int> = 0>
-  friend StrongOrdering RIEGELI_COMPARE(const CompactString& a, T&& b) {
-    return riegeli::Compare(absl::string_view(a), BytesRef(std::forward<T>(b)));
+  friend StrongOrdering RIEGELI_COMPARE(const CompactString& a,
+                                        absl::string_view b) {
+    return riegeli::Compare(absl::string_view(a), b);
   }
 
   template <typename HashState>
@@ -620,7 +611,7 @@
     // Use `memmove()` to support assigning from a substring of `*this`.
     riegeli::null_safe_memmove(data(), src.data(), src.size());
   } else {
-    AssignSlow(src);
+    AssignSlow(absl::string_view(src));
   }
   return *this;
 }
diff --git a/riegeli/base/dependency.h b/riegeli/base/dependency.h
index 530baab..9ecb4aa 100644
--- a/riegeli/base/dependency.h
+++ b/riegeli/base/dependency.h
@@ -233,6 +233,38 @@
   ~DependencyImpl() = default;
 };
 
+// Specialization of `DependencyImpl<absl::string_view, Manager>` when
+// `DependencyManagerRef<Manager>` is convertible to `BytesRef`.
+template <typename Manager>
+class DependencyImpl<
+    absl::string_view, Manager,
+    std::enable_if_t<std::conjunction_v<
+        std::is_pointer<DependencyManagerPtr<Manager>>,
+        std::is_convertible<DependencyManagerRef<Manager>, BytesRef>>>>
+    : public DependencyManager<Manager> {
+ public:
+  using DependencyImpl::DependencyManager::DependencyManager;
+
+  absl::string_view get() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+    return absl::string_view(BytesRef(*this->ptr()));
+  }
+
+  static constexpr bool kIsStable =
+      DependencyImpl::DependencyManager::kIsStable ||
+      std::is_same_v<Manager, absl::string_view> ||
+      std::is_same_v<Manager, absl::Span<char>> ||
+      std::is_same_v<Manager, absl::Span<const char>>;
+
+ protected:
+  DependencyImpl(const DependencyImpl& that) = default;
+  DependencyImpl& operator=(const DependencyImpl& that) = default;
+
+  DependencyImpl(DependencyImpl&& that) = default;
+  DependencyImpl& operator=(DependencyImpl&& that) = default;
+
+  ~DependencyImpl() = default;
+};
+
 // Specialization of `DependencyImpl<absl::Span<T>, Manager>` when
 // `DependencyManagerRef<Manager>` is explicitly convertible to `absl::Span<T>`.
 //
@@ -303,68 +335,7 @@
     return this->ptr();
   }
 
- protected:
-  DependencyImpl(const DependencyImpl& that) = default;
-  DependencyImpl& operator=(const DependencyImpl& that) = default;
-
-  DependencyImpl(DependencyImpl&& that) = default;
-  DependencyImpl& operator=(DependencyImpl&& that) = default;
-
-  ~DependencyImpl() = default;
-};
-
-// Specialization of `DependencyImpl<absl::string_view, Manager>` when
-// `DependencyManagerRef<Manager>` is convertible to `BytesRef`.
-template <typename Manager>
-class DependencyImpl<
-    absl::string_view, Manager,
-    std::enable_if_t<std::conjunction_v<
-        std::is_pointer<DependencyManagerPtr<Manager>>,
-        std::is_convertible<DependencyManagerRef<Manager>, BytesRef>>>>
-    : public DependencyManager<Manager> {
- public:
-  using DependencyImpl::DependencyManager::DependencyManager;
-
-  absl::string_view get() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
-    return BytesRef(*this->ptr());
-  }
-
-  static constexpr bool kIsStable =
-      DependencyImpl::DependencyManager::kIsStable ||
-      std::is_same_v<Manager, absl::string_view> ||
-      std::is_same_v<Manager, absl::Span<const char>> ||
-      std::is_same_v<Manager, absl::Span<char>>;
-
- protected:
-  DependencyImpl(const DependencyImpl& that) = default;
-  DependencyImpl& operator=(const DependencyImpl& that) = default;
-
-  DependencyImpl(DependencyImpl&& that) = default;
-  DependencyImpl& operator=(DependencyImpl&& that) = default;
-
-  ~DependencyImpl() = default;
-};
-
-// Specialization of `DependencyImpl<absl::string_view, Manager>` when
-// `DependencyManagerPtr<Manager>` is `absl::Span<const char>` or
-// `absl::Span<char>`.
-//
-// Specialized separately because `absl::Span<const char>` is not convertible
-// to `absl::string_view` in the regular way.
-template <typename Manager>
-class DependencyImpl<
-    absl::string_view, Manager,
-    std::enable_if_t<std::disjunction_v<
-        std::is_same<DependencyManagerPtr<Manager>, absl::Span<const char>>,
-        std::is_same<DependencyManagerPtr<Manager>, absl::Span<char>>>>>
-    : public DependencyManager<Manager> {
- public:
-  using DependencyImpl::DependencyManager::DependencyManager;
-
-  absl::string_view get() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
-    const absl::Span<const char> span = this->ptr();
-    return absl::string_view(span.data(), span.size());
-  }
+  static constexpr bool kIsStable = true;
 
  protected:
   DependencyImpl(const DependencyImpl& that) = default;
diff --git a/riegeli/base/external_ref_base.h b/riegeli/base/external_ref_base.h
index aaa8c38..f0bb578 100644
--- a/riegeli/base/external_ref_base.h
+++ b/riegeli/base/external_ref_base.h
@@ -254,7 +254,7 @@
   template <typename T,
             std::enable_if_t<HasCallOperatorSubstr<T>::value, int> = 0>
   static void CallOperatorWhole(T&& object) {
-    const absl::string_view data = BytesRef(object);
+    const absl::string_view data{BytesRef(object)};
     std::forward<T>(object)(data);
   }
   template <typename T,
@@ -273,7 +273,7 @@
           int> = 0>
   static void CallOperatorWhole(T&& object) {
     absl::remove_cvref_t<T> copy(object);
-    const absl::string_view data = BytesRef(copy);
+    const absl::string_view data{BytesRef(copy)};
     std::move(copy)(data);
   }
   template <
@@ -315,7 +315,7 @@
   static void CallOperatorSubstr(
       T&& object, ABSL_ATTRIBUTE_UNUSED absl::string_view substr) {
     absl::remove_cvref_t<T> copy(object);
-    const absl::string_view data = BytesRef(copy);
+    const absl::string_view data{BytesRef(copy)};
     std::move(copy)(data);
   }
   template <
@@ -407,7 +407,7 @@
                     HasRiegeliExternalDelegateSubstr<T, Callback>>,
                 int> = 0>
   static void ExternalDelegateWhole(T&& object, Callback&& delegate_to) {
-    const absl::string_view data = BytesRef(object);
+    const absl::string_view data{BytesRef(object)};
     RiegeliExternalDelegate(ExternalRef::Pointer(std::forward<T>(object)), data,
                             std::forward<Callback>(delegate_to));
   }
@@ -497,7 +497,7 @@
                                  HasRiegeliToChainBlockSubstr<T>>,
                              int> = 0>
   static Chain::Block ToChainBlockWhole(T&& object) {
-    const absl::string_view data = BytesRef(object);
+    const absl::string_view data{BytesRef(object)};
     return RiegeliToChainBlock(ExternalRef::Pointer(std::forward<T>(object)),
                                data);
   }
@@ -540,7 +540,7 @@
         std::enable_if_t<std::is_convertible_v<const SubT&, BytesRef>, int> = 0>
     void operator()(SubT&& subobject) && {
       // The constructor processes the subobject.
-      const absl::string_view data = BytesRef(subobject);
+      const absl::string_view data{BytesRef(subobject)};
       ConverterToChainBlockWhole<SubT> converter(
           std::forward<SubT>(subobject), data, context_, use_string_view_,
           use_chain_block_);
@@ -826,7 +826,7 @@
                                    HasRiegeliToCordSubstr<T>>,
                 int> = 0>
   static absl::Cord ToCordWhole(T&& object) {
-    const absl::string_view data = BytesRef(object);
+    const absl::string_view data{BytesRef(object)};
     return RiegeliToCord(ExternalRef::Pointer(std::forward<T>(object)), data);
   }
 
@@ -865,7 +865,7 @@
         std::enable_if_t<std::is_convertible_v<const SubT&, BytesRef>, int> = 0>
     void operator()(SubT&& subobject) && {
       // The constructor processes the subobject.
-      const absl::string_view data = BytesRef(subobject);
+      const absl::string_view data{BytesRef(subobject)};
       ConverterToCordWhole<SubT> converter(std::forward<SubT>(subobject), data,
                                            context_, use_string_view_,
                                            use_cord_);
@@ -974,7 +974,7 @@
         T&& object, ABSL_ATTRIBUTE_UNUSED absl::string_view data) && {
       ObjectForCordWhole<std::decay_t<T>> object_for_cord(
           std::forward<T>(object));
-      const absl::string_view moved_data = BytesRef(*object_for_cord);
+      const absl::string_view moved_data{BytesRef(*object_for_cord)};
       use_cord_(context_, absl::MakeCordFromExternal(
                               moved_data, std::move(object_for_cord)));
     }
@@ -1216,7 +1216,7 @@
                              HasToExternalDataSubstr<T>>,
           int> = 0>
   static ExternalData ToExternalDataWhole(T&& object) {
-    const absl::string_view data = BytesRef(object);
+    const absl::string_view data{BytesRef(object)};
     return ExternalRef::ToExternalDataSubstr(std::forward<T>(object), data);
   }
 
@@ -1248,7 +1248,7 @@
         std::enable_if_t<std::is_convertible_v<const SubT&, BytesRef>, int> = 0>
     void operator()(SubT&& subobject) && {
       // The constructor processes the subobject.
-      const absl::string_view data = BytesRef(subobject);
+      const absl::string_view data{BytesRef(subobject)};
       ConverterToExternalDataWhole<SubT> converter(
           std::forward<SubT>(subobject), data, context_, use_external_data_);
     }
@@ -1334,7 +1334,7 @@
     void Callback(T&& object, ABSL_ATTRIBUTE_UNUSED absl::string_view data) {
       auto* const storage =
           new ExternalObjectWhole<std::decay_t<T>>(std::forward<T>(object));
-      const absl::string_view moved_data = BytesRef(**storage);
+      const absl::string_view moved_data{BytesRef(**storage)};
       use_external_data_(
           context_,
           ExternalData{
@@ -1521,7 +1521,7 @@
     void Initialize(Initializer<T> object) {
       object_.emplace(
           std::move(object).Reference(std::move(temporary_storage_)));
-      StorageBase::Initialize(BytesRef(*object_));
+      StorageBase::Initialize(absl::string_view(BytesRef(*object_)));
     }
 
     void ToChainBlock(size_t max_bytes_to_copy, void* context,
@@ -1585,7 +1585,7 @@
       T&& reference =
           std::move(object).Reference(std::move(temporary_storage_));
       object_ = &reference;
-      StorageBase::Initialize(BytesRef(*object_));
+      StorageBase::Initialize(absl::string_view(BytesRef(*object_)));
     }
 
     void ToChainBlock(size_t max_bytes_to_copy, void* context,
diff --git a/riegeli/base/optional_compact_string.h b/riegeli/base/optional_compact_string.h
index a6bf69e..506df7f 100644
--- a/riegeli/base/optional_compact_string.h
+++ b/riegeli/base/optional_compact_string.h
@@ -18,7 +18,6 @@
 #include <stdint.h>
 
 #include <cstddef>
-#include <type_traits>
 #include <utility>
 
 #include "absl/base/attributes.h"
@@ -28,7 +27,6 @@
 #include "riegeli/base/compact_string.h"
 #include "riegeli/base/compare.h"
 #include "riegeli/base/iterable.h"
-#include "riegeli/base/type_traits.h"
 
 namespace riegeli {
 
@@ -168,25 +166,14 @@
     return StrongOrdering::greater;
   }
 
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<OptionalCompactString, T>,
-                                          NotSameRef<std::nullptr_t, T>,
-                                          std::is_convertible<T&&, BytesRef>>,
-                       int> = 0>
-  friend bool operator==(const OptionalCompactString& a, T&& b) {
+  friend bool operator==(const OptionalCompactString& a, absl::string_view b) {
     if (a.repr_ == kNullRepr) return false;
-    return *a == absl::string_view(b);
+    return *a == b;
   }
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<OptionalCompactString, T>,
-                                          NotSameRef<std::nullptr_t, T>,
-                                          std::is_convertible<T&&, BytesRef>>,
-                       int> = 0>
-  friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a, T&& b) {
+  friend StrongOrdering RIEGELI_COMPARE(const OptionalCompactString& a,
+                                        absl::string_view b) {
     if (a.repr_ == kNullRepr) return StrongOrdering::less;
-    return riegeli::Compare(*a, absl::string_view(b));
+    return riegeli::Compare(*a, b);
   }
 
  private:
diff --git a/riegeli/base/string_ref.h b/riegeli/base/string_ref.h
index 6c0eff2..a2796db 100644
--- a/riegeli/base/string_ref.h
+++ b/riegeli/base/string_ref.h
@@ -23,7 +23,6 @@
 #include <utility>
 
 #include "absl/base/attributes.h"
-#include "absl/base/config.h"  // IWYU pragma: keep
 #include "absl/base/nullability.h"
 #include "absl/strings/string_view.h"
 #include "riegeli/base/assert.h"
@@ -37,62 +36,13 @@
 
 namespace riegeli {
 
-// `StringRef` stores an `absl::string_view`, usually representing text data
-// (see `BytesRef` for binary data), possibly converted through temporary
-// `std::string`.
-//
-// It is intended for function parameters when the implementation needs
-// an `absl::string_view`, and the caller might have another representation
-// of the string.
-//
-// It is convertible from:
-//  * types convertible to `absl::string_view`
-//  * types convertible to `std::string`, e.g. `StringInitializer`
-//
-// `StringRef` does not own string contents and is efficiently copyable.
-class StringRef : public WithCompare<StringRef> {
+// Common parts of `StringRef`, `BytesRef`, and `PathRef`.
+class StringRefBase {
  public:
-  // Stores an empty `absl::string_view`.
-  StringRef() = default;
+  explicit operator absl::string_view() const { return str_; }
+  explicit operator std::string() const { return std::string(str_); }
 
-  // Stores `str` converted to `absl::string_view`.
-  ABSL_ATTRIBUTE_ALWAYS_INLINE
-  /*implicit*/ StringRef(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : str_(str) {}
-
-  // Stores `str`.
-  /*implicit*/ StringRef(absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : str_(str) {}
-
-  // Stores `str` converted to `absl::string_view`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<NotSameRef<StringRef, T>,
-                                   NotSameRef<absl::string_view, T>,
-                                   std::is_convertible<T&&, absl::string_view>>,
-                int> = 0>
-  /*implicit*/ StringRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : str_(std::forward<T>(str)) {}
-
-  // Stores `str` materialized and then converted to `absl::string_view`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<
-                    NotSameRef<StringRef, T>,
-                    std::negation<std::is_convertible<T&&, absl::string_view>>,
-                    std::is_convertible<T&&, std::string>>,
-                int> = 0>
-  /*implicit*/ StringRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
-                         TemporaryStorage<std::string>&& storage
-                             ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : str_(std::move(storage).emplace(std::forward<T>(str))) {}
-
-  StringRef(const StringRef& that) = default;
-  StringRef& operator=(const StringRef&) = delete;
-
-  /*implicit*/ operator absl::string_view() const { return str_; }
-
-  bool empty() const { return size() == 0; }
+  bool empty() const { return str_.empty(); }
   const char* absl_nullable data() const { return str_.data(); };
   size_t size() const {
     RIEGELI_ASSUME_LE(str_.size(), str_.max_size());
@@ -107,42 +57,88 @@
   void remove_prefix(size_t length);
   void remove_suffix(size_t length);
 
+  // Default stringification by `absl::StrCat()` etc.
+  template <typename Sink>
+  friend void AbslStringify(Sink& dest, const StringRefBase& src) {
+    dest.Append(absl::string_view(src));
+  }
+
+  friend std::ostream& operator<<(std::ostream& dest,
+                                  const StringRefBase& src) {
+    return dest << absl::string_view(src);
+  }
+
+ protected:
+  StringRefBase() = default;
+
+  explicit StringRefBase(absl::string_view str) : str_(str) {}
+
+  StringRefBase(const StringRefBase& that) = default;
+  StringRefBase& operator=(const StringRefBase&) = delete;
+
+  ~StringRefBase() = default;
+
+ private:
+  absl::string_view str_;
+};
+
+// `StringRef` stores an `absl::string_view`, usually representing text data
+// (see `BytesRef` for binary data), possibly converted through temporary
+// `std::string`.
+//
+// It is intended for function parameters when the implementation needs
+// an `absl::string_view`, and the caller might have another representation
+// of the string.
+//
+// It is implicitly convertible from:
+//  * types convertible to `absl::string_view`
+//  * types convertible to `std::string`, e.g. `StringInitializer`
+//
+// It is explicitly convertible to `absl::string_view` or `std::string`.
+//
+// `StringRef` does not own string contents and is efficiently copyable.
+class StringRef : public StringRefBase, public WithCompare<StringRef> {
+ public:
+  // Stores an empty `absl::string_view`.
+  StringRef() = default;
+
+  // Stores `str` converted to `absl::string_view`.
+
+  ABSL_ATTRIBUTE_ALWAYS_INLINE
+  /*implicit*/ StringRef(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(str) {}
+
+  /*implicit*/ StringRef(absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(str) {}
+
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<NotSameRef<StringRef, T>,
+                                   std::is_convertible<T&&, absl::string_view>>,
+                int> = 0>
+  /*implicit*/ StringRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(std::forward<T>(str)) {}
+
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<
+                    NotSameRef<StringRef, T>,
+                    std::negation<std::is_convertible<T&&, absl::string_view>>,
+                    std::is_convertible<T&&, std::string>>,
+                int> = 0>
+  /*implicit*/ StringRef(T&& str, TemporaryStorage<std::string>&& storage
+                                      ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : StringRefBase(std::move(storage).emplace(std::forward<T>(str))) {}
+
+  StringRef(const StringRef& that) = default;
+  StringRef& operator=(const StringRef&) = delete;
+
   friend bool operator==(StringRef a, StringRef b) {
     return absl::string_view(a) == absl::string_view(b);
   }
   friend riegeli::StrongOrdering RIEGELI_COMPARE(StringRef a, StringRef b) {
     return riegeli::Compare(absl::string_view(a), absl::string_view(b));
   }
-
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<NotSameRef<StringRef, T>,
-                                   std::is_convertible<T&&, absl::string_view>>,
-                int> = 0>
-  friend bool operator==(StringRef a, T&& b) {
-    return a == StringRef(std::forward<T>(b));
-  }
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<NotSameRef<StringRef, T>,
-                                   std::is_convertible<T&&, absl::string_view>>,
-                int> = 0>
-  friend riegeli::StrongOrdering RIEGELI_COMPARE(StringRef a, T&& b) {
-    return riegeli::Compare(a, StringRef(std::forward<T>(b)));
-  }
-
-  // Default stringification by `absl::StrCat()` etc.
-  template <typename Sink>
-  friend void AbslStringify(Sink& dest, const StringRef& src) {
-    dest.Append(absl::string_view(src));
-  }
-
-  friend std::ostream& operator<<(std::ostream& dest, const StringRef& src) {
-    return dest << absl::string_view(src);
-  }
-
- private:
-  absl::string_view str_;
 };
 
 // `StringInitializer` is convertible from the same types as `StringRef`,
@@ -153,15 +149,25 @@
  public:
   StringInitializer() = default;
 
-  // Stores `str` converted to `absl::string_view` and then to `std::string`.
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   /*implicit*/ StringInitializer(
       const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND,
       TemporaryStorage<MakerType<absl::string_view>>&& storage
           ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(std::move(storage).emplace(absl::string_view(str))) {}
+      : StringInitializer(absl::string_view(str), std::move(storage)) {}
 
-  // Stores `str` converted to `std::string`.
+  /*implicit*/ StringInitializer(
+      absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : Initializer(std::move(storage).emplace(str)) {}
+
+  /*implicit*/ StringInitializer(
+      StringRef str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : StringInitializer(absl::string_view(str), std::move(storage)) {}
+
   template <typename T,
             std::enable_if_t<
                 std::conjunction_v<NotSameRef<StringInitializer, T>,
@@ -170,21 +176,19 @@
   /*implicit*/ StringInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : Initializer(std::forward<T>(str)) {}
 
-  // Stores `str` converted to `StringRef`, then to `absl::string_view`, and
-  // then to `std::string`.
   template <
       typename T,
       std::enable_if_t<std::conjunction_v<
                            NotSameRef<StringInitializer, T>,
                            std::negation<std::is_convertible<T&&, std::string>>,
-                           std::is_convertible<T&&, StringRef>>,
+                           std::is_convertible<T&&, absl::string_view>>,
                        int> = 0>
   /*implicit*/ StringInitializer(
       T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
       TemporaryStorage<MakerType<absl::string_view>>&& storage
           ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(
-            std::move(storage).emplace(StringRef(std::forward<T>(str)))) {}
+      : StringInitializer(absl::string_view(std::forward<T>(str)),
+                          std::move(storage)) {}
 
   StringInitializer(StringInitializer&& that) = default;
   StringInitializer& operator=(StringInitializer&&) = delete;
@@ -192,40 +196,40 @@
 
 // Implementation details follow.
 
-inline const char& StringRef::operator[](size_t index) const {
+inline const char& StringRefBase::operator[](size_t index) const {
   RIEGELI_ASSERT_LT(index, size())
-      << "Failed precondition of StringRef::operator[]: index out of range";
+      << "Failed precondition of StringRefBase::operator[]: index out of range";
   return str_[index];
 }
 
-inline const char& StringRef::at(size_t index) const {
+inline const char& StringRefBase::at(size_t index) const {
   RIEGELI_ASSERT_LT(index, size())
-      << "Failed precondition of StringRef::at(): index out of range";
+      << "Failed precondition of StringRefBase::at(): index out of range";
   return str_[index];
 }
 
-inline const char& StringRef::front() const {
+inline const char& StringRefBase::front() const {
   RIEGELI_ASSERT(!empty())
-      << "Failed precondition of StringRef::front(): empty string";
+      << "Failed precondition of StringRefBase::front(): empty string";
   return str_.front();
 }
 
-inline const char& StringRef::back() const {
+inline const char& StringRefBase::back() const {
   RIEGELI_ASSERT(!empty())
-      << "Failed precondition of StringRef::back(): empty string";
+      << "Failed precondition of StringRefBase::back(): empty string";
   return str_.back();
 }
 
-inline void StringRef::remove_prefix(size_t length) {
+inline void StringRefBase::remove_prefix(size_t length) {
   RIEGELI_ASSERT_LE(length, size())
-      << "Failed precondition of StringRef::remove_prefix(): "
+      << "Failed precondition of StringRefBase::remove_prefix(): "
          "length out of range";
   str_.remove_prefix(length);
 }
 
-inline void StringRef::remove_suffix(size_t length) {
+inline void StringRefBase::remove_suffix(size_t length) {
   RIEGELI_ASSERT_LE(length, size())
-      << "Failed precondition of StringRef::remove_suffix(): "
+      << "Failed precondition of StringRefBase::remove_suffix(): "
          "length out of range";
   str_.remove_suffix(length);
 }
diff --git a/riegeli/bytes/BUILD b/riegeli/bytes/BUILD
index 6575b3f..de6b70b 100644
--- a/riegeli/bytes/BUILD
+++ b/riegeli/bytes/BUILD
@@ -829,13 +829,17 @@
     deps = [
         ":reader",
         "//riegeli/base:assert",
+        "//riegeli/base:bytes_ref",
         "//riegeli/base:dependency",
         "//riegeli/base:initializer",
         "//riegeli/base:moving_dependency",
         "//riegeli/base:object",
+        "//riegeli/base:string_ref",
+        "//riegeli/base:type_traits",
         "//riegeli/base:types",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/strings:string_view",
+        "@com_google_absl//absl/types:span",
     ],
 )
 
diff --git a/riegeli/bytes/backward_writer.h b/riegeli/bytes/backward_writer.h
index d81bf44..7aa7ff1 100644
--- a/riegeli/bytes/backward_writer.h
+++ b/riegeli/bytes/backward_writer.h
@@ -537,7 +537,7 @@
     return true;
   }
   AssertInitialized(cursor(), start_to_cursor());
-  return WriteSlow(src);
+  return WriteSlow(absl::string_view(src));
 }
 
 inline bool BackwardWriter::Write(ExternalRef src) {
diff --git a/riegeli/bytes/fd_handle.cc b/riegeli/bytes/fd_handle.cc
index ef9e9dc..c1e6c1a 100644
--- a/riegeli/bytes/fd_handle.cc
+++ b/riegeli/bytes/fd_handle.cc
@@ -113,7 +113,7 @@
     dir_filename = dir_fd.filename();
     if (!dir_filename.empty() && dir_filename.back() != '/') separator = "/";
   }
-  Reset(-1, absl::StrCat(dir_filename, separator, filename));
+  Reset(-1, absl::StrCat(dir_filename, separator, absl::string_view(filename)));
 
 again:
   const int fd = openat(dir_fd.get(),
diff --git a/riegeli/bytes/fd_handle.h b/riegeli/bytes/fd_handle.h
index cc1edda..dab232a 100644
--- a/riegeli/bytes/fd_handle.h
+++ b/riegeli/bytes/fd_handle.h
@@ -88,7 +88,8 @@
            absl::Status>>> : std::true_type {};
 
 // `FdSupportsOpenAt<T>::value` is `true` if `T` supports `OpenAt()` with the
-// signature like in `OwnedFd` (with `permissions` present).
+// signature like in `OwnedFd` (with `permissions` present), but taking
+// `absl::string_view filename` instead of `PathRef filename` is sufficient.
 
 template <typename T, typename Enable = void>
 struct FdSupportsOpenAt : std::false_type {};
@@ -97,7 +98,7 @@
 struct FdSupportsOpenAt<
     T, std::enable_if_t<std::is_convertible_v<
            decltype(std::declval<T&>().OpenAt(
-               std::declval<UnownedFd>(), std::declval<PathRef>(),
+               std::declval<UnownedFd>(), std::declval<absl::string_view>(),
                std::declval<int>(), std::declval<fd_internal::Permissions>())),
            absl::Status>>> : std::true_type {};
 
diff --git a/riegeli/bytes/fd_mmap_reader.h b/riegeli/bytes/fd_mmap_reader.h
index 0fb7e52..d5e77b9 100644
--- a/riegeli/bytes/fd_mmap_reader.h
+++ b/riegeli/bytes/fd_mmap_reader.h
@@ -488,8 +488,8 @@
 void FdMMapReader<Src>::OpenAtImpl(UnownedFd dir_fd, PathRef filename,
                                    Options&& options) {
   absl::Status status =
-      src_.manager().OpenAt(std::move(dir_fd), filename, options.mode(),
-                            OwnedFd::kDefaultPermissions);
+      src_.manager().OpenAt(std::move(dir_fd), absl::string_view(filename),
+                            options.mode(), OwnedFd::kDefaultPermissions);
   if (ABSL_PREDICT_FALSE(!status.ok())) {
     FdMMapReaderBase::Reset(kClosed);
     FailWithoutAnnotation(std::move(status));
diff --git a/riegeli/bytes/fd_reader.h b/riegeli/bytes/fd_reader.h
index 4cefa8c..a660d04 100644
--- a/riegeli/bytes/fd_reader.h
+++ b/riegeli/bytes/fd_reader.h
@@ -609,8 +609,8 @@
 void FdReader<Src>::OpenAtImpl(UnownedFd dir_fd, PathRef filename,
                                Options&& options) {
   absl::Status status =
-      src_.manager().OpenAt(std::move(dir_fd), filename, options.mode(),
-                            OwnedFd::kDefaultPermissions);
+      src_.manager().OpenAt(std::move(dir_fd), absl::string_view(filename),
+                            options.mode(), OwnedFd::kDefaultPermissions);
   if (ABSL_PREDICT_FALSE(!status.ok())) {
     FdReaderBase::Reset(kClosed);
     FailWithoutAnnotation(std::move(status));
diff --git a/riegeli/bytes/fd_writer.h b/riegeli/bytes/fd_writer.h
index 0239fac..48f3009 100644
--- a/riegeli/bytes/fd_writer.h
+++ b/riegeli/bytes/fd_writer.h
@@ -787,8 +787,9 @@
           std::enable_if_t<FdSupportsOpenAt<DependentDest>::value, int>>
 void FdWriter<Dest>::OpenAtImpl(UnownedFd dir_fd, PathRef filename,
                                 Options&& options) {
-  absl::Status status = dest_.manager().OpenAt(
-      std::move(dir_fd), filename, options.mode(), options.permissions());
+  absl::Status status =
+      dest_.manager().OpenAt(std::move(dir_fd), absl::string_view(filename),
+                             options.mode(), options.permissions());
   if (ABSL_PREDICT_FALSE(!status.ok())) {
     FdWriterBase::Reset(kClosed);
     FailWithoutAnnotation(std::move(status));
diff --git a/riegeli/bytes/path_ref.h b/riegeli/bytes/path_ref.h
index dbd54be..0b00245 100644
--- a/riegeli/bytes/path_ref.h
+++ b/riegeli/bytes/path_ref.h
@@ -47,10 +47,11 @@
 // an `absl::string_view`, and the caller might have another representation
 // of the string.
 //
-// It is convertible from:
+// It is implicitly convertible from:
 //  * types convertible to `absl::string_view`
 //  * types convertible to `std::string`, e.g. `PathInitializer`
 //  * `std::filesystem::path`
+//  * `StringRef`
 //
 // For `std::filesystem::path` with `value_type = char`, it refers to
 // `path.native()`.
@@ -59,39 +60,26 @@
 // `path.string()` stored in a storage object passed as a default argument to
 // the constructor.
 //
+// It is explicitly convertible to `absl::string_view`, `std::string`, or
+// `StringRef`.
+//
 // `PathRef` does not own path contents and is efficiently copyable.
-class PathRef : public StringRef, public WithCompare<PathRef> {
+class PathRef : public StringRefBase, public WithCompare<PathRef> {
  public:
   // Stores an empty `absl::string_view`.
   PathRef() = default;
 
   // Stores `str` converted to `absl::string_view`.
+
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   /*implicit*/ PathRef(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(absl::string_view(str)) {}
+      : StringRefBase(str) {}
 
-  // Stores `str` converted to `StringRef` and then to `absl::string_view`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<NotSameRef<PathRef, T>,
-                                   std::is_convertible<T&&, absl::string_view>>,
-                int> = 0>
-  /*implicit*/ PathRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(std::forward<T>(str)) {}
+  /*implicit*/ PathRef(absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(str) {}
 
-  // Stores `str` materialized, then converted to `StringRef` and then to
-  // `absl::string_view`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<
-                    NotSameRef<PathRef, T>,
-                    std::negation<std::is_convertible<T&&, absl::string_view>>,
-                    std::is_convertible<T&&, std::string>>,
-                int> = 0>
-  /*implicit*/ PathRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
-                       TemporaryStorage<std::string>&& storage
-                           ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : StringRef(std::forward<T>(str), std::move(storage)) {}
+  /*implicit*/ PathRef(StringRef str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(absl::string_view(str)) {}
 
 #if __cpp_lib_filesystem >= 201703
 
@@ -103,7 +91,8 @@
                        int> = 0>
   /*implicit*/ PathRef(
       const std::filesystem::path& path ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : StringRef(static_cast<const DependentPath&>(path).native()) {}
+      : StringRefBase(static_cast<const DependentPath&>(path).native()) {}
+
   template <
       typename DependentPath = std::filesystem::path,
       std::enable_if_t<std::is_same_v<typename DependentPath::value_type, char>,
@@ -123,49 +112,41 @@
   /*implicit*/ PathRef(const std::filesystem::path& path,
                        TemporaryStorage<std::string>&& storage
                            ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : StringRef(std::move(storage).emplace(
-            riegeli::Invoker([&path] { return path.string(); }))) {}
+      : StringRefBase(std::move(storage).emplace(
+            riegeli::Invoker([&] { return path.string(); }))) {}
 
 #endif
 
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<NotSameRef<PathRef, T>,
+                                   std::is_convertible<T&&, absl::string_view>>,
+                int> = 0>
+  /*implicit*/ PathRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : StringRefBase(std::forward<T>(str)) {}
+
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<
+                    NotSameRef<PathRef, T>,
+                    std::negation<std::is_convertible<T&&, absl::string_view>>,
+                    std::is_convertible<T&&, std::string>>,
+                int> = 0>
+  /*implicit*/ PathRef(T&& str, TemporaryStorage<std::string>&& storage
+                                    ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : StringRefBase(std::move(storage).emplace(std::forward<T>(str))) {}
+
   PathRef(const PathRef& that) = default;
   PathRef& operator=(const PathRef&) = delete;
 
+  explicit operator StringRef() const { return absl::string_view(*this); }
+
   friend bool operator==(PathRef a, PathRef b) {
     return absl::string_view(a) == absl::string_view(b);
   }
   friend riegeli::StrongOrdering RIEGELI_COMPARE(PathRef a, PathRef b) {
     return riegeli::Compare(absl::string_view(a), absl::string_view(b));
   }
-
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<PathRef, T>,
-                                          std::is_convertible<T&&, StringRef>>,
-                       int> = 0>
-  friend bool operator==(PathRef a, T&& b) {
-    return a == PathRef(std::forward<T>(b));
-  }
-  template <
-      typename T,
-      std::enable_if_t<std::conjunction_v<NotSameRef<PathRef, T>,
-                                          std::is_convertible<T&&, StringRef>>,
-                       int> = 0>
-  friend riegeli::StrongOrdering RIEGELI_COMPARE(PathRef a, T&& b) {
-    return riegeli::Compare(a, PathRef(std::forward<T>(b)));
-  }
-
-#if __cpp_lib_filesystem >= 201703
-
-  friend bool operator==(PathRef a, const std::filesystem::path& b) {
-    return a == PathRef(b);
-  }
-  friend riegeli::StrongOrdering RIEGELI_COMPARE(
-      PathRef a, const std::filesystem::path& b) {
-    return riegeli::Compare(a, PathRef(b));
-  }
-
-#endif
 };
 
 // `PathInitializer` is convertible from the same types as `PathRef`,
@@ -190,21 +171,29 @@
 
   PathInitializer() = default;
 
-  // Stores `str` converted to `absl::string_view` and then to `std::string`.
+  // Stores `str` converted to `std::string`.
+
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   /*implicit*/ PathInitializer(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND,
                                TemporaryStorage<MakerType<absl::string_view>>&&
                                    storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(std::move(storage).emplace(absl::string_view(str))) {}
+      : PathInitializer(absl::string_view(str), std::move(storage)) {}
 
-  // Stores `str` converted to `std::string`.
-  template <typename T,
-            std::enable_if_t<
-                std::conjunction_v<NotSameRef<PathInitializer, T>,
-                                   std::is_convertible<T&&, std::string>>,
-                int> = 0>
-  /*implicit*/ PathInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
-      : Initializer(std::forward<T>(str)) {}
+  /*implicit*/ PathInitializer(
+      absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      TemporaryStorage<MakerType<absl::string_view>>&& storage
+          ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : Initializer(std::move(storage).emplace(str)) {}
+
+  /*implicit*/ PathInitializer(StringRef str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+                               TemporaryStorage<MakerType<absl::string_view>>&&
+                                   storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : PathInitializer(absl::string_view(str), std::move(storage)) {}
+
+  /*implicit*/ PathInitializer(PathRef str ABSL_ATTRIBUTE_LIFETIME_BOUND,
+                               TemporaryStorage<MakerType<absl::string_view>>&&
+                                   storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
+      : PathInitializer(absl::string_view(str), std::move(storage)) {}
 
 #if __cpp_lib_filesystem >= 201703
   // Stores `path.string()`.
@@ -215,24 +204,26 @@
       : Initializer(std::move(storage).emplace(path)) {}
 #endif
 
-  // Stores `str` converted to `PathRef`, then to `absl::string_view`, and then
-  // to `std::string`.
+  template <typename T,
+            std::enable_if_t<
+                std::conjunction_v<NotSameRef<PathInitializer, T>,
+                                   std::is_convertible<T&&, std::string>>,
+                int> = 0>
+  /*implicit*/ PathInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
+      : Initializer(std::forward<T>(str)) {}
+
   template <
       typename T,
       std::enable_if_t<std::conjunction_v<
                            NotSameRef<PathInitializer, T>,
                            std::negation<std::is_convertible<T&&, std::string>>,
-#if __cpp_lib_filesystem >= 201703
-                           NotSameRef<std::filesystem::path, T>,
-#endif
-                           std::is_convertible<T&&, StringRef>>,
+                           std::is_convertible<T&&, absl::string_view>>,
                        int> = 0>
   /*implicit*/ PathInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
                                TemporaryStorage<MakerType<absl::string_view>>&&
                                    storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
-      : Initializer(
-            std::move(storage).emplace(StringRef(std::forward<T>(str)))) {
-  }
+      : PathInitializer(absl::string_view(std::forward<T>(str)),
+                        std::move(storage)) {}
 
   PathInitializer(PathInitializer&& that) = default;
   PathInitializer& operator=(PathInitializer&&) = delete;
diff --git a/riegeli/bytes/string_reader.h b/riegeli/bytes/string_reader.h
index 2404a1f..b73396e 100644
--- a/riegeli/bytes/string_reader.h
+++ b/riegeli/bytes/string_reader.h
@@ -24,11 +24,15 @@
 
 #include "absl/base/attributes.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/span.h"
 #include "riegeli/base/assert.h"
+#include "riegeli/base/bytes_ref.h"
 #include "riegeli/base/dependency.h"
 #include "riegeli/base/initializer.h"
 #include "riegeli/base/moving_dependency.h"
 #include "riegeli/base/object.h"
+#include "riegeli/base/string_ref.h"
+#include "riegeli/base/type_traits.h"
 #include "riegeli/base/types.h"
 #include "riegeli/bytes/reader.h"
 
@@ -103,6 +107,19 @@
                              int> = 0>
   StringReader();
 
+  // Will read from `absl::string_view(BytesRef(src))`. This constructor is
+  // present only if `Src` is `absl::string_view`.
+  template <
+      typename Arg, typename DependentSrc = Src,
+      std::enable_if_t<
+          std::conjunction_v<
+              NotSameRef<StringReader, Arg>,
+              std::is_same<DependentSrc, absl::string_view>,
+              std::negation<std::is_convertible<const Arg&, Initializer<Src>>>,
+              std::is_convertible<const Arg&, BytesRef>>,
+          int> = 0>
+  explicit StringReader(const Arg& src);
+
   // Will read from `absl::string_view(src, size)`. This constructor is present
   // only if `Src` is `absl::string_view`.
   template <typename DependentSrc = Src,
@@ -122,6 +139,16 @@
             std::enable_if_t<std::is_same_v<DependentSrc, absl::string_view>,
                              int> = 0>
   ABSL_ATTRIBUTE_REINITIALIZES void Reset();
+  template <
+      typename Arg, typename DependentSrc = Src,
+      std::enable_if_t<
+          std::conjunction_v<
+              NotSameRef<StringReader, Arg>,
+              std::is_same<DependentSrc, absl::string_view>,
+              std::negation<std::is_convertible<const Arg&, Initializer<Src>>>,
+              std::is_convertible<const Arg&, BytesRef>>,
+          int> = 0>
+  ABSL_ATTRIBUTE_REINITIALIZES void Reset(const Arg& src);
   template <typename DependentSrc = Src,
             std::enable_if_t<std::is_same_v<DependentSrc, absl::string_view>,
                              int> = 0>
@@ -149,10 +176,13 @@
 explicit StringReader(Closed) -> StringReader<DeleteCtad<Closed>>;
 template <typename Src>
 explicit StringReader(Src&& src) -> StringReader<std::conditional_t<
-    std::disjunction_v<
-        std::conjunction<std::is_lvalue_reference<Src>,
-                         std::is_convertible<Src, absl::string_view>>,
-        std::is_convertible<Src&&, const char*>>,
+    std::disjunction_v<std::is_convertible<const Src&, const char*>,
+                       std::conjunction<std::is_lvalue_reference<Src>,
+                                        std::is_convertible<Src, BytesRef>>,
+                       std::is_same<std::decay_t<Src>, StringRef>,
+                       std::is_same<std::decay_t<Src>, BytesRef>,
+                       std::is_same<std::decay_t<Src>, absl::Span<char>>,
+                       std::is_same<std::decay_t<Src>, absl::Span<const char>>>,
     absl::string_view, TargetT<Src>>>;
 StringReader() -> StringReader<>;
 explicit StringReader(const char* src, size_t size) -> StringReader<>;
@@ -215,6 +245,19 @@
 
 template <typename Src>
 template <
+    typename Arg, typename DependentSrc,
+    std::enable_if_t<
+        std::conjunction_v<
+            NotSameRef<StringReader<Src>, Arg>,
+            std::is_same<DependentSrc, absl::string_view>,
+            std::negation<std::is_convertible<const Arg&, Initializer<Src>>>,
+            std::is_convertible<const Arg&, BytesRef>>,
+        int>>
+inline StringReader<Src>::StringReader(const Arg& src)
+    : StringReader(absl::string_view(BytesRef(src))) {}
+
+template <typename Src>
+template <
     typename DependentSrc,
     std::enable_if_t<std::is_same_v<DependentSrc, absl::string_view>, int>>
 inline StringReader<Src>::StringReader(
@@ -244,6 +287,20 @@
 
 template <typename Src>
 template <
+    typename Arg, typename DependentSrc,
+    std::enable_if_t<
+        std::conjunction_v<
+            NotSameRef<StringReader<Src>, Arg>,
+            std::is_same<DependentSrc, absl::string_view>,
+            std::negation<std::is_convertible<const Arg&, Initializer<Src>>>,
+            std::is_convertible<const Arg&, BytesRef>>,
+        int>>
+inline void StringReader<Src>::Reset(const Arg& src) {
+  Reset(absl::string_view(BytesRef(src)));
+}
+
+template <typename Src>
+template <
     typename DependentSrc,
     std::enable_if_t<std::is_same_v<DependentSrc, absl::string_view>, int>>
 inline void StringReader<Src>::Reset(const char* src, size_t size) {
diff --git a/riegeli/bytes/writer.h b/riegeli/bytes/writer.h
index 1527bbd..c7087c9 100644
--- a/riegeli/bytes/writer.h
+++ b/riegeli/bytes/writer.h
@@ -664,8 +664,7 @@
     move_cursor(src.size());
     return true;
   }
-  AssertInitialized(start(), start_to_cursor());
-  return WriteSlow(src);
+  return WriteSlow(absl::string_view(src));
 }
 
 inline bool Writer::Write(ExternalRef src) {
diff --git a/riegeli/chunk_encoding/deferred_encoder.cc b/riegeli/chunk_encoding/deferred_encoder.cc
index 15aa45b..a0a267f 100644
--- a/riegeli/chunk_encoding/deferred_encoder.cc
+++ b/riegeli/chunk_encoding/deferred_encoder.cc
@@ -71,7 +71,7 @@
 }
 
 bool DeferredEncoder::AddRecord(BytesRef record) {
-  return AddRecordImpl(record);
+  return AddRecordImpl(absl::string_view(record));
 }
 
 bool DeferredEncoder::AddRecord(ExternalRef record) {
diff --git a/riegeli/chunk_encoding/simple_encoder.cc b/riegeli/chunk_encoding/simple_encoder.cc
index a9c3934..b186742 100644
--- a/riegeli/chunk_encoding/simple_encoder.cc
+++ b/riegeli/chunk_encoding/simple_encoder.cc
@@ -85,7 +85,9 @@
   return true;
 }
 
-bool SimpleEncoder::AddRecord(BytesRef record) { return AddRecordImpl(record); }
+bool SimpleEncoder::AddRecord(BytesRef record) {
+  return AddRecordImpl(absl::string_view(record));
+}
 
 bool SimpleEncoder::AddRecord(ExternalRef record) {
   return AddRecordImpl(std::move(record));
diff --git a/riegeli/digests/digester_handle.h b/riegeli/digests/digester_handle.h
index 2c7c62f..bb9e9b9 100644
--- a/riegeli/digests/digester_handle.h
+++ b/riegeli/digests/digester_handle.h
@@ -163,7 +163,9 @@
 #if __cpp_char8_t
   bool Write(char8_t src) { return Write(static_cast<char>(src)); }
 #endif
-  bool Write(BytesRef src) { return methods()->write(target(), src); }
+  bool Write(BytesRef src) {
+    return methods()->write(target(), absl::string_view(src));
+  }
   ABSL_ATTRIBUTE_ALWAYS_INLINE
   bool Write(const char* src) { return Write(absl::string_view(src)); }
   bool Write(const Chain& src) { return methods()->write_chain(target(), src); }
diff --git a/riegeli/messages/serialized_message_reader.h b/riegeli/messages/serialized_message_reader.h
index e1716e3..510abf4 100644
--- a/riegeli/messages/serialized_message_reader.h
+++ b/riegeli/messages/serialized_message_reader.h
@@ -1121,7 +1121,7 @@
   if constexpr (std::conjunction_v<serialized_message_reader_internal::
                                        IsFieldHandlerFromString<
                                            FieldHandlers, Context...>...>) {
-    return ReadMessageFromString(src, context...);
+    return ReadMessageFromString(absl::string_view(src), context...);
   } else {
     return ReadMessage(StringReader(src), context...);
   }
diff --git a/riegeli/records/record_writer.cc b/riegeli/records/record_writer.cc
index 6649755..23ac9f2 100644
--- a/riegeli/records/record_writer.cc
+++ b/riegeli/records/record_writer.cc
@@ -936,7 +936,7 @@
 
 bool RecordWriterBase::WriteRecord(BytesRef record) {
   if (ABSL_PREDICT_FALSE(!ok())) return false;
-  return WriteRecordImpl(record.size(), record);
+  return WriteRecordImpl(record.size(), absl::string_view(record));
 }
 
 bool RecordWriterBase::WriteRecord(ExternalRef record) {