ferrilog_core/encode/mod.rs
1pub mod format;
2pub mod primitives;
3pub mod strings;
4pub mod traits;
5
6pub const ENCODED_SIZE_DYNAMIC: usize = usize::MAX;
7
8pub const FORMAT_ALIGN_NONE: u8 = 0;
9pub const FORMAT_ALIGN_LEFT: u8 = 1;
10pub const FORMAT_ALIGN_RIGHT: u8 = 2;
11pub const FORMAT_ALIGN_CENTER: u8 = 3;
12pub const FORMAT_PRECISION_NONE: usize = usize::MAX;
13
14/// Trait that must be implemented by all types that can be written to the log queue.
15///
16/// Built-in types are implemented by the library; custom types can derive it
17/// via `#[derive(Encode)]`.
18///
19/// ## Three-layer type path
20///
21/// - **Layer 1 (Fast Primitive)**: i32/u64/f64/bool, etc. -- dedicated number_format
22/// - **Layer 2 (Fast String)**: &str/String -- length-prefixed encoding
23/// - **Layer 3 (General Display)**: user-defined types -- Display fallback
24pub trait Encode {
25 /// Compile-time hint for encoded byte width. Dynamic-width types keep the sentinel value.
26 const ENCODED_SIZE_HINT: usize = ENCODED_SIZE_DYNAMIC;
27
28 /// Returns the number of bytes needed to encode this value into the queue.
29 fn encoded_size(&self) -> usize;
30
31 /// Encodes the value at `buffer + offset` (hot path).
32 ///
33 /// # Safety
34 /// `buffer + offset` must have sufficient space (guaranteed by `alloc_message`).
35 unsafe fn encode(&self, buffer: *mut u8, offset: &mut usize);
36
37 /// Encodes the value at a fixed byte offset from `buffer`.
38 ///
39 /// The default implementation forwards to [`Encode::encode`] using a local
40 /// offset variable. Fixed-width types can override this to perform a direct
41 /// write at a compile-time-known payload position.
42 ///
43 /// # Safety
44 /// `buffer + offset` must have sufficient space for the encoded value.
45 #[inline(always)]
46 unsafe fn encode_at_offset(&self, buffer: *mut u8, offset: usize) {
47 let mut offset = offset;
48 unsafe {
49 <Self as Encode>::encode(self, buffer, &mut offset);
50 }
51 }
52
53 /// Decodes from `buffer + offset` and appends the formatted result to `output` (cold path).
54 ///
55 /// `format_spec` is a format specifier such as `".4"` or `"#x"`;
56 /// an empty string means the default format.
57 ///
58 /// # Safety
59 /// `buffer + offset` must point to valid encoded data.
60 unsafe fn decode_and_format(
61 buffer: *const u8,
62 offset: &mut usize,
63 output: &mut Vec<u8>,
64 format_spec: &str,
65 );
66
67 /// Decodes from `buffer + offset` and appends the formatted result.
68 ///
69 /// The default implementation forwards to [`Encode::decode_and_format`]
70 /// using a local mutable offset. Fixed-width types can override this to
71 /// perform a direct read at a compile-time-known payload position.
72 ///
73 /// # Safety
74 /// `buffer + offset` must point to valid encoded data.
75 #[inline(always)]
76 unsafe fn decode_and_format_at_offset(
77 buffer: *const u8,
78 offset: usize,
79 output: &mut Vec<u8>,
80 format_spec: &str,
81 ) {
82 let mut offset = offset;
83 unsafe {
84 <Self as Encode>::decode_and_format(buffer, &mut offset, output, format_spec);
85 }
86 }
87}
88
89/// Extreme-performance encoding path.
90///
91/// Currently selected explicitly by `log_fast!()` / `prewarm_fast!()`;
92/// only supports an empty format specifier by default.
93///
94/// # Safety
95///
96/// Implementors must guarantee that `encode_fast` writes exactly
97/// `encoded_size_fast()` bytes starting at `buffer + *offset`, and that
98/// `decode_and_append_fast` reads the same number of bytes from a buffer
99/// previously written by `encode_fast`.
100pub unsafe trait EncodeFast: Encode {
101 /// Returns the encoded byte size using the fast path (defaults to [`Encode::encoded_size`]).
102 fn encoded_size_fast(&self) -> usize {
103 <Self as Encode>::encoded_size(self)
104 }
105
106 /// Encodes the value using the fast path (defaults to [`Encode::encode`]).
107 ///
108 /// # Safety
109 /// Same requirements as [`Encode::encode`].
110 unsafe fn encode_fast(&self, buffer: *mut u8, offset: &mut usize) {
111 unsafe {
112 <Self as Encode>::encode(self, buffer, offset);
113 }
114 }
115
116 /// Encodes the value using the fast path at a fixed byte offset.
117 ///
118 /// The default implementation forwards to [`Encode::encode_at_offset`],
119 /// which lets fixed-width types benefit from compile-time layout
120 /// specialization without duplicating logic.
121 ///
122 /// # Safety
123 /// Same requirements as [`Encode::encode_at_offset`].
124 #[inline(always)]
125 unsafe fn encode_fast_at_offset(&self, buffer: *mut u8, offset: usize) {
126 unsafe {
127 <Self as Encode>::encode_at_offset(self, buffer, offset);
128 }
129 }
130
131 /// Decodes and appends using the fast path with an empty format specifier
132 /// (defaults to [`Encode::decode_and_format`]).
133 ///
134 /// # Safety
135 /// Same requirements as [`Encode::decode_and_format`].
136 unsafe fn decode_and_append_fast(buffer: *const u8, offset: &mut usize, output: &mut Vec<u8>) {
137 unsafe {
138 <Self as Encode>::decode_and_format(buffer, offset, output, "");
139 }
140 }
141
142 /// Decodes and appends using the fast path from `buffer + offset`.
143 ///
144 /// The default implementation forwards to [`EncodeFast::decode_and_append_fast`]
145 /// using a local mutable offset.
146 ///
147 /// # Safety
148 /// Same requirements as [`EncodeFast::decode_and_append_fast`].
149 #[inline(always)]
150 unsafe fn decode_and_append_fast_at_offset(
151 buffer: *const u8,
152 offset: usize,
153 output: &mut Vec<u8>,
154 ) {
155 let mut offset = offset;
156 unsafe {
157 <Self as EncodeFast>::decode_and_append_fast(buffer, &mut offset, output);
158 }
159 }
160}
161
162// Re-export everything that was previously public from this module.
163pub use format::{VecWriter, append_display_const_default, append_display_with_spec};
164pub use traits::{
165 EncodeBinary, EncodeConstBinaryFormat, EncodeConstDefaultFormat, EncodeConstFloatFormat,
166 EncodeConstLowerHexFormat, EncodeConstOctalFormat, EncodeConstUpperHexFormat, EncodeLowerHex,
167 EncodeOctal, EncodeUpperHex,
168};