55 "LibXR::Print::FormatCompiler: frontend must expose ErrorType, "
56 "SourceData(), and SourceSize()");
58 using Error =
typename Frontend::ErrorType;
66 template <
size_t BlobBytes,
size_t ArgCount>
70 std::array<uint8_t, BlobBytes>;
71 using ArgumentListData =
79 FormatProfile profile =
85 template <
size_t BlobBytes,
size_t ArgCount>
86 using Result = ResultData<BlobBytes, ArgCount>;
92 static constexpr size_t inline_text_limit = 2 *
sizeof(size_t) - 1;
103 static constexpr size_t max_code_bytes = 3 * Frontend::SourceSize() + 1;
104 static constexpr size_t max_text_pool_bytes = Frontend::SourceSize();
105 static constexpr size_t max_arg_count = Frontend::SourceSize();
106 static constexpr uint8_t unspecified_precision = std::numeric_limits<uint8_t>::max();
115 static consteval void EmitByte(
auto& data,
size_t& out, uint8_t value)
129 template <
typename T>
130 static consteval void EmitNative(
auto& data,
size_t& out, T value)
132 auto bytes = std::bit_cast<std::array<uint8_t,
sizeof(T)>>(value);
133 for (
auto byte : bytes)
148 template <
typename T>
149 [[nodiscard]]
static consteval T ReadNative(
const auto& data,
size_t& pos)
151 std::array<uint8_t,
sizeof(T)> bytes{};
152 for (
size_t i = 0; i <
sizeof(T); ++i)
154 bytes[i] = data[pos++];
156 return std::bit_cast<T>(bytes);
165 [[nodiscard]]
static consteval auto Failed(Error error)
167 Result<1, 0> result{};
168 result.compile_error = error;
179 [[nodiscard]]
static consteval FormatProfile ProfileForOp(FormatOp op)
183 case FormatOp::U32Dec:
184 case FormatOp::U32ZeroPadWidth:
185 case FormatOp::I32Dec:
186 case FormatOp::U32Binary:
187 case FormatOp::U32Octal:
188 case FormatOp::U32HexLower:
189 case FormatOp::U32HexUpper:
190 return FormatProfile::NarrowInt;
191 case FormatOp::StringRaw:
192 case FormatOp::CharacterRaw:
193 return FormatProfile::TextArg;
194 case FormatOp::GenericField:
195 return FormatProfile::None;
196 case FormatOp::TextInline:
197 case FormatOp::TextRef:
198 case FormatOp::TextSpace:
200 return FormatProfile::None;
203 return FormatProfile::None;
210 [[nodiscard]]
static consteval FormatOp FastFieldOp(
const FormatField& field)
212 const bool raw_integer_field = IsRawIntegerField(field);
213 const bool raw_text_field = IsRawTextField(field);
215 if (raw_integer_field && field.
type == FormatType::Signed32 &&
216 field.
pack == FormatPackKind::I32)
218 return FormatOp::I32Dec;
221 if (field.
type == FormatType::Unsigned32 && field.
pack == FormatPackKind::U32 &&
224 return FormatOp::U32Dec;
227 if (raw_integer_field && field.
type == FormatType::Binary32 &&
228 field.
pack == FormatPackKind::U32)
230 return FormatOp::U32Binary;
233 if (raw_integer_field && field.
type == FormatType::Octal32 &&
234 field.
pack == FormatPackKind::U32)
236 return FormatOp::U32Octal;
239 if (raw_integer_field && field.
type == FormatType::HexLower32 &&
240 field.
pack == FormatPackKind::U32)
242 return FormatOp::U32HexLower;
245 if (raw_integer_field && field.
type == FormatType::HexUpper32 &&
246 field.
pack == FormatPackKind::U32)
248 return FormatOp::U32HexUpper;
251 if (raw_text_field && field.
type == FormatType::Character &&
252 field.
pack == FormatPackKind::Character)
254 return FormatOp::CharacterRaw;
257 if (raw_text_field && field.
type == FormatType::String &&
258 field.
pack == FormatPackKind::StringView)
260 return FormatOp::StringRaw;
263 if (field.
type == FormatType::Unsigned32 && field.
pack == FormatPackKind::U32 &&
264 field.
flags ==
static_cast<uint8_t
>(FormatFlag::ZeroPad) && field.
fill ==
' ' &&
267 return FormatOp::U32ZeroPadWidth;
270 return FormatOp::GenericField;
277 [[nodiscard]]
static consteval bool IsRawIntegerField(
const FormatField& field)
279 return field.
flags == 0 && field.
fill ==
' ' && field.
width == 0 &&
280 field.
precision == unspecified_precision;
287 [[nodiscard]]
static consteval bool IsRawTextField(
const FormatField& field)
289 return (field.
flags == 0 ||
290 field.
flags ==
static_cast<uint8_t
>(FormatFlag::LeftAlign)) &&
292 field.
precision == unspecified_precision;
299 struct ScratchBuilder
301 std::array<uint8_t, max_code_bytes>
304 std::array<uint8_t, max_text_pool_bytes>
306 std::array<FormatArgumentInfo, max_arg_count>
311 size_t text_pool_bytes = 0;
312 size_t arg_count = 0;
313 FormatProfile profile = FormatProfile::None;
315 Error frontend_error = Error::None;
327 [[nodiscard]]
consteval Error
Text(
size_t offset,
size_t text_size)
334 if (text_size == 1 && Frontend::SourceData()[offset] ==
' ')
336 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(FormatOp::TextSpace));
340 if (text_size <= inline_text_limit)
342 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(FormatOp::TextInline));
343 for (
size_t i = 0; i < text_size; ++i)
345 EmitByte(code_scratch, code_bytes,
346 static_cast<uint8_t
>(Frontend::SourceData()[offset + i]));
348 EmitByte(code_scratch, code_bytes, 0);
352 if (text_pool_bytes > std::numeric_limits<uint16_t>::max())
354 return Error::TextOffsetOverflow;
356 if (text_size > std::numeric_limits<uint16_t>::max())
358 return Error::TextSizeOverflow;
361 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(FormatOp::TextRef));
362 EmitNative(code_scratch, code_bytes,
static_cast<uint16_t
>(text_pool_bytes));
363 EmitNative(code_scratch, code_bytes,
static_cast<uint16_t
>(text_size));
365 for (
size_t i = 0; i < text_size; ++i)
367 text_scratch[text_pool_bytes++] =
368 static_cast<uint8_t
>(Frontend::SourceData()[offset + i]);
380 [[nodiscard]]
consteval Error Field(
const FormatField& field)
382 auto op = FastFieldOp(field);
383 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(op));
387 case FormatOp::U32Dec:
388 case FormatOp::I32Dec:
389 case FormatOp::U32Binary:
390 case FormatOp::U32Octal:
391 case FormatOp::U32HexLower:
392 case FormatOp::U32HexUpper:
393 case FormatOp::StringRaw:
394 case FormatOp::CharacterRaw:
396 case FormatOp::U32ZeroPadWidth:
397 EmitByte(code_scratch, code_bytes, field.
width);
399 case FormatOp::GenericField:
400 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(field.
type));
401 EmitByte(code_scratch, code_bytes, field.
flags);
402 EmitByte(code_scratch, code_bytes,
static_cast<uint8_t
>(field.
fill));
403 EmitByte(code_scratch, code_bytes, field.
width);
404 EmitByte(code_scratch, code_bytes, field.
precision);
406 case FormatOp::TextInline:
407 case FormatOp::TextRef:
408 case FormatOp::TextSpace:
414 op == FormatOp::GenericField ? GenericProfileFor(field.
type) : ProfileForOp(op);
428 [[nodiscard]]
constexpr size_t FinalCodeBytes()
const {
return code_bytes + 1; }
434 [[nodiscard]]
constexpr size_t FinalBlobBytes()
const
436 return FinalCodeBytes() + text_pool_bytes;
454 template <
size_t CodeBytes,
size_t BlobBytes,
size_t ArgCount>
455 [[nodiscard]]
consteval auto Finish()
const
457 Result<BlobBytes, ArgCount> result{};
458 result.profile = profile;
459 size_t scratch_in = 0;
462 while (scratch_in < code_bytes)
464 auto op =
static_cast<FormatOp
>(code_scratch[scratch_in++]);
465 EmitByte(result.codes, code_out,
static_cast<uint8_t
>(op));
467 if (op == FormatOp::TextInline)
471 uint8_t
byte = code_scratch[scratch_in++];
472 EmitByte(result.codes, code_out,
byte);
481 const size_t payload_bytes = FormatOpPayloadBytes(op);
482 if (payload_bytes == 0)
487 if (op == FormatOp::TextRef)
489 auto relative_offset = ReadNative<uint16_t>(code_scratch, scratch_in);
490 auto text_size = ReadNative<uint16_t>(code_scratch, scratch_in);
491 size_t absolute_offset = CodeBytes + relative_offset;
492 if (absolute_offset > std::numeric_limits<uint16_t>::max())
494 result.compile_error = Error::TextOffsetOverflow;
497 EmitNative(result.codes, code_out,
static_cast<uint16_t
>(absolute_offset));
498 EmitNative(result.codes, code_out, text_size);
502 if (payload_bytes == 1)
504 EmitByte(result.codes, code_out, code_scratch[scratch_in++]);
508 if (op == FormatOp::GenericField)
510 for (
size_t i = 0; i < payload_bytes; ++i)
512 EmitByte(result.codes, code_out, code_scratch[scratch_in++]);
517 EmitByte(result.codes, code_out,
static_cast<uint8_t
>(FormatOp::End));
519 for (
size_t i = 0; i < text_pool_bytes; ++i)
521 result.codes[CodeBytes + i] = text_scratch[i];
523 for (
size_t i = 0; i < ArgCount; ++i)
525 result.arg_info[i] = arg_scratch[i];
533 "LibXR::Print::FormatCompiler: frontend Walk(visitor) must accept "
534 "the shared builder and return ErrorType");
546 constexpr auto scratch = []()
consteval
548 ScratchBuilder builder{};
549 builder.frontend_error = Frontend::Walk(builder);
553 if constexpr (scratch.frontend_error != Error::None)
555 return Failed(scratch.frontend_error);
559 return scratch.template Finish<scratch.FinalCodeBytes(), scratch.FinalBlobBytes(),
560 scratch.arg_count>();