FastLED 3.10.8
Loading...
Searching...
No Matches
istream.cpp.hpp
Go to the documentation of this file.
1#include "fl/stl/istream.h"
2#include "fl/math/math.h"
4//#include <stddef.h>
5// <cstdlib> not available on AVR platforms like Arduino UNO
6// We implement custom integer parsing functions instead
7
8#include "fl/math/math.h"
9#include "fl/stl/noexcept.h"
10
11namespace fl {
12
13namespace {
14 // Helper function to check if a character is a digit
15 inline bool isDigit(char c) FL_NO_EXCEPT {
16 return c >= '0' && c <= '9';
17 }
18
19 // Helper function to check if a character is whitespace
20 inline bool isSpace(char c) FL_NO_EXCEPT {
21 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
22 }
23
24 // Custom integer parsing function for signed 32-bit integers
25 bool parse_i32(const char* str, fl::i32& result) FL_NO_EXCEPT {
26 if (!str) return false;
27
28 // Skip leading whitespace
29 while (*str && isSpace(*str)) {
30 str++;
31 }
32
33 if (*str == '\0') return false;
34
35 bool negative = false;
36 if (*str == '-') {
37 negative = true;
38 str++;
39 } else if (*str == '+') {
40 str++;
41 }
42
43 if (*str == '\0' || !isDigit(*str)) return false;
44
45 fl::u32 value = 0;
46 const fl::u32 max_div_10 = 214748364U; // INT32_MAX / 10
47 const fl::u32 max_mod_10 = 7U; // INT32_MAX % 10
48 const fl::u32 max_neg_div_10 = 214748364U; // -INT32_MIN / 10
49 const fl::u32 max_neg_mod_10 = 8U; // -INT32_MIN % 10
50
51 while (*str && isDigit(*str)) {
52 fl::u32 digit = *str - '0';
53
54 // Check for overflow
55 if (negative) {
56 if (value > max_neg_div_10 || (value == max_neg_div_10 && digit > max_neg_mod_10)) {
57 return false; // Overflow
58 }
59 } else {
60 if (value > max_div_10 || (value == max_div_10 && digit > max_mod_10)) {
61 return false; // Overflow
62 }
63 }
64
65 value = value * 10 + digit;
66 str++;
67 }
68
69 // Check if we stopped at a non-digit character (should be end of string for valid parse)
70 if (*str != '\0') return false;
71
72 if (negative) {
73 result = -static_cast<fl::i32>(value);
74 } else {
75 result = static_cast<fl::i32>(value);
76 }
77
78 return true;
79 }
80
81 // Custom integer parsing function for unsigned 32-bit integers
82 bool parse_u32(const char* str, fl::u32& result) FL_NO_EXCEPT {
83 if (!str) return false;
84
85 // Skip leading whitespace
86 while (*str && isSpace(*str)) {
87 str++;
88 }
89
90 if (*str == '\0') return false;
91
92 // Optional '+' sign (no '-' for unsigned)
93 if (*str == '+') {
94 str++;
95 } else if (*str == '-') {
96 return false; // Negative not allowed for unsigned
97 }
98
99 if (*str == '\0' || !isDigit(*str)) return false;
100
101 fl::u32 value = 0;
102 const fl::u32 max_div_10 = 429496729U; // UINT32_MAX / 10
103 const fl::u32 max_mod_10 = 5U; // UINT32_MAX % 10
104
105 while (*str && isDigit(*str)) {
106 fl::u32 digit = *str - '0';
107
108 // Check for overflow
109 if (value > max_div_10 || (value == max_div_10 && digit > max_mod_10)) {
110 return false; // Overflow
111 }
112
113 value = value * 10 + digit;
114 str++;
115 }
116
117 // Check if we stopped at a non-digit character (should be end of string for valid parse)
118 if (*str != '\0') return false;
119
120 result = value;
121 return true;
122 }
123}
124
126// Global cin instance (stub that conditionally delegates).
127// FL_LINT_ALLOW_GLOBAL(public API surface — `fl::cin >> x;` is called by name, same source-compat constraint as `cout` in ostream.cpp.hpp. Real storage IS lazy: `cin` is a small stub, `istream_real` lives inside `cin_real()`'s function-local static below and DOES elide when unused.)
129
130// Function to get singleton instance of istream_real (for better linker elimination)
132 // Local static instance - only constructed when first called
133 // This allows the linker to eliminate it if never referenced
134 static istream_real instance;
135 return instance;
136}
137
139 // If we have no more data available and no buffered data, we're at EOF
140 if (mPos >= mBufferLen && fl::available() == 0) {
141 return false;
142 }
143
144 // Read characters until newline or no more data
145 mBufferLen = 0;
146 while (fl::available() > 0 && mBufferLen < BUFFER_SIZE - 1) {
147 int c = fl::read();
148 if (c == -1) break;
149 if (c == '\n') break;
150 if (c == '\r') continue; // Skip carriage return
151 mBuffer[mBufferLen++] = static_cast<char>(c);
152 }
153
154 // Null terminate the buffer
155 mBuffer[mBufferLen] = '\0';
156 mPos = 0;
157 return true;
158}
159
161 while (mPos < mBufferLen &&
162 (mBuffer[mPos] == ' ' || mBuffer[mPos] == '\t' ||
163 mBuffer[mPos] == '\n' || mBuffer[mPos] == '\r')) {
164 mPos++;
165 }
166
167 // If we've consumed all buffer and there's more input, read more
168 if (mPos >= mBufferLen && fl::available() > 0) {
169 if (readLine()) {
171 }
172 }
173}
174
177
178 if (mPos >= mBufferLen && fl::available() == 0) {
179 mFailed = true;
180 return false;
181 }
182
183 // If buffer is empty but data is available, read a line
184 if (mPos >= mBufferLen && fl::available() > 0) {
185 if (!readLine()) {
186 mFailed = true;
187 return false;
188 }
190 }
191
192 // Read until whitespace or end of buffer
193 token.clear();
194 while (mPos < mBufferLen &&
195 mBuffer[mPos] != ' ' && mBuffer[mPos] != '\t' &&
196 mBuffer[mPos] != '\n' && mBuffer[mPos] != '\r') {
197 // Explicitly append as a character string to avoid fl::u8->number conversion
198 char ch[2] = {mBuffer[mPos], '\0'};
199 token.append(ch, 1);
200 mPos++;
201 }
202
203 return !token.empty();
204}
205
207 if (!readToken(str)) {
208 mFailed = true;
209 }
210 return *this;
211}
212
215
216 if (mPos >= mBufferLen && fl::available() > 0) {
217 if (!readLine()) {
218 mFailed = true;
219 return *this;
220 }
222 }
223
224 if (mPos < mBufferLen) {
225 c = mBuffer[mPos];
226 mPos++;
227 } else {
228 mFailed = true;
229 }
230 return *this;
231}
232
234 string token;
235 if (readToken(token)) {
236 fl::i32 temp;
237 if (parse_i32(token.c_str(), temp) && temp >= -128 && temp <= 127) {
238 n = static_cast<fl::i8>(temp);
239 } else {
240 mFailed = true;
241 }
242 } else {
243 mFailed = true;
244 }
245 return *this;
246}
247
249 string token;
250 if (readToken(token)) {
251 fl::u32 temp;
252 if (parse_u32(token.c_str(), temp) && temp <= 255) {
253 n = static_cast<fl::u8>(temp);
254 } else {
255 mFailed = true;
256 }
257 } else {
258 mFailed = true;
259 }
260 return *this;
261}
262
264 string token;
265 if (readToken(token)) {
266 fl::i32 temp;
267 if (parse_i32(token.c_str(), temp) && temp >= -32768 && temp <= 32767) {
268 n = static_cast<fl::i16>(temp);
269 } else {
270 mFailed = true;
271 }
272 } else {
273 mFailed = true;
274 }
275 return *this;
276}
277
278// u16 operator>> removed - now handled by template in header
279
281 string token;
282 if (readToken(token)) {
283 if (!parse_i32(token.c_str(), n)) {
284 mFailed = true;
285 }
286 } else {
287 mFailed = true;
288 }
289 return *this;
290}
291
293 string token;
294 if (readToken(token)) {
295 if (!parse_u32(token.c_str(), n)) {
296 mFailed = true;
297 }
298 } else {
299 mFailed = true;
300 }
301 return *this;
302}
303
305 string token;
306 if (readToken(token)) {
307 // Use the existing toFloat() method
308 f = token.toFloat();
309 // Check if parsing was successful by checking for valid float
310 // toFloat() returns 0.0f for invalid input, but we need to distinguish
311 // between actual 0.0f and parse failure
312 if (fl::almost_equal(f, 0.0f) && token != "0" && token != "0.0" && token != "0." && token.find("0") != 0) {
313 mFailed = true;
314 }
315 } else {
316 mFailed = true;
317 }
318 return *this;
319}
320
322 string token;
323 if (readToken(token)) {
324 // Use the existing toFloat() method
325 float f = token.toFloat();
326 d = static_cast<double>(f);
327 // Check if parsing was successful (same logic as float)
328 if (fl::almost_equal(f, 0.0f) && token != "0" && token != "0.0" && token != "0." && token.find("0") != 0) {
329 mFailed = true;
330 }
331 } else {
332 mFailed = true;
333 }
334 return *this;
335}
336
337// fl::size operator>> removed - now handled by template in header
338
340 str.clear();
341
342 // Read from current buffer position to end
343 while (mPos < mBufferLen) {
344 if (mBuffer[mPos] == '\n') {
345 mPos++; // Consume the newline
346 return *this;
347 }
348 // Explicitly append as a character string to avoid fl::u8->number conversion
349 char ch[2] = {mBuffer[mPos], '\0'};
350 str.append(ch, 1);
351 mPos++;
352 }
353
354 // If we need more data, read a new line
355 if (fl::available() > 0) {
356 // Read more characters until newline
357 while (fl::available() > 0) {
358 int c = fl::read();
359 if (c == -1) break;
360 if (c == '\n') break;
361 if (c == '\r') continue; // Skip carriage return
362 // Explicitly append as a character string to avoid fl::u8->number conversion
363 char ch[2] = {static_cast<char>(c), '\0'};
364 str.append(ch, 1);
365 }
366 }
367
368 return *this;
369}
370
372 if (mPos >= mBufferLen && fl::available() > 0) {
373 if (!readLine()) {
374 return -1;
375 }
376 }
377
378 if (mPos < mBufferLen) {
379 return static_cast<int>(static_cast<unsigned char>(mBuffer[mPos++]));
380 }
381
382 // Try to read directly from input if buffer is empty
383 return fl::read();
384}
385
387 if (mPos > 0) {
388 mPos--;
389 mBuffer[mPos] = c;
390 } else {
391 // Insert at beginning of buffer - shift existing data
392 if (mBufferLen < BUFFER_SIZE - 1) {
393 for (fl::size i = mBufferLen; i > 0; --i) {
394 mBuffer[i] = mBuffer[i-1];
395 }
396 mBuffer[0] = c;
397 mBufferLen++;
398 mBuffer[mBufferLen] = '\0';
399 }
400 }
401 return *this;
402}
403
405 if (mPos >= mBufferLen && fl::available() > 0) {
406 if (!readLine()) {
407 return -1;
408 }
409 }
410
411 if (mPos < mBufferLen) {
412 return static_cast<int>(static_cast<unsigned char>(mBuffer[mPos]));
413 }
414
415 return -1;
416}
417
418} // namespace fl
float toFloat() const FL_NO_EXCEPT
fl::size find(const char &value) const FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
fl::size mBufferLen
Definition istream.h:32
fl::size mPos
Definition istream.h:33
int peek() FL_NO_EXCEPT
char mBuffer[BUFFER_SIZE]
Definition istream.h:31
istream_real & getline(string &str) FL_NO_EXCEPT
void skipWhitespace() FL_NO_EXCEPT
istream_real & putback(char c) FL_NO_EXCEPT
bool readToken(string &token) FL_NO_EXCEPT
static const fl::size BUFFER_SIZE
Definition istream.h:25
bool readLine() FL_NO_EXCEPT
istream_real & operator>>(string &str) FL_NO_EXCEPT
int get() FL_NO_EXCEPT
istream_real() FL_NO_EXCEPT=default
#define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
bool parse_i32(const char *str, fl::i32 &result) FL_NO_EXCEPT
bool parse_u32(const char *str, fl::u32 &result) FL_NO_EXCEPT
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
constexpr int type_rank< T >::value
constexpr bool almost_equal(T a, T b, U tolerance) FL_NO_EXCEPT
Definition math.h:98
fl::optional< fl::string > readLine(char delimiter, char skipChar, fl::optional< u32 > timeoutMs) FL_NO_EXCEPT
FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS istream cin
int read() FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
int available() FL_NO_EXCEPT
istream_real & cin_real() FL_NO_EXCEPT
signed char i8
Definition stdint.h:130
Base definition for an LED controller.
Definition crgb.hpp:179