[ValueStruct] Fix issues formatting longer numbers + add parse Str

This commit is contained in:
TD-er committed 2026-09-07 20:18:03 +02:00
1 parent 24884cdc7f
commit c6df9e0fbd
7 files changed
+230 -38

No files matched your search

+174 -18
View File
@@ -1,7 +1,9 @@
#include "../DataStructs/ValueStruct.h"
#include "../Helpers/Numerical.h"
#include "../Helpers/Memory.h"
#include "../Helpers/PrintToString.h"
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringConverter_Numerical.h"
#define VALUE_STRUCT_SSO_FIRST_CHAR_INDEX 1
@@ -13,13 +15,10 @@
// ********************************************************************************
ValueStruct::~ValueStruct()
{
const auto vtype = static_cast<ValueStruct::ValueType>(_valueType);
if (!_isSSO && (vtype == ValueStruct::ValueType::String))
{
if (str_val != nullptr) {
free(str_val);
}
if (!_isSSO &&
(static_cast<ValueStruct::ValueType>(_valueType) == ValueStruct::ValueType::String)
&& (str_val != nullptr)) {
free(str_val);
}
}
@@ -29,13 +28,50 @@ ValueStruct::ValueStruct(ValueStruct&& rhs)
memset(rhs.bytes_all, 0, sizeof(bytes_all));
}
void ValueStruct::setPreferredFormat(ValueStruct::PreferredFormat format)
{
const auto vtype = getValueType();
if ((vtype == ValueStruct::ValueType::Int) ||
(vtype == ValueStruct::ValueType::UInt))
{
_preferredFormat = (uint64_t)format;
}
}
void ValueStruct::clear() {
if (!_isSSO
&& (getValueType() == ValueStruct::ValueType::String)
&& (str_val != nullptr)) {
free(str_val);
}
memset(bytes_all, 0, sizeof(bytes_all));
}
ValueStruct& ValueStruct::operator=(ValueStruct&& rhs)
{
clear();
memcpy(bytes_all, rhs.bytes_all, sizeof(bytes_all));
// Make sure rhs will not free allocated string
memset(rhs.bytes_all, 0, sizeof(bytes_all));
return *this;
}
ValueStruct& ValueStruct::deepCopy(const ValueStruct& rhs)
{
if (!rhs._isSSO && (rhs.getValueType() == ValueStruct::ValueType::String))
{
this->operator=(ValueStruct(rhs.toString()));
} else {
clear();
// RHS does not have anything heap-allocated, so can make literal copy
memcpy(bytes_all, rhs.bytes_all, sizeof(bytes_all));
}
return *this;
}
ValueStruct::ValueStruct(const bool& val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::Bool),
@@ -46,6 +82,8 @@ ValueStruct::ValueStruct(const bool& val) :
ValueStruct::ValueStruct(int val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::Int),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
i64_val(val)
{}
@@ -54,6 +92,8 @@ ValueStruct::ValueStruct(int val) :
ValueStruct::ValueStruct(int32_t val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::Int),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
i64_val(val)
{}
@@ -62,6 +102,8 @@ ValueStruct::ValueStruct(int32_t val) :
ValueStruct::ValueStruct(uint32_t val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::UInt),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
u64_val(val)
{}
@@ -70,6 +112,8 @@ ValueStruct::ValueStruct(uint32_t val) :
ValueStruct::ValueStruct(size_t val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::UInt),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
u64_val(val)
{}
@@ -78,6 +122,8 @@ ValueStruct::ValueStruct(size_t val) :
ValueStruct::ValueStruct(const uint64_t& val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::UInt),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
u64_val(val)
{}
@@ -85,6 +131,8 @@ ValueStruct::ValueStruct(const uint64_t& val) :
ValueStruct::ValueStruct(const int64_t& val) :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::Int),
_preferredFormat((uint64_t)PreferredFormat::Default),
_minNrDigits(1),
_size(sizeof(val) * 8),
i64_val(val)
{}
@@ -196,6 +244,109 @@ ValueStruct::ValueStruct(const __FlashStringHelper *val) :
str_val((void *)(val))
{}
ValueStruct::ValueType ValueStruct::fromString(const __FlashStringHelper *val)
{
if (fromString(String(val)) == ValueStruct::ValueType::String)
{
// We can store it as a flash string, which doesn't need memory allocation
this->operator=(ValueStruct(val));
}
return getValueType();
}
ValueStruct::ValueType ValueStruct::fromString(const String& val)
{
clear();
String numStr;
NumericalType detectedType = NumericalType::Not_a_number;
bool negativeValue{};
if (!val.isEmpty())
{
String trimmedVal(val);
trimmedVal.trim();
const bool b_true = trimmedVal.equalsIgnoreCase(F("true"));
const bool b_false = trimmedVal.equalsIgnoreCase(F("false"));
if (b_true || b_false) {
this->operator=(ValueStruct(b_true));
return ValueStruct::ValueType::Bool;
}
numStr = getNumerical(trimmedVal, NumericalType::FloatingPoint, detectedType);
numStr.trim();
if (!numStr.isEmpty() && (numStr[0] == '-')) {
negativeValue = true;
}
}
switch (detectedType)
{
case NumericalType::Not_a_number:
break;
case NumericalType::FloatingPoint:
{
ESPEASY_RULES_FLOAT_TYPE val_f{};
int nrDec{};
if (validDoubleFromString(numStr, val_f, nrDec) || (nrDec < 0))
{
this->operator=(ValueStruct(val_f, nrDec));
return ValueStruct::ValueType::Float;
}
break;
}
default:
{
if (negativeValue) {
int64_t result{};
if (validInt64FromString(numStr, result))
{
if (std::numeric_limits<int32_t>::min() < result) {
this->operator=(ValueStruct(static_cast<int32_t>(result)));
}
else {
this->operator=(ValueStruct(result));
}
}
} else {
uint64_t result{};
if (validUInt64FromString(numStr, result))
{
if (std::numeric_limits<uint32_t>::max() > result) {
this->operator=(ValueStruct(static_cast<uint32_t>(result)));
}
else {
this->operator=(ValueStruct(result));
}
}
}
if (isSet()) {
PreferredFormat format(PreferredFormat::Default);
if (detectedType == NumericalType::BinaryUint) { format = PreferredFormat::Bin; }
else if (detectedType == NumericalType::HexadecimalUInt) { format = PreferredFormat::Hex; }
setPreferredFormat(format);
}
break;
}
}
if (!isSet()) {
// Just store it as a string type
this->operator=(ValueStruct(val));
}
return getValueType();
}
String ValueStruct::toString() const
{
ValueType valueType;
@@ -214,7 +365,7 @@ String ValueStruct::toString(ValueType& valueType) const
int64_t ValueStruct::toInt() const
{
switch(getValueType())
switch (getValueType())
{
case ValueStruct::ValueType::Bool:
{
@@ -243,7 +394,7 @@ int64_t ValueStruct::toInt() const
double ValueStruct::toFloat() const
{
switch(getValueType())
switch (getValueType())
{
case ValueStruct::ValueType::Bool:
{
@@ -326,16 +477,26 @@ size_t ValueStruct::print(Print& out, ValueType& valueType) const
}
case ValueStruct::ValueType::Int:
{
if (_size == 8) {
return out.print(ll2String(i64_val));
if ((_size > 32) || _minNrDigits) {
return out.print(ll2String(i64_val, DEC, _minNrDigits));
}
auto v = static_cast<int32_t>(i64_val);
return out.print(v);
}
case ValueStruct::ValueType::UInt:
{
if (_size == 8) {
return out.print(ull2String(u64_val));
const auto format = getPreferredFormat();
if (format == PreferredFormat::Bin) {
return out.print(concat(F("0b"), ull2String(u64_val, BIN, _minNrDigits)));
}
if (format == PreferredFormat::Hex) {
return out.print(concat(F("0x"), ull2String(u64_val, HEX, _minNrDigits)));
}
if ((_size > 32) || _minNrDigits) {
return out.print(ull2String(u64_val, DEC, _minNrDigits));
}
auto v = static_cast<uint32_t>(u64_val);
return out.print(v);
@@ -361,8 +522,3 @@ bool ValueStruct::isEmpty() const
}
return false;
}
void ValueStruct::clear()
{
this->operator=(ValueStruct());
}
+44 -12
View File
@@ -24,6 +24,14 @@ public:
};
enum class PreferredFormat : uint8_t {
Default = 0,
Dec,
Bin,
Hex
};
ValueStruct() :
_isSSO(0),
_valueType((uint64_t)ValueStruct::ValueType::Unset),
@@ -73,24 +81,47 @@ public:
return static_cast<ValueStruct::ValueType>(_valueType);
}
ValueStruct::PreferredFormat getPreferredFormat() const
{
return static_cast<ValueStruct::PreferredFormat>(_preferredFormat);
}
void setPreferredFormat(ValueStruct::PreferredFormat format);
void setMinNrDigits(uint8_t minNrDigits) { _minNrDigits = (uint64_t)minNrDigits; }
ValueStruct& operator=(ValueStruct&& rhs);
ValueStruct& operator=(const ValueStruct& rhs) = delete;
String toString() const;
// We really try to enforce moving the ValueStruct, but when needed a deepcopy is possible
ValueStruct& deepCopy(const ValueStruct& rhs);
String toString(ValueType& valueType) const;
operator bool() const {
return getValueType() != ValueStruct::ValueType::Unset;
}
int64_t toInt() const;
// Try to interpret the given string and store the value as compact as possible
// If the given string is a numerical, try to detect:
// - preferred notation (Hex/Dec/Bin)
// - number of decimals
ValueStruct::ValueType fromString(const __FlashStringHelper *val);
ValueStruct::ValueType fromString(const String& val);
double toFloat() const;
String toString() const;
size_t print(Print& out) const;
String toString(ValueType& valueType) const;
bool isEmpty() const;
int64_t toInt() const;
void clear();
double toFloat() const;
bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; }
size_t print(Print& out) const;
bool isEmpty() const;
void clear();
bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; }
private:
@@ -101,11 +132,12 @@ private:
struct {
uint64_t _isSSO : 1;
uint64_t _trimTrailingZeros : 1;
uint64_t _valueType : 6;
uint64_t _valueType : 4;
uint64_t _preferredFormat : 2;
uint64_t _nrDecimals : 8;
uint64_t _size : 16;
uint64_t unused : 32;
uint64_t _minNrDigits : 8; // For printing ints with leading zeroes
uint64_t _size : 16; // Length of string or nr of bits
uint64_t unused : 24;
union {
void *str_val;
+1 -1
View File
@@ -383,5 +383,5 @@ bool isNumerical(const String& tBuf, NumericalType& detectedType) {
return result.length() >= tmp.length();
}
return result.length() > 0;
return false;
}
+2 -2
View File
@@ -349,14 +349,14 @@ void replaceUnicodeByChar(String& line, char replChar) {
void padToMinimumLength(String& line, uint32_t minimumLength, char padChar)
{
if (line.length() >= minimumLength) return;
if (minimumLength == 0 || line.length() >= minimumLength) return;
line.reserve(minimumLength);
while (line.length() < minimumLength) line += padChar;
}
void prefixToMinimumLength(String& line, uint32_t minimumLength, char prefixChar)
{
if (line.length() >= minimumLength) return;
if (minimumLength == 0 || line.length() >= minimumLength) return;
String prefix;
padToMinimumLength(prefix, minimumLength - line.length(), prefixChar);
@@ -181,6 +181,8 @@ bool doubleToValidString(String& str,
str = F("Inf");
return false;
}
if (decimalPlaces == 255) decimalPlaces = 0;
#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE
// We use some trick here to prevent rounding errors
+3 -3
View File
@@ -117,7 +117,7 @@ void TaskValuesWriterHelper::writeCustom(
uint8_t varNr,
const String& label,
const String& val,
const String& attr,
ValueStruct&& attr,
bool isLast)
{
if (!validDeviceIndex(deviceIndex)) { return; }
@@ -132,7 +132,7 @@ void TaskValuesWriterHelper::writeCustom(
_isLast = isLast;
valName = label;
value = val;
attribute = attr;
attribute = std::move(attr);
write();
}
@@ -245,7 +245,7 @@ void TaskValuesWriterHelper::pluginWebformShowValue()
}
#endif // if FEATURE_STRING_VARIABLES
addHtmlDiv(F("div_l"), valName, format_ID(ID_type::ValueName), attribute);
addHtmlDiv(F("div_l"), valName, format_ID(ID_type::ValueName), attribute.toString());
if (!value_tmp.isEmpty() || !_preformatted) {
addHtmlDiv(F("div_r"), value_tmp, format_ID(ID_type::Value));
+4 -2
View File
@@ -2,6 +2,7 @@
#include "../../ESPEasy_common.h"
#include "../DataStructs/ValueStruct.h"
#include "../DataTypes/DeviceIndex.h"
// Helper function to create formatted custom values for display in the devices overview page.
@@ -53,7 +54,7 @@ struct TaskValuesWriterHelper {
void writeCustom(uint8_t varNr,
const String& label,
const String& val,
const String& attr,
ValueStruct&& attr,
bool isLast = false);
private:
@@ -70,7 +71,8 @@ private:
public:
EventStruct * const event = nullptr;
String valName, value, attribute;
String valName, value;
ValueStruct attribute;
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
String uom;
#endif