#include "istream.h" #include "fl/math.h" #include "fl/compiler_control.h" //#include // not available on AVR platforms like Arduino UNO // We implement custom integer parsing functions instead #include "fl/math_macros.h" namespace fl { namespace { // Helper function to check if a character is a digit inline bool isDigit(char c) { return c >= '0' && c <= '9'; } // Helper function to check if a character is whitespace inline bool isSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; } // Custom integer parsing function for signed 32-bit integers bool parse_i32(const char* str, fl::i32& result) { if (!str) return false; // Skip leading whitespace while (*str && isSpace(*str)) { str++; } if (*str == '\0') return false; bool negative = false; if (*str == '-') { negative = true; str++; } else if (*str == '+') { str++; } if (*str == '\0' || !isDigit(*str)) return false; fl::u32 value = 0; const fl::u32 max_div_10 = 214748364U; // INT32_MAX / 10 const fl::u32 max_mod_10 = 7U; // INT32_MAX % 10 const fl::u32 max_neg_div_10 = 214748364U; // -INT32_MIN / 10 const fl::u32 max_neg_mod_10 = 8U; // -INT32_MIN % 10 while (*str && isDigit(*str)) { fl::u32 digit = *str - '0'; // Check for overflow if (negative) { if (value > max_neg_div_10 || (value == max_neg_div_10 && digit > max_neg_mod_10)) { return false; // Overflow } } else { if (value > max_div_10 || (value == max_div_10 && digit > max_mod_10)) { return false; // Overflow } } value = value * 10 + digit; str++; } // Check if we stopped at a non-digit character (should be end of string for valid parse) if (*str != '\0') return false; if (negative) { result = -static_cast(value); } else { result = static_cast(value); } return true; } // Custom integer parsing function for unsigned 32-bit integers bool parse_u32(const char* str, fl::u32& result) { if (!str) return false; // Skip leading whitespace while (*str && isSpace(*str)) { str++; } if (*str == '\0') return false; // Optional '+' sign (no '-' for unsigned) if (*str == '+') { str++; } else if (*str == '-') { return false; // Negative not allowed for unsigned } if (*str == '\0' || !isDigit(*str)) return false; fl::u32 value = 0; const fl::u32 max_div_10 = 429496729U; // UINT32_MAX / 10 const fl::u32 max_mod_10 = 5U; // UINT32_MAX % 10 while (*str && isDigit(*str)) { fl::u32 digit = *str - '0'; // Check for overflow if (value > max_div_10 || (value == max_div_10 && digit > max_mod_10)) { return false; // Overflow } value = value * 10 + digit; str++; } // Check if we stopped at a non-digit character (should be end of string for valid parse) if (*str != '\0') return false; result = value; return true; } } FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS // Global cin instance (stub that conditionally delegates) istream cin; // Function to get singleton instance of istream_real (for better linker elimination) istream_real& cin_real() { // Local static instance - only constructed when first called // This allows the linker to eliminate it if never referenced static istream_real instance; return instance; } bool istream_real::readLine() { // If we have no more data available and no buffered data, we're at EOF if (pos_ >= buffer_len_ && fl::available() == 0) { return false; } // Read characters until newline or no more data buffer_len_ = 0; while (fl::available() > 0 && buffer_len_ < BUFFER_SIZE - 1) { int c = fl::read(); if (c == -1) break; if (c == '\n') break; if (c == '\r') continue; // Skip carriage return buffer_[buffer_len_++] = static_cast(c); } // Null terminate the buffer buffer_[buffer_len_] = '\0'; pos_ = 0; return true; } void istream_real::skipWhitespace() { while (pos_ < buffer_len_ && (buffer_[pos_] == ' ' || buffer_[pos_] == '\t' || buffer_[pos_] == '\n' || buffer_[pos_] == '\r')) { pos_++; } // If we've consumed all buffer and there's more input, read more if (pos_ >= buffer_len_ && fl::available() > 0) { if (readLine()) { skipWhitespace(); } } } bool istream_real::readToken(string& token) { skipWhitespace(); if (pos_ >= buffer_len_ && fl::available() == 0) { failed_ = true; return false; } // If buffer is empty but data is available, read a line if (pos_ >= buffer_len_ && fl::available() > 0) { if (!readLine()) { failed_ = true; return false; } skipWhitespace(); } // Read until whitespace or end of buffer token.clear(); while (pos_ < buffer_len_ && buffer_[pos_] != ' ' && buffer_[pos_] != '\t' && buffer_[pos_] != '\n' && buffer_[pos_] != '\r') { // Explicitly append as a character string to avoid fl::u8->number conversion char ch[2] = {buffer_[pos_], '\0'}; token.append(ch, 1); pos_++; } return !token.empty(); } istream_real& istream_real::operator>>(string& str) { if (!readToken(str)) { failed_ = true; } return *this; } istream_real& istream_real::operator>>(char& c) { skipWhitespace(); if (pos_ >= buffer_len_ && fl::available() > 0) { if (!readLine()) { failed_ = true; return *this; } skipWhitespace(); } if (pos_ < buffer_len_) { c = buffer_[pos_]; pos_++; } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(fl::i8& n) { string token; if (readToken(token)) { fl::i32 temp; if (parse_i32(token.c_str(), temp) && temp >= -128 && temp <= 127) { n = static_cast(temp); } else { failed_ = true; } } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(fl::u8& n) { string token; if (readToken(token)) { fl::u32 temp; if (parse_u32(token.c_str(), temp) && temp <= 255) { n = static_cast(temp); } else { failed_ = true; } } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(fl::i16& n) { string token; if (readToken(token)) { fl::i32 temp; if (parse_i32(token.c_str(), temp) && temp >= -32768 && temp <= 32767) { n = static_cast(temp); } else { failed_ = true; } } else { failed_ = true; } return *this; } // u16 operator>> removed - now handled by template in header istream_real& istream_real::operator>>(fl::i32& n) { string token; if (readToken(token)) { if (!parse_i32(token.c_str(), n)) { failed_ = true; } } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(fl::u32& n) { string token; if (readToken(token)) { if (!parse_u32(token.c_str(), n)) { failed_ = true; } } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(float& f) { string token; if (readToken(token)) { // Use the existing toFloat() method f = token.toFloat(); // Check if parsing was successful by checking for valid float // toFloat() returns 0.0f for invalid input, but we need to distinguish // between actual 0.0f and parse failure if (ALMOST_EQUAL_FLOAT(f, 0.0f) && token != "0" && token != "0.0" && token != "0." && token.find("0") != 0) { failed_ = true; } } else { failed_ = true; } return *this; } istream_real& istream_real::operator>>(double& d) { string token; if (readToken(token)) { // Use the existing toFloat() method float f = token.toFloat(); d = static_cast(f); // Check if parsing was successful (same logic as float) if (ALMOST_EQUAL_FLOAT(f, 0.0f) && token != "0" && token != "0.0" && token != "0." && token.find("0") != 0) { failed_ = true; } } else { failed_ = true; } return *this; } // fl::size operator>> removed - now handled by template in header istream_real& istream_real::getline(string& str) { str.clear(); // Read from current buffer position to end while (pos_ < buffer_len_) { if (buffer_[pos_] == '\n') { pos_++; // Consume the newline return *this; } // Explicitly append as a character string to avoid fl::u8->number conversion char ch[2] = {buffer_[pos_], '\0'}; str.append(ch, 1); pos_++; } // If we need more data, read a new line if (fl::available() > 0) { // Read more characters until newline while (fl::available() > 0) { int c = fl::read(); if (c == -1) break; if (c == '\n') break; if (c == '\r') continue; // Skip carriage return // Explicitly append as a character string to avoid fl::u8->number conversion char ch[2] = {static_cast(c), '\0'}; str.append(ch, 1); } } return *this; } int istream_real::get() { if (pos_ >= buffer_len_ && fl::available() > 0) { if (!readLine()) { return -1; } } if (pos_ < buffer_len_) { return static_cast(static_cast(buffer_[pos_++])); } // Try to read directly from input if buffer is empty return fl::read(); } istream_real& istream_real::putback(char c) { if (pos_ > 0) { pos_--; buffer_[pos_] = c; } else { // Insert at beginning of buffer - shift existing data if (buffer_len_ < BUFFER_SIZE - 1) { for (fl::size i = buffer_len_; i > 0; --i) { buffer_[i] = buffer_[i-1]; } buffer_[0] = c; buffer_len_++; buffer_[buffer_len_] = '\0'; } } return *this; } int istream_real::peek() { if (pos_ >= buffer_len_ && fl::available() > 0) { if (!readLine()) { return -1; } } if (pos_ < buffer_len_) { return static_cast(static_cast(buffer_[pos_])); } return -1; } } // namespace fl