libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
writer.hpp
1#pragma once
2
3#include <array>
4#include <bit>
5#include <cmath>
6#include <concepts>
7#include <cstddef>
8#include <cstdint>
9#include <cstring>
10#include <limits>
11#include <string>
12#include <string_view>
13#include <tuple>
14#include <type_traits>
15#include <utility>
16
17#include "../libxr_def.hpp"
18#include "format_argument.hpp"
19#include "format_protocol.hpp"
20#include "print_contract.hpp"
21
22namespace LibXR::Print
23{
24namespace Detail::WriterFloatLimit
25{
32[[nodiscard]] constexpr size_t DecimalDigitCount(size_t value)
34 size_t digits = 1;
35 while (value >= 10)
36 {
37 value /= 10;
38 ++digits;
39 }
40 return digits;
41}
42
49template <typename Float>
50[[nodiscard]] constexpr size_t FixedTextLimit()
51{
52 constexpr size_t decimal_point =
53 (Config::max_float_precision != 0 || Config::enable_alternate) ? 1U : 0U;
54 return Config::max_float_integer_digits + decimal_point +
55 static_cast<size_t>(Config::max_float_precision);
56}
57
64template <typename Float>
65[[nodiscard]] constexpr size_t ScientificTextLimit()
66{
67 constexpr size_t decimal_point =
68 (Config::max_float_precision != 0 || Config::enable_alternate) ? 1U : 0U;
69 constexpr size_t exponent_digits =
70 DecimalDigitCount(static_cast<size_t>(std::numeric_limits<Float>::max_exponent10));
71 return 1U + decimal_point + static_cast<size_t>(Config::max_float_precision) + 2U +
72 exponent_digits;
73}
74
81template <typename Float>
82[[nodiscard]] constexpr size_t FloatTextLimit()
83{
84 constexpr size_t fixed_limit = FixedTextLimit<Float>();
85 constexpr size_t scientific_limit = ScientificTextLimit<Float>();
86 return fixed_limit > scientific_limit ? fixed_limit : scientific_limit;
87}
88
94[[nodiscard]] constexpr size_t ComputeBufferCapacity()
95{
96 size_t capacity = 4;
97 if constexpr (Config::enable_float_fixed || Config::enable_float_scientific ||
98 Config::enable_float_general)
99 {
100 capacity = FloatTextLimit<float>();
101 }
102 if constexpr (Config::enable_float_double)
103 {
104 constexpr size_t double_limit = FloatTextLimit<double>();
105 capacity = capacity > double_limit ? capacity : double_limit;
106 }
107 if constexpr (Config::enable_float_long_double)
108 {
109 constexpr size_t long_double_limit = FloatTextLimit<long double>();
110 capacity = capacity > long_double_limit ? capacity : long_double_limit;
112 return capacity;
113}
114} // namespace Detail::WriterFloatLimit
115
121{
122 public:
145 template <typename Sink, typename Format, auto ArgumentOrder, typename... Args>
147 [[nodiscard]] static ErrorCode RunArgumentOrder(Sink& sink, const Format&,
148 Args&&... args)
149 {
150 using Built = std::remove_cvref_t<Format>;
151 static_assert(ArgumentOrder.size() == Built::ArgumentList().size(),
152 "LibXR::Print::Writer: argument reorder list must match the "
153 "compiled field count");
154 static_assert(
155 Built::template Matches<Args...>(),
156 "LibXR::Print::Writer::RunArgumentOrder: format arguments do not match");
157
158 return RunTaggedArgumentOrder<Sink, Built::ArgumentList(), ArgumentOrder,
159 Built::Profile()>(sink, Built::Codes().data(),
160 std::forward<Args>(args)...);
161 }
162
163 private:
168 static constexpr uint8_t unspecified_precision = 0xFF;
169 template <FormatPackKind K>
170 static constexpr bool dependent_false_v = false;
171 static constexpr size_t float_buffer_capacity =
172 Detail::WriterFloatLimit::ComputeBufferCapacity();
173
178 [[nodiscard]] static constexpr bool HasFlag(uint8_t flags, uint8_t bit)
179 {
180 return (flags & bit) != 0;
181 }
182
187 struct Spec
188 {
189 uint8_t flags = 0;
190 char fill = ' ';
191 uint8_t width = 0;
192 uint8_t precision = unspecified_precision;
194
200 [[nodiscard]] constexpr bool LeftAlign() const
201 {
202 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::LeftAlign));
203 }
204
211 [[nodiscard]] constexpr bool ForceSign() const
212 {
213 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::ForceSign));
214 }
215
221 [[nodiscard]] constexpr bool CenterAlign() const
222 {
223 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::CenterAlign));
224 }
225
232 [[nodiscard]] constexpr bool SpaceSign() const
233 {
234 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::SpaceSign));
235 }
236
242 [[nodiscard]] constexpr bool Alternate() const
243 {
244 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::Alternate));
245 }
246
252 [[nodiscard]] constexpr bool ZeroPad() const
253 {
254 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::ZeroPad));
255 }
256
262 [[nodiscard]] constexpr bool UpperCase() const
263 {
264 return Writer::HasFlag(flags, static_cast<uint8_t>(FormatFlag::UpperCase));
265 }
266
272 [[nodiscard]] constexpr bool HasPrecision() const
273 {
274 return precision != unspecified_precision;
275 }
276 };
277
285 template <size_t N>
286 [[nodiscard]] static constexpr size_t BoundedTextLength(const char (&text)[N]) noexcept;
287
295 template <typename T>
296 [[nodiscard]] static constexpr std::string_view ToStringView(const T& text);
297
306 template <FormatPackKind pack, typename T>
307 [[nodiscard]] static constexpr auto PackValue(T&& value);
308
316 template <typename T>
317 static void StoreArgument(uint8_t*& out, const T& value);
318
325 template <auto ArgumentInfoList>
326 [[nodiscard]] static consteval size_t PackedArgumentBytes();
327
339 template <auto ArgumentInfoList, auto ArgumentOrder, typename Tuple>
340 static void StoreArgumentsOrdered(uint8_t*& out, Tuple& tuple);
341
358 template <std::unsigned_integral UInt, uint8_t Base>
359 [[nodiscard]] static consteval size_t UnsignedDigitCapacity();
360
373 template <uint8_t Base, bool UpperCase = false, size_t N, std::unsigned_integral UInt>
374 [[nodiscard]] static size_t AppendUnsigned(char (&out)[N], UInt value);
375
383 template <size_t N>
384 [[nodiscard]] static size_t AppendSmallUnsigned(char (&out)[N], uint8_t value);
385
393 [[nodiscard]] static constexpr size_t FieldPadding(uint8_t width, size_t payload_size);
394
402 [[nodiscard]] static constexpr size_t IntegerPrecisionZeros(const Spec& spec,
403 size_t digit_count);
404
414 template <std::unsigned_integral UInt>
415 [[nodiscard]] static constexpr std::string_view IntegerPrefix(FormatType type,
416 const Spec& spec,
417 UInt value);
418
436 template <std::unsigned_integral UInt>
437 [[nodiscard]] static size_t ApplyAlternateOctal(char* digits, size_t digit_count,
438 const Spec& spec, UInt value);
439
447 [[nodiscard]] static constexpr bool UsesFloatTextBackend(FormatType type);
448
456 [[nodiscard]] static constexpr bool FloatEnabled(FormatType type);
462 [[nodiscard]] static constexpr uint8_t DefaultFloatPrecision();
473 template <typename Float>
474 [[nodiscard]] static bool ExceedsFixedIntegerDigits(Float value, uint8_t precision);
475
486 [[nodiscard]] static bool AppendBufferChar(char* buffer, size_t capacity, size_t& size,
487 char ch);
488
499 [[nodiscard]] static bool AppendBufferText(char* buffer, size_t capacity, size_t& size,
500 std::string_view text);
501
513 [[nodiscard]] static bool AppendBufferU32ZeroPad(char* buffer, size_t capacity,
514 size_t& size, uint32_t value,
515 uint8_t width);
516
517#if LIBXR_PRINT_ENABLE_FLOAT
523 template <typename Float>
524 struct DecimalScale;
525
531 template <typename Float>
532 struct ScientificDigits;
533
541 template <typename Float>
542 [[nodiscard]] static Float Power10(int exponent);
543
553 template <typename Float>
554 [[nodiscard]] static Float RoundDecimal(Float value, uint8_t precision);
555
564 template <typename Float>
565 [[nodiscard]] static ScientificDigits<Float> RoundScientificDigits(Float value,
566 uint8_t precision);
567
575 template <typename Float>
576 [[nodiscard]] static DecimalScale<Float> NormalizeDecimal(Float value);
577
586 template <typename Float>
587 [[nodiscard]] static uint8_t ExtractDigit(Float& value, Float scale);
588
597 [[nodiscard]] static size_t TrimGeneralText(char* text, size_t size);
598
610 template <typename Float>
611 [[nodiscard]] static bool FormatFixedText(Float value, uint8_t precision,
612 bool alternate, char* out, size_t& out_size);
613
623 [[nodiscard]] static bool AppendExponentText(char* out, size_t& out_size, int exponent,
624 bool upper_case);
625
638 template <typename Float>
639 [[nodiscard]] static bool FormatScientificText(Float value, uint8_t precision,
640 bool alternate, bool upper_case,
641 char* out, size_t& out_size);
642
655 template <typename Float>
656 [[nodiscard]] static bool FormatFloatText(FormatType type, const Spec& spec,
657 Float value, char* out, size_t& out_size);
658#endif
659
660 class CodeReader;
661 class ArgumentReader;
662
663 template <OutputSink Sink>
664 class Executor;
665
677 template <OutputSink Sink, FormatProfile Profile>
678 [[nodiscard]] static LIBXR_NOINLINE ErrorCode Execute(Sink& sink, const uint8_t* codes,
679 const uint8_t* args)
680 {
681 return Executor<Sink>(sink, codes, args).template Run<Profile>();
682 }
683
698 template <OutputSink Sink, auto ArgumentInfoList, auto ArgumentOrder,
699 FormatProfile Profile, typename... Args>
700 [[nodiscard]] static LIBXR_NOINLINE ErrorCode
701 RunTaggedArgumentOrder(Sink& sink, const uint8_t* codes, Args&&... args)
702 {
703 if constexpr (ArgumentInfoList.size() == 0)
704 {
705 return Execute<Sink, Profile>(sink, codes, nullptr);
706 }
707 else
708 {
709 constexpr size_t packed_arg_bytes = PackedArgumentBytes<ArgumentInfoList>();
710 uint8_t packed[packed_arg_bytes];
711 auto tuple = std::forward_as_tuple(std::forward<Args>(args)...);
712 auto* cursor = packed;
713 StoreArgumentsOrdered<ArgumentInfoList, ArgumentOrder>(cursor, tuple);
714 return Execute<Sink, Profile>(sink, codes, packed);
715 }
716 }
717};
718
719// These implementation headers form an ordered dependency chain.
720// clang-format off
721#include "writer/writer_argument.hpp"
722#include "writer/writer_integer.hpp"
723#if LIBXR_PRINT_ENABLE_FLOAT
724#include "writer/writer_float_runtime.hpp"
725#include "writer/writer_float_math.hpp"
726#include "writer/writer_float_fixed.hpp"
727#include "writer/writer_float_general.hpp"
728#endif
729#include "writer/writer_reader.hpp"
730#include "writer/writer_executor.hpp"
731// clang-format on
732} // namespace LibXR::Print
运行期 writer,负责打包调用点参数并执行编译好的字节流 / Runtime writer that packs call-site arguments and executes the compi...
Definition writer.hpp:121
static ErrorCode RunArgumentOrder(Sink &sink, const Format &, Args &&... args)
执行一份编译格式;它的字段顺序与源参数顺序可以不同 / Run one compiled format whose field order and source argument order may d...
Definition writer.hpp:147
Writer 可执行的编译后格式对象 / Compiled format object executable by Writer.
接收编译格式输出的写入端 / Output endpoint accepted by compiled format writers
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes