10#include "format_protocol.hpp"
11#include "print_contract.hpp"
41template <
typename Frontend>
46 "LibXR::Print::FormatCompiler: frontend must expose ErrorType, "
47 "SourceData(), and SourceSize()");
49 using Error =
typename Frontend::ErrorType;
55 template <
size_t BlobBytes,
size_t ArgCount>
59 std::array<uint8_t, BlobBytes>;
66 FormatProfile
profile = FormatProfile::None;
70 template <
size_t BlobBytes,
size_t ArgCount>
85 static constexpr size_t max_code_bytes = 3 * Frontend::SourceSize() + 1;
86 static constexpr size_t max_text_pool_bytes = Frontend::SourceSize();
87 static constexpr size_t max_arg_count = Frontend::SourceSize();
88 static constexpr uint8_t unspecified_precision =
89 std::numeric_limits<uint8_t>::max();
91 static consteval void EmitByte(
auto& data,
size_t& out, uint8_t value)
97 static consteval void EmitNative(
auto& data,
size_t& out, T value)
99 auto bytes = std::bit_cast<std::array<uint8_t,
sizeof(T)>>(value);
100 for (
auto byte : bytes)
106 template <
typename T>
107 [[nodiscard]]
static consteval T ReadNative(
const auto& data,
size_t& pos)
109 std::array<uint8_t,
sizeof(T)> bytes{};
110 for (
size_t i = 0; i <
sizeof(T); ++i)
112 bytes[i] = data[pos++];
114 return std::bit_cast<T>(bytes);
117 [[nodiscard]]
static consteval auto Failed(Error error)
119 Result<1, 0> result{};
120 result.compile_error = error;
132 case FormatOp::U32Dec:
133 case FormatOp::U32ZeroPadWidth:
134 return FormatProfile::U32;
135 case FormatOp::StringRaw:
136 return FormatProfile::TextArg;
137 case FormatOp::F32FixedPrec:
138 return FormatProfile::F32Fixed;
139 case FormatOp::F64FixedPrec:
140 return FormatProfile::F64Fixed;
141 case FormatOp::GenericField:
142 return FormatProfile::Generic;
143 case FormatOp::TextInline:
144 case FormatOp::TextRef:
145 case FormatOp::TextSpace:
147 return FormatProfile::None;
150 return FormatProfile::None;
159 if (field.
type == FormatType::Unsigned32 && field.
pack == FormatPackKind::U32 &&
161 field.
precision == unspecified_precision)
163 return FormatOp::U32Dec;
166 if (field.
type == FormatType::Unsigned32 && field.
pack == FormatPackKind::U32 &&
167 field.
flags ==
static_cast<uint8_t
>(FormatFlag::ZeroPad) &&
169 field.
precision == unspecified_precision)
171 return FormatOp::U32ZeroPadWidth;
174 if (field.
type == FormatType::String &&
175 field.
pack == FormatPackKind::StringView && field.
flags == 0 &&
179 return FormatOp::StringRaw;
182 if (field.
type == FormatType::FloatFixed && field.
pack == FormatPackKind::F32 &&
184 field.
precision != unspecified_precision)
186 return FormatOp::F32FixedPrec;
189 if (field.
type == FormatType::DoubleFixed && field.
pack == FormatPackKind::F64 &&
191 field.
precision != unspecified_precision)
193 return FormatOp::F64FixedPrec;
196 return FormatOp::GenericField;
219 [[nodiscard]]
consteval Error
Text(
size_t offset,
size_t text_size)
226 if (text_size == 1 && Frontend::SourceData()[offset] ==
' ')
235 for (
size_t i = 0; i < text_size; ++i)
238 static_cast<uint8_t
>(Frontend::SourceData()[offset + i]));
246 return Error::TextOffsetOverflow;
248 if (text_size > std::numeric_limits<uint16_t>::max())
250 return Error::TextSizeOverflow;
257 for (
size_t i = 0; i < text_size; ++i)
260 static_cast<uint8_t
>(Frontend::SourceData()[offset + i]);
278 case FormatOp::U32Dec:
279 case FormatOp::StringRaw:
281 case FormatOp::U32ZeroPadWidth:
284 case FormatOp::F32FixedPrec:
285 case FormatOp::F64FixedPrec:
288 case FormatOp::GenericField:
295 case FormatOp::TextInline:
296 case FormatOp::TextRef:
297 case FormatOp::TextSpace:
329 template <
size_t CodeBytes,
size_t BlobBytes,
size_t ArgCount>
330 [[nodiscard]]
consteval auto Finish()
const
334 size_t scratch_in = 0;
339 auto op =
static_cast<FormatOp
>(
code_scratch[scratch_in++]);
340 EmitByte(result.codes, code_out,
static_cast<uint8_t
>(op));
342 if (op == FormatOp::TextInline)
347 EmitByte(result.codes, code_out,
byte);
356 if (op == FormatOp::TextRef)
358 auto relative_offset = ReadNative<uint16_t>(
code_scratch, scratch_in);
359 auto text_size = ReadNative<uint16_t>(
code_scratch, scratch_in);
360 size_t absolute_offset = CodeBytes + relative_offset;
361 if (absolute_offset > std::numeric_limits<uint16_t>::max())
363 result.compile_error = Error::TextOffsetOverflow;
366 EmitNative(result.codes, code_out,
static_cast<uint16_t
>(absolute_offset));
367 EmitNative(result.codes, code_out, text_size);
371 if (op == FormatOp::U32ZeroPadWidth || op == FormatOp::F32FixedPrec ||
372 op == FormatOp::F64FixedPrec)
374 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
378 if (op == FormatOp::GenericField)
380 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
381 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
382 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
383 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
384 EmitByte(result.codes, code_out,
code_scratch[scratch_in++]);
388 EmitByte(result.codes, code_out,
static_cast<uint8_t
>(FormatOp::End));
394 for (
size_t i = 0; i < ArgCount; ++i)
404 "LibXR::Print::FormatCompiler: frontend Walk(visitor) must accept "
405 "the shared builder and return ErrorType");
414 constexpr auto scratch = []()
consteval {
420 if constexpr (scratch.frontend_error != Error::None)
422 return Failed(scratch.frontend_error);
426 return scratch.template Finish<scratch.FinalCodeBytes(),
427 scratch.FinalBlobBytes(), scratch.arg_count>();