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