quill
DirectFormatCodec.h
1 
7 #pragma once
8 
9 #include "quill/bundled/fmt/base.h"
10 #include "quill/core/Attributes.h"
11 #include "quill/core/Codec.h"
12 #include "quill/core/DynamicFormatArgStore.h"
13 #include "quill/core/InlinedVector.h"
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <cstring>
18 #include <string_view>
19 #include <type_traits>
20 
21 QUILL_BEGIN_NAMESPACE
22 
87 QUILL_BEGIN_EXPORT
88 
98 template <typename T>
100 {
101  static size_t compute_encoded_size(quill::detail::SizeCacheVector& conditional_arg_size_cache, T const& arg)
102  {
103  // The computed size includes the size of the length field.
104  // Note: this formats the argument once to measure it, and encode() formats it again to
105  // write the bytes — two formatting passes per argument on the frontend hot path.
106  if constexpr (fmtquill::detail::is_view<T>::value)
107  {
108  return sizeof(uint32_t) +
109  conditional_arg_size_cache.push_back(
110  detail::clamp_encoded_string_length(fmtquill::formatted_size("{}", T{arg})));
111  }
112  else
113  {
114  return sizeof(uint32_t) +
115  conditional_arg_size_cache.push_back(
116  detail::clamp_encoded_string_length(fmtquill::formatted_size("{}", arg)));
117  }
118  }
119 
120  static void encode(std::byte*& buffer, quill::detail::SizeCacheVector const& conditional_arg_size_cache,
121  uint32_t& conditional_arg_size_cache_index, T const& arg)
122  {
123  uint32_t const len = conditional_arg_size_cache[conditional_arg_size_cache_index++];
124  std::memcpy(buffer, &len, sizeof(len));
125  buffer += sizeof(len);
126 
127  if constexpr (fmtquill::detail::is_view<T>::value)
128  {
129  // fmt disallows formatting view types as lvalues; formatting a temporary copy keeps
130  // DirectFormatCodec usable for cheap fmt views such as fmtquill::join_view.
131  QUILL_MAYBE_UNUSED auto const result =
132  fmtquill::format_to_n(reinterpret_cast<char*>(buffer), len, "{}", T{arg});
133  assert_formatted_size_matches_encoded_size(result.size, len);
134  }
135  else
136  {
137  QUILL_MAYBE_UNUSED auto const result =
138  fmtquill::format_to_n(reinterpret_cast<char*>(buffer), len, "{}", arg);
139  assert_formatted_size_matches_encoded_size(result.size, len);
140  }
141 
142  buffer += len;
143  }
144 
145  static std::string_view decode_arg(std::byte*& buffer)
146  {
147  return quill::Codec<std::string>::decode_arg(buffer);
148  }
149 
150  static void decode_and_store_arg(std::byte*& buffer, quill::DynamicFormatArgStore* args_store)
151  {
152  args_store->push_back(decode_arg(buffer));
153  }
154 
155 private:
156  static void assert_formatted_size_matches_encoded_size(QUILL_MAYBE_UNUSED size_t formatted_size,
157  QUILL_MAYBE_UNUSED uint32_t len) noexcept
158  {
159  // The formatter must be pure: the size probed via formatted_size() in
160  // compute_encoded_size() must match what format_to_n() actually produces. A mismatch
161  // means the user's fmtquill::formatter<T> is non-deterministic, which silently
162  // truncates or leaves garbage bytes in the encoded log message.
163  QUILL_ASSERT(static_cast<uint32_t>(formatted_size) == len,
164  "DirectFormatCodec: fmtquill::formatter<T> produced a different byte count "
165  "between formatted_size() and format_to_n(). The formatter must be a pure "
166  "function of its argument");
167  }
168 };
169 
170 // fmt views, e.g. fmtquill::join_view, are provided by optional fmt headers. Detect them
171 // through fmt's view trait instead of naming each view type or including those headers here.
172 template <typename T>
173 struct Codec<T, std::enable_if_t<fmtquill::detail::is_view<T>::value>> : DirectFormatCodec<T>
174 {
175 };
176 
177 QUILL_END_EXPORT
178 
179 QUILL_END_NAMESPACE
Definition: UserDefinedDirectFormatFuzzer.cpp:81
Codec contract notes (apply to this primary template and every specialization, including those in the...
Definition: Codec.h:168
Provides codec functionality for serializing complex user-defined types into strings.
Definition: DirectFormatCodec.h:99