#include #include // ok include - for strcmp #include "fl/str.h" #include "crgb.h" #include "fl/fft.h" #include "fl/namespace.h" #include "fl/unused.h" #include "fl/xymap.h" #include "fl/json.h" #include "fl/tile2x2.h" #include "fl/compiler_control.h" // UI dependency moved to separate compilation unit to break dependency chain #ifdef FASTLED_TESTING #include // ok include #endif namespace fl { // Define static const member for npos (only for string class) const fl::size string::npos; // Explicit template instantiations for commonly used sizes template class StrN<64>; namespace string_functions { static void ftoa(float value, char *buffer, int precision = 2) { #ifdef FASTLED_TESTING // use snprintf during testing with precision snprintf(buffer, 64, "%.*f", precision, value); return; #else // Handle negative values if (value < 0) { *buffer++ = '-'; value = -value; } // Extract integer part u32 intPart = (u32)value; // Convert integer part to string (reversed) char intBuf[12]; // Enough for 32-bit integers int i = 0; do { intBuf[i++] = '0' + (intPart % 10); intPart /= 10; } while (intPart); // Write integer part in correct order while (i--) { *buffer++ = intBuf[i]; } *buffer++ = '.'; // Decimal point // Extract fractional part float fracPart = value - (u32)value; for (int j = 0; j < precision; ++j) { fracPart *= 10.0f; int digit = (int)fracPart; *buffer++ = '0' + digit; fracPart -= digit; } *buffer = '\0'; // Null-terminate #endif } static int itoa(int value, char *sp, int radix) { char tmp[16]; // be careful with the length of the buffer char *tp = tmp; int i; unsigned v; int sign = (radix == 10 && value < 0); if (sign) v = -value; else v = (unsigned)value; while (v || tp == tmp) { i = v % radix; v = radix ? v / radix : 0; if (i < 10) *tp++ = i + '0'; else *tp++ = i + 'a' - 10; } int len = tp - tmp; if (sign) { *sp++ = '-'; len++; } while (tp > tmp) *sp++ = *--tp; return len; } static int utoa32(uint32_t value, char *sp, int radix) { char tmp[16]; // be careful with the length of the buffer char *tp = tmp; int i; uint32_t v = value; while (v || tp == tmp) { i = v % radix; v = radix ? v / radix : 0; if (i < 10) *tp++ = i + '0'; else *tp++ = i + 'a' - 10; } int len = tp - tmp; while (tp > tmp) *sp++ = *--tp; return len; } static int utoa64(uint64_t value, char *sp, int radix) { char tmp[32]; // larger buffer for 64-bit values char *tp = tmp; int i; uint64_t v = value; while (v || tp == tmp) { i = v % radix; v = radix ? v / radix : 0; if (i < 10) *tp++ = i + '0'; else *tp++ = i + 'a' - 10; } int len = tp - tmp; while (tp > tmp) *sp++ = *--tp; return len; } static float atoff(const char *str, fl::size len) { float result = 0.0f; // The resulting number float sign = 1.0f; // Positive or negative float fraction = 0.0f; // Fractional part float divisor = 1.0f; // Divisor for the fractional part int isFractional = 0; // Whether the current part is fractional fl::size pos = 0; // Current position in the string // Handle empty input if (len == 0) { return 0.0f; } // Skip leading whitespace (manual check instead of isspace) while (pos < len && (str[pos] == ' ' || str[pos] == '\t' || str[pos] == '\n' || str[pos] == '\r' || str[pos] == '\f' || str[pos] == '\v')) { pos++; } // Handle optional sign if (pos < len && str[pos] == '-') { sign = -1.0f; pos++; } else if (pos < len && str[pos] == '+') { pos++; } // Main parsing loop while (pos < len) { if (str[pos] >= '0' && str[pos] <= '9') { if (isFractional) { divisor *= 10.0f; fraction += (str[pos] - '0') / divisor; } else { result = result * 10.0f + (str[pos] - '0'); } } else if (str[pos] == '.' && !isFractional) { isFractional = 1; } else { // Stop parsing at invalid characters break; } pos++; } // Combine integer and fractional parts result = result + fraction; // Apply the sign return sign * result; } } // namespace string_functions void StringFormatter::append(i32 val, StrN<64> *dst) { char buf[63] = {0}; string_functions::itoa(val, buf, 10); dst->write(buf, strlen(buf)); } void StringFormatter::append(u32 val, StrN<64> *dst) { char buf[63] = {0}; string_functions::utoa32(val, buf, 10); dst->write(buf, strlen(buf)); } void StringFormatter::append(uint64_t val, StrN<64> *dst) { char buf[63] = {0}; string_functions::utoa64(val, buf, 10); dst->write(buf, strlen(buf)); } void StringFormatter::append(i16 val, StrN<64> *dst) { append(static_cast(val), dst); } void StringFormatter::append(u16 val, StrN<64> *dst) { append(static_cast(val), dst); } StringHolder::StringHolder(const char *str) { mLength = strlen(str); // Don't include null terminator in length mCapacity = mLength + 1; // Capacity includes null terminator mData = new char[mCapacity]; memcpy(mData, str, mLength); mData[mLength] = '\0'; } StringHolder::StringHolder(fl::size length) { mData = (char *)malloc(length + 1); if (mData) { mLength = length; mData[mLength] = '\0'; } else { mLength = 0; } mCapacity = mLength; } StringHolder::StringHolder(const char *str, fl::size length) { mData = (char *)malloc(length + 1); if (mData) { mLength = length; memcpy(mData, str, mLength); mData[mLength] = '\0'; } else { mLength = 0; } mCapacity = mLength; } StringHolder::~StringHolder() { free(mData); // Release the memory } void StringHolder::grow(fl::size newLength) { if (newLength <= mCapacity) { // New length must be greater than current length mLength = newLength; return; } char *newData = (char *)realloc(mData, newLength + 1); if (newData) { mData = newData; mLength = newLength; mCapacity = newLength; mData[mLength] = '\0'; // Ensure null-termination } else { // handle re-allocation failure. char *newData = (char *)malloc(newLength + 1); if (newData) { memcpy(newData, mData, mLength + 1); free(mData); mData = newData; mLength = newLength; mCapacity = mLength; } else { // memory failure. } } } float StringFormatter::parseFloat(const char *str, fl::size len) { return string_functions::atoff(str, len); } int StringFormatter::parseInt(const char *str, fl::size len) { float f = parseFloat(str, len); return static_cast(f); } int StringFormatter::parseInt(const char *str) { return parseInt(str, strlen(str)); } int string::strcmp(const string& a, const string& b) { return ::strcmp(a.c_str(), b.c_str()); } string &string::append(const FFTBins &str) { append("\n FFTImpl Bins:\n "); append(str.bins_raw); append("\n"); append(" FFTImpl Bins DB:\n "); append(str.bins_db); append("\n"); return *this; } string &string::append(const XYMap &map) { append("XYMap("); append(map.getWidth()); append(","); append(map.getHeight()); append(")"); return *this; } string &string::append(const Tile2x2_u8_wrap &tile) { Tile2x2_u8_wrap::Entry data[4] = { tile.at(0, 0), tile.at(0, 1), tile.at(1, 0), tile.at(1, 1), }; append("Tile2x2_u8_wrap("); for (int i = 0; i < 4; i++) { vec2 pos = data[i].first; u8 alpha = data[i].second; append("("); append(pos.x); append(","); append(pos.y); append(","); append(alpha); append(")"); if (i < 3) { append(","); } } append(")"); return *this; } void string::swap(string &other) { if (this != &other) { fl::swap(mLength, other.mLength); char temp[FASTLED_STR_INLINED_SIZE]; memcpy(temp, mInlineData, FASTLED_STR_INLINED_SIZE); memcpy(mInlineData, other.mInlineData, FASTLED_STR_INLINED_SIZE); memcpy(other.mInlineData, temp, FASTLED_STR_INLINED_SIZE); fl::swap(mHeapData, other.mHeapData); } } void string::compileTimeAssertions() { static_assert(FASTLED_STR_INLINED_SIZE > 0, "FASTLED_STR_INLINED_SIZE must be greater than 0"); static_assert(FASTLED_STR_INLINED_SIZE == kStrInlineSize, "If you want to change the FASTLED_STR_INLINED_SIZE, then it " "must be through a build define and not an include define."); } string &string::append(const CRGB &rgb) { append("CRGB("); append(rgb.r); append(","); append(rgb.g); append(","); append(rgb.b); append(")"); return *this; } void StringFormatter::appendFloat(const float &val, StrN<64> *dst) { char buf[64] = {0}; string_functions::ftoa(val, buf); dst->write(buf, strlen(buf)); } void StringFormatter::appendFloat(const float &val, StrN<64> *dst, int precision) { char buf[64] = {0}; string_functions::ftoa(val, buf, precision); dst->write(buf, strlen(buf)); } // JsonUiInternal append implementation moved to str_ui.cpp to break dependency chain // JSON type append implementations // NOTE: These use forward declarations to avoid circular dependency with json.h string &string::append(const JsonValue& val) { // Use the JsonValue's to_string method if available // For now, just append a placeholder to avoid compilation errors FL_UNUSED(val); append(""); return *this; } string &string::append(const Json& val) { // Use the Json's to_string method if available // For now, just append a placeholder to avoid compilation errors //append(""); append("Json("); append(val.to_string()); append(")"); return *this; } } // namespace fl