blob: 8b7eacb1efc152cb2e3db6997e87fc7de02d6000 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_STRINGS_LAZY_STRING_BUILDER_H_
#define BASE_STRINGS_LAZY_STRING_BUILDER_H_
#include <stddef.h>
#include <initializer_list>
#include <list>
#include <string>
#include <string_view>
#include <type_traits>
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/memory/stack_allocated.h"
#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
// Predeclare classes that will have access to LazyStringBuilder.
namespace url {
class Origin;
class SchemeHostPort;
} // namespace url
namespace net {
class HttpCache;
class NetworkIsolationKey;
class SchemefulSite;
} // namespace net
namespace base {
// A string builder that avoids copying the string contents in the normal case.
// This means it is unsafe to use after the underlying string has been deleted.
// There is an AppendByValue() method for when a runtime-allocated string needs
// to be included in the output, but this class should not be used when that is
// the normal case.
//
// In most cases, base::StrCat() should be used directly in preference to this
// class. LazyStringBuilder should only be used when multiple modules need
// to coordinate to assemble a string, and it is critical to reduce the number
// of allocations. For this reason, its use is restricted to the set of classes
// explicitly listed in LazyStringBuilder::AccessKey.
class BASE_EXPORT LazyStringBuilder {
// Prevent objects being allocated on the heap to reduce the risk of
// use-after-free bugs.
STACK_ALLOCATED();
public:
class AccessKey {
public:
~AccessKey() = default;
private:
// Tests should not be added to this list, but use the CreateForTesting()
// static method instead.
friend LazyStringBuilder;
friend url::Origin;
friend url::SchemeHostPort;
friend net::SchemefulSite;
friend net::NetworkIsolationKey;
friend net::HttpCache;
AccessKey() = default;
AccessKey(const AccessKey&) = default;
AccessKey& operator=(const AccessKey&) = default;
};
// Tests should acquire a LazyStringBuilder like
// auto builder = LazyStringBuilder::CreateForTesting();
// This should never be called in production code.
static LazyStringBuilder CreateForTesting();
// Construct an empty object.
explicit LazyStringBuilder(AccessKey);
~LazyStringBuilder();
// Since `views_` can point into `scratch_`, it is not trivial to copy this
// type.
LazyStringBuilder(const LazyStringBuilder&) = delete;
LazyStringBuilder& operator=(const LazyStringBuilder&) = delete;
// LazyStringBuilder needs to be movable for CreateForTesting() to work.
LazyStringBuilder(LazyStringBuilder&&);
LazyStringBuilder& operator=(LazyStringBuilder&&);
// Appends a single std::string_view to the output string. The contents of
// `view` are referenced, not copied. Can be used with std::strings as long as
// they outlive this object.
void AppendByReference(std::string_view view LIFETIME_CAPTURE_BY(this));
// Prevent AppendByReference() from being called for rvalue strings, as it
// would result in a dangling reference. Use AppendByValue() instead.
void AppendByReference(std::string&&) = delete;
// This overload is needed to disambiguate calls to AppendByReference() with
// string constants.
void AppendByReference(const char* str LIFETIME_CAPTURE_BY(this)) {
AppendByReference(std::string_view(str));
}
// Moves `string` into this object. Memory-safe but inefficient, so usage
// should be rare.
void AppendByValue(std::string string);
// Appends multiple string_views to the output string, eg.
// string_view_joiner.AppendByReference(view1, " ", view2);
//
// This is more efficient than appending them one at a time. This cannot be
// used for runtime-allocated strings; they must use AppendByValue().
template <typename... T>
requires(sizeof...(T) > 1 &&
(std::convertible_to<T, std::string_view> && ...) &&
(!std::same_as<T, std::string> && ...))
void AppendByReference(T&&... views LIFETIME_CAPTURE_BY(this)) {
AppendInternal({std::string_view(views)...});
}
// Copies and returns the output string.
std::string Build() const;
private:
// This is private because the public API makes it easier to check that no-one
// is passing in rvalue strings, but we want to make the actual implementation
// out-of-line to avoid exploding binary size.
void AppendInternal(std::initializer_list<std::string_view> views);
// The views that have been appended to the object. Heap memory need not be
// allocated unless there are more than 32.
absl::InlinedVector<std::string_view, 32u> views_;
// Any strings that have been passed to AppendByValue(). These are referenced
// by `views_`. std::list rather than vector because pointer stability is
// required.
std::list<std::string> scratch_;
};
} // namespace base
#endif // BASE_STRINGS_LAZY_STRING_BUILDER_H_