FastLED 3.10.8
Loading...
Searching...
No Matches
cstdio.cpp.hpp
Go to the documentation of this file.
1#include "fl/stl/cstdio.h"
2
3#include "fl/stl/stdint.h"
5#include "fl/stl/strstream.h" // For sstream
6
7// Platform-specific I/O function declarations
8// Each platform provides implementations in their .cpp.hpp files
9#include "platforms/io.h"
10#include "fl/stl/noexcept.h"
11
12// Forward declare delay to avoid Arduino conflict
13namespace fl {
15 namespace detail {
16 void delay_impl(u32 ms, bool run_async) FL_NO_EXCEPT;
17 }
18}
19
20// =============================================================================
21// Global Log Level Storage and API
22// =============================================================================
23
24namespace fl {
25
26// Default log level is DEBUG (all logging enabled)
28
32
34 gLogLevel = level;
35}
36
37} // namespace fl
38
39namespace fl {
40
41// =============================================================================
42// Print Functions
43// =============================================================================
44
45#ifdef FASTLED_TESTING
46// Static storage for injected handlers using lazy initialization to avoid global constructors
47static print_handler_t& get_print_handler() FL_NO_EXCEPT {
48 static print_handler_t handler;
49 return handler;
50}
51
52static println_handler_t& get_println_handler() FL_NO_EXCEPT {
53 static println_handler_t handler;
54 return handler;
55}
56
57static available_handler_t& get_available_handler() FL_NO_EXCEPT {
58 static available_handler_t handler;
59 return handler;
60}
61
62static read_handler_t& get_read_handler() FL_NO_EXCEPT {
63 static read_handler_t handler;
64 return handler;
65}
66
67static flush_handler_t& get_flush_handler() FL_NO_EXCEPT {
68 static flush_handler_t handler;
69 return handler;
70}
71
72static write_bytes_handler_t& get_write_bytes_handler() FL_NO_EXCEPT {
73 static write_bytes_handler_t handler;
74 return handler;
75}
76#endif
77
78void print(const char* str) FL_NO_EXCEPT {
79 if (!str) return;
80 // Check global log level - if NONE, suppress all output
81 if (gLogLevel == static_cast<u8>(LogLevel::FL_LOG_LEVEL_NONE)) return;
82
83#ifdef FASTLED_TESTING
84 // Check for injected handler first
85 if (get_print_handler()) {
86 get_print_handler()(str);
87 return;
88 }
89#endif
90
91 // Delegate to platform implementation
92 platforms::print(str);
93}
94
95void println(const char* str) FL_NO_EXCEPT {
96 if (!str) return;
97 // Check global log level - if NONE, suppress all output
98 if (gLogLevel == static_cast<u8>(LogLevel::FL_LOG_LEVEL_NONE)) return;
99
100#ifdef FASTLED_TESTING
101 // Check for injected handler first
102 if (get_println_handler()) {
103 get_println_handler()(str);
104 return;
105 }
106#endif
107
108 // Delegate to platform implementation
109 platforms::println(str);
110}
111
113#ifdef FASTLED_TESTING
114 // Check for injected handler first
115 if (get_available_handler()) {
116 return get_available_handler()();
117 }
118#endif
119
120 // Delegate to platform implementation
121 return platforms::available();
122}
123
125 return platforms::peek();
126}
127
129#ifdef FASTLED_TESTING
130 // Check for injected handler first
131 if (get_read_handler()) {
132 return get_read_handler()();
133 }
134#endif
135
136 // Delegate to platform implementation
137 return platforms::read();
138}
139
140bool readStringUntil(sstream& out, char delimiter, char skipChar, fl::optional<u32> timeoutMs) FL_NO_EXCEPT {
141 // Follows Arduino Serial.readStringUntil() API - blocks until delimiter found
142 u32 startTime = fl::millis();
143
144 // Read characters until we find delimiter or timeout
145 while (true) {
146 // Check timeout (only if timeout is set)
147 if (timeoutMs.has_value()) {
148 if (fl::millis() - startTime >= timeoutMs.value()) {
149 // Timeout occurred
150 return false;
151 }
152 }
153
154 // Try to read next character
155 int c = read();
156
157 // Handle -1 (no data available) like Arduino's timedRead():
158 // Keep trying until timeout (or forever if no timeout set)
159 if (c == -1) {
160 // Brief 1us yield to prevent busy loop without the 1ms
161 // minimum sleep that fl::delay(1) imposes on FreeRTOS
162 // (vTaskDelay(1) = 1 tick = 1ms). The 1ms delay was too
163 // slow for USB CDC multi-packet assembly (64-byte packets).
165 continue;
166 }
167
168 // Found delimiter - complete
169 if (c == delimiter) {
170 break;
171 }
172
173 // Skip specified character (e.g., '\r' for cross-platform line endings)
174 if (c == skipChar) {
175 continue;
176 }
177
178 // Valid character - add to output stream
179 out << static_cast<char>(c);
180 }
181
182 // Successfully read until delimiter
183 return true;
184}
185
186fl::optional<fl::string> readLine(char delimiter, char skipChar, fl::optional<u32> timeoutMs) FL_NO_EXCEPT {
187 // Try platform-native line reading first (e.g., Arduino's Serial.readStringUntil).
188 // This is critical for USB CDC platforms (ESP32-C6/S3) where the native
189 // implementation uses yield() (immediate context switch) instead of
190 // delay(1) (1ms FreeRTOS sleep), enabling correct multi-packet assembly.
191 char nativeBuf[512];
192 int nativeLen = platforms::readLineNative(delimiter, nativeBuf, sizeof(nativeBuf));
193 if (nativeLen >= 0) {
194 fl::string result(nativeBuf, nativeLen);
195 return fl::string(result.trim());
196 }
197
198 // Fallback: character-by-character reading for non-Arduino platforms
199 sstream buffer;
200 if (!readStringUntil(buffer, delimiter, skipChar, timeoutMs)) {
201 return fl::nullopt; // Timeout occurred
202 }
203
204 // Convert to string and trim whitespace (trim() returns a new fl::string)
205 fl::string result = buffer.str();
206 return fl::string(result.trim());
207}
208
209bool flush(u32 timeoutMs) FL_NO_EXCEPT {
210#ifdef FASTLED_TESTING
211 if (get_flush_handler()) {
212 return get_flush_handler()(timeoutMs);
213 }
214#endif
215 return platforms::flush(timeoutMs);
216}
217
218size_t write_bytes(const u8* buffer, size_t size) FL_NO_EXCEPT {
219 if (!buffer || size == 0) return 0;
220#ifdef FASTLED_TESTING
221 if (get_write_bytes_handler()) {
222 return get_write_bytes_handler()(buffer, size);
223 }
224#endif
225 return platforms::write_bytes(buffer, size);
226}
227
228#if !FL_SERIAL_BEGIN_INLINE
229void serial_begin(u32 baudRate) FL_NO_EXCEPT {
230 platforms::begin(baudRate);
231}
232#endif
233
235 return platforms::serial_ready();
236}
237
238// =============================================================================
239// Timing Functions
240// =============================================================================
241// Note: fl::millis() is provided by fl/stl/chrono.h
242// Note: fl::delay() is provided by fl/system/delay.h
243
244#ifdef FASTLED_TESTING
245
246// Inject function handlers for testing
247void inject_print_handler(const print_handler_t& handler) FL_NO_EXCEPT {
248 get_print_handler() = handler;
249}
250
251void inject_println_handler(const println_handler_t& handler) FL_NO_EXCEPT {
252 get_println_handler() = handler;
253}
254
255void inject_available_handler(const available_handler_t& handler) FL_NO_EXCEPT {
256 get_available_handler() = handler;
257}
258
259void inject_read_handler(const read_handler_t& handler) FL_NO_EXCEPT {
260 get_read_handler() = handler;
261}
262
263void inject_flush_handler(const flush_handler_t& handler) FL_NO_EXCEPT {
264 get_flush_handler() = handler;
265}
266
267void inject_write_bytes_handler(const write_bytes_handler_t& handler) FL_NO_EXCEPT {
268 get_write_bytes_handler() = handler;
269}
270
271// Clear all injected handlers (restores default behavior)
272void clear_io_handlers() FL_NO_EXCEPT {
273 get_print_handler() = print_handler_t{};
274 get_println_handler() = println_handler_t{};
275 get_available_handler() = available_handler_t{};
276 get_read_handler() = read_handler_t{};
277 get_flush_handler() = flush_handler_t{};
278 get_write_bytes_handler() = write_bytes_handler_t{};
279}
280
281// Clear individual handlers
282void clear_print_handler() FL_NO_EXCEPT {
283 get_print_handler() = print_handler_t{};
284}
285
286void clear_println_handler() FL_NO_EXCEPT {
287 get_println_handler() = println_handler_t{};
288}
289
290void clear_available_handler() FL_NO_EXCEPT {
291 get_available_handler() = available_handler_t{};
292}
293
294void clear_read_handler() FL_NO_EXCEPT {
295 get_read_handler() = read_handler_t{};
296}
297
298void clear_flush_handler() FL_NO_EXCEPT {
299 get_flush_handler() = flush_handler_t{};
300}
301
302void clear_write_bytes_handler() FL_NO_EXCEPT {
303 get_write_bytes_handler() = write_bytes_handler_t{};
304}
305
306// Force early initialization of function-level statics to avoid
307// static initialization order issues on macOS. The fl::function
308// objects may allocate memory during construction, and if the first
309// access happens during a memory allocation callback, we get recursion.
310namespace cstdio_init {
311 void init_io_handlers() FL_NO_EXCEPT {
312 // Touch each handler to force initialization during static construction
313 (void)get_print_handler();
314 (void)get_println_handler();
315 (void)get_available_handler();
316 (void)get_read_handler();
317 (void)get_flush_handler();
318 (void)get_write_bytes_handler();
319 }
320}
321
322FL_INIT(cstdio_init_wrapper, cstdio_init::init_io_handlers)
323
324#endif // FASTLED_TESTING
325
326} // namespace fl
string str() const FL_NO_EXCEPT
Definition strstream.h:43
void delay_impl(u32 ms, bool run_async) FL_NO_EXCEPT
Internal delay implementation used by the public fl::delay wrapper.
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_info.h:57
static u8 gLogLevel
unsigned char u8
Definition stdint.h:131
u8 getLogLevel() FL_NO_EXCEPT
Get the current global log level.
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
void print(const char *str) FL_NO_EXCEPT
bool readStringUntil(sstream &out, char delimiter, char skipChar, fl::optional< u32 > timeoutMs) FL_NO_EXCEPT
bool serial_ready() FL_NO_EXCEPT
void setLogLevel(u8 level) FL_NO_EXCEPT
Set the global log level.
fl::optional< fl::string > readLine(char delimiter, char skipChar, fl::optional< u32 > timeoutMs) FL_NO_EXCEPT
fl::u32 millis() FL_NO_EXCEPT
Universal millisecond timer - returns milliseconds since system startup.
Optional< T > optional
Definition optional.h:16
size_t write_bytes(const u8 *buffer, size_t size) FL_NO_EXCEPT
void serial_begin(u32 baudRate) FL_NO_EXCEPT
int read() FL_NO_EXCEPT
@ FL_LOG_LEVEL_DEBUG
All logging including debug (default)
Definition cstdio.h:42
@ FL_LOG_LEVEL_NONE
No logging (completely silent)
Definition cstdio.h:38
bool flush(u32 timeoutMs) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
constexpr nullopt_t nullopt
Definition optional.h:13
void println(const char *str) FL_NO_EXCEPT
int available() FL_NO_EXCEPT
int peek() FL_NO_EXCEPT
void delayMicroseconds(u32 us) FL_NO_EXCEPT
Delay for a given number of microseconds.
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NO_EXCEPT