v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
string-format.h
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_BASE_STRING_FORMAT_H_
6#define V8_BASE_STRING_FORMAT_H_
7
8#include <array>
9#include <cinttypes>
10#include <limits>
11#include <string_view>
12#include <tuple>
13
14#include "src/base/logging.h"
16
17namespace v8::base {
18
19// Implementation detail, do not use outside this header. The public interface
20// is below.
21namespace impl {
22
23template <const std::string_view&... strs>
25 static constexpr auto JoinIntoNullTerminatedArray() noexcept {
26 constexpr size_t kArraySize = (1 + ... + strs.size());
27 std::array<char, kArraySize> arr{};
28 char* ptr = arr.data();
29 for (auto str : std::initializer_list<std::string_view>{strs...}) {
30 for (auto c : str) *ptr++ = c;
31 }
32 *ptr++ = '\0';
33 DCHECK_EQ(arr.data() + arr.size(), ptr);
34 return arr;
35 }
36
37 // Store in an array with static linkage, so we can reference it from the
38 // {std::string_view} below.
39 static constexpr auto array = JoinIntoNullTerminatedArray();
40
41 // Create a string view to the null-terminated array. The null byte is not
42 // included.
43 static constexpr std::string_view string_view = {array.data(),
44 array.size() - 1};
45};
46
47template <typename T>
49 std::is_bounded_array_v<T> &&
50 std::is_same_v<char,
51 std::remove_cv_t<std::remove_pointer_t<std::decay_t<T>>>>;
52
53template <typename T>
54struct FormattedStringPart; // Specializations below.
55
56// Use a single implementation for all integral types; this avoids hassle with
57// int32_t, int64_t, long, long long sometimes being the same types and
58// sometimes not, depending on the platform.
59template <typename I>
60 requires std::is_integral_v<I>
62 static_assert(sizeof(I) == 4 || sizeof(I) == 8);
63 constexpr static bool kIs64Bit = sizeof(I) == 8;
64 constexpr static bool kIsSigned = std::is_signed_v<I>;
65 static constexpr int kMaxLen = (kIs64Bit ? 20 : 10) + (kIsSigned ? 1 : 0);
66 static constexpr std::string_view kFormats[2][2]{{"%" PRIu32, "%" PRId32},
67 {"%" PRIu64, "%" PRId64}};
68 static constexpr std::string_view kFormatPart = kFormats[kIs64Bit][kIsSigned];
69
71 std::conditional_t<kIs64Bit,
72 std::conditional_t<kIsSigned, int64_t, uint64_t>,
73 std::conditional_t<kIsSigned, int32_t, uint32_t>>;
75};
76
77template <typename S>
78 requires FixedSizeString<S>
80 static constexpr size_t kCharArraySize = std::extent_v<S>;
81
82 static_assert(kCharArraySize >= 1, "Do not print (static) empty strings");
83 static_assert(kCharArraySize <= 128, "Do not include huge strings");
84 static constexpr int kMaxLen = kCharArraySize - 1;
85 static constexpr std::string_view kFormatPart = "%s";
86
87 const char* value;
88};
89
90template <const std::string_view& kFormat, int kMaxLen, typename... Parts>
91std::array<char, kMaxLen> PrintFormattedStringToArray(Parts... parts) {
92 std::array<char, kMaxLen> message;
93
94 static_assert(kMaxLen > 0);
95 static_assert(
96 kMaxLen < 128,
97 "Don't generate overly large strings; this limit can be increased, but "
98 "consider that the array lives on the stack of the caller.");
99
100 // Add a special case for empty strings, because compilers complain about
101 // empty format strings.
102 static_assert((kFormat.size() == 0) == (sizeof...(Parts) == 0));
103 if constexpr (kFormat.size() == 0) {
104 message[0] = '\0';
105 } else {
106 int characters = base::OS::SNPrintF(message.data(), kMaxLen, kFormat.data(),
107 parts.value...);
108 CHECK(characters >= 0 && characters < kMaxLen);
109 DCHECK_EQ('\0', message[characters]);
110 }
111
112 return message;
113}
114
115} // namespace impl
116
117// `FormattedString` allows to format strings with statically known number and
118// type of constituents.
119// The class stores all values that should be printed, and generates the final
120// string via `SNPrintF` into a `std::array`, without any dynamic memory
121// allocation. The format string is computed statically.
122// This makes this class not only very performant, but also suitable for
123// situations where we do not want to perform any memory allocation (like for
124// reporting OOM or fatal errors).
125//
126// Use like this:
127// auto message = FormattedString{} << "Cannot allocate " << size << " bytes";
128// V8::FatalProcessOutOfMemory(nullptr, message.PrintToArray().data());
129//
130// This code is compiled into the equivalent of
131// std::array<char, 34> message_arr;
132// int chars = SNPrintF(message_arr.data(), 34, "%s%d%s", "Cannot allocate ",
133// size, " bytes");
134// CHECK(chars >= 0 && chars < 34);
135// V8::FatalProcessOutOfMemory(nullptr, message_arr.data());
136template <typename... Ts>
138 template <typename T>
140
141 static_assert(std::conjunction_v<std::is_trivial<Part<Ts>>...>,
142 "All parts needs to be trivial to guarantee optimal code");
143
144 public:
145 static constexpr int kMaxLen = (1 + ... + Part<Ts>::kMaxLen);
146 static constexpr std::string_view kFormat =
148
150 static_assert(sizeof...(Ts) == 0,
151 "Only explicitly construct empty FormattedString, use "
152 "operator<< to appending");
153 }
154
155 // Add one more part to the FormattedString. Only allowed on r-value ref (i.e.
156 // temporary object) to avoid misuse like `FormattedString<> str; str << 3;`
157 // instead of `auto str = FormattedString{} << 3;`.
158 template <typename T>
159 V8_WARN_UNUSED_RESULT auto operator<<(T&& t) const&& {
160 using PlainT = std::remove_cv_t<std::remove_reference_t<T>>;
161 return FormattedString<Ts..., PlainT>{
162 std::tuple_cat(parts_, std::make_tuple(Part<PlainT>{t}))};
163 }
164
165 // Print this FormattedString into an array. Does not allocate any dynamic
166 // memory. The result lives on the stack of the caller.
167 V8_INLINE V8_WARN_UNUSED_RESULT std::array<char, kMaxLen> PrintToArray()
168 const {
169 return std::apply(
171 parts_);
172 }
173
174 private:
175 template <typename... Us>
176 friend class FormattedString;
177
178 explicit FormattedString(std::tuple<Part<Ts>...> parts) : parts_(parts) {}
179
180 std::tuple<Part<Ts>...> parts_;
181};
182
183// Add an explicit deduction guide for empty template parameters (fixes
184// clang's -Wctad-maybe-unsupported warning). Non-empty formatted strings
185// explicitly declare template parameters anyway.
186FormattedString()->FormattedString<>;
187
188} // namespace v8::base
189
190#endif // V8_BASE_STRING_FORMAT_H_
std::tuple< Part< Ts >... > parts_
static constexpr std::string_view kFormat
FormattedString(std::tuple< Part< Ts >... > parts)
V8_INLINE V8_WARN_UNUSED_RESULT std::array< char, kMaxLen > PrintToArray() const
static constexpr int kMaxLen
V8_WARN_UNUSED_RESULT auto operator<<(T &&t) const &&
std::array< char, kMaxLen > PrintFormattedStringToArray(Parts... parts)
FormattedString() -> FormattedString<>
#define I(name, number_of_args, result_size)
Definition runtime.cc:36
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
std::conditional_t< kIs64Bit, std::conditional_t< kIsSigned, int64_t, uint64_t >, std::conditional_t< kIsSigned, int32_t, uint32_t > > StorageType
static constexpr auto JoinIntoNullTerminatedArray() noexcept
static constexpr std::string_view string_view
#define V8_INLINE
Definition v8config.h:500
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671