diff --git a/include/bx/inline/string.inl b/include/bx/inline/string.inl index 87bbef2..7c66aab 100644 --- a/include/bx/inline/string.inl +++ b/include/bx/inline/string.inl @@ -240,9 +240,8 @@ namespace bx template inline constexpr FixedStringT::FixedStringT() - : m_len(0) + : m_pod{ .storage = { '\0' }, .len = 0 } { - m_storage[0] = '\0'; } template @@ -273,45 +272,51 @@ namespace bx template inline constexpr void FixedStringT::set(const StringView& _str) { - int32_t copied = strCopy(m_storage, MaxCapacityT, _str); - m_len = copied; + int32_t copied = strCopy(m_pod.storage, MaxCapacityT, _str); + m_pod.len = copied; } template inline constexpr void FixedStringT::append(const StringView& _str) { - m_len += strCopy(&m_storage[m_len], MaxCapacityT-m_len, _str); + m_pod.len += strCopy(&m_pod.storage[m_pod.len], MaxCapacityT-m_pod.len, _str); } template inline constexpr void FixedStringT::clear() { - m_len = 0; - m_storage[0] = '\0'; + m_pod.len = 0; + m_pod.storage[0] = '\0'; } template inline constexpr bool FixedStringT::isEmpty() const { - return 0 == m_len; + return 0 == m_pod.len; } template inline constexpr int32_t FixedStringT::getLength() const { - return m_len; + return m_pod.len; } template inline constexpr const char* FixedStringT::getCPtr() const { - return m_storage; + return m_pod.storage; } template inline constexpr FixedStringT::operator StringView() const { - return StringView(m_storage, m_len); + return StringView(m_pod.storage, m_pod.len); + } + + template + inline FixedStringT::Pod& FixedStringT::asPod() + { + return m_pod; } template @@ -596,4 +601,52 @@ namespace bx return narrowCastTest(_out, tmp); } + template + inline FixedStringT toHuman(uint64_t _value) + { + FixedStringT result; + auto& pod = result.asPod(); + + pod.len = formatHumanNumber(pod.storage, MaxCapacityT, double(_value), 0); + + return result; + }; + + template + inline FixedStringT toHuman(uint64_t _value, Units::Enum _units, uint8_t _numFrac) + { + FixedStringT result; + auto& pod = result.asPod(); + + switch (_units) + { + case Units::Kilo: + pod.len = formatHumanNumber(pod.storage, MaxCapacityT, double(_value), _numFrac, 1000.0, "", " KMGTPEZY"); + break; + + case Units::KiloByte: + pod.len = formatHumanNumber(pod.storage, MaxCapacityT, double(_value), _numFrac, 1000.0, "B", "BkMGTPEZY"); + break; + + case Units::KibiByte: + default: + pod.len = formatHumanNumber(pod.storage, MaxCapacityT, double(_value), _numFrac, 1024.0, "iB", "BKMGTPEZY"); + break; + } + + return result; + }; + + template + inline FixedStringT toHuman(Ticks _value, uint8_t _numFrac) + { + FixedStringT result; + auto& pod = result.asPod(); + + const double value = toSeconds(_value); + pod.len = formatHumanNumber(pod.storage, MaxCapacityT, value, _numFrac, 1000.0, "s", "pnum ", 4); + + return result; + }; + } // namespace bx diff --git a/include/bx/inline/timer.inl b/include/bx/inline/timer.inl index b1c224b..2edbac4 100644 --- a/include/bx/inline/timer.inl +++ b/include/bx/inline/timer.inl @@ -29,6 +29,7 @@ namespace bx } inline constexpr Ticks Ticks::operator+ (Ticks _rhs) const { return Ticks(ticks + _rhs.ticks); } + inline constexpr Ticks Ticks::operator- ( ) const { return Ticks(-ticks); } inline constexpr Ticks Ticks::operator- (Ticks _rhs) const { return Ticks(ticks - _rhs.ticks); } inline constexpr Ticks Ticks::operator* (float _rhs) const { return Ticks(int64_t(double(ticks * _rhs) ) ); } inline constexpr Ticks& Ticks::operator+=(Ticks _rhs) { ticks += _rhs.ticks; return *this; } diff --git a/include/bx/string.h b/include/bx/string.h index 4f99a97..a4177e9 100644 --- a/include/bx/string.h +++ b/include/bx/string.h @@ -7,6 +7,7 @@ #define BX_STRING_H_HEADER_GUARD #include "allocator.h" +#include "timer.h" namespace bx { @@ -15,8 +16,9 @@ namespace bx { enum Enum //!< Units: { - Kilo, //!< SI units - Kibi, //!< IEC prefix + Kilo, + KiloByte, //!< SI units + KibiByte, //!< IEC prefix }; }; @@ -152,6 +154,12 @@ namespace bx class FixedStringT { public: + struct Pod + { + char storage[MaxCapacityT]; + int32_t len; + }; + /// constexpr FixedStringT(); @@ -192,9 +200,12 @@ namespace bx /// constexpr operator StringView() const; + /// + /// + Pod& asPod(); + private: - char m_storage[MaxCapacityT]; - int32_t m_len; + Pod m_pod; }; /// @@ -458,10 +469,25 @@ namespace bx /// @param[in] _numFrac Number of fraction digits. /// @returns Length of output string. /// - int32_t formatHumanNumber(char* _out, int32_t _count, double _value, int32_t _numFrac); + int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, const StringView& _unit = "", char _prefix = ' '); + + /// + int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, double _unitStep, const StringView& _unit, const StringView& _prefix, uint8_t _basePrefix = 0); + + /// + template + FixedStringT toHuman(uint64_t _value); + + /// + template + FixedStringT toHuman(uint64_t _value, Units::Enum _units, uint8_t _numFrac = 2); + + /// + template + FixedStringT toHuman(Ticks _value, uint8_t _numFrac = 4); /// Convert size in bytes to human readable string kibi units. - int32_t prettify(char* _out, int32_t _count, uint64_t _value, Units::Enum _units = Units::Kibi); + int32_t prettify(char* _out, int32_t _count, uint64_t _value, Units::Enum _units = Units::KibiByte); /// Converts bool value to string. int32_t toString(char* _out, int32_t _max, bool _value); diff --git a/include/bx/timer.h b/include/bx/timer.h index 55c0b46..91f4329 100644 --- a/include/bx/timer.h +++ b/include/bx/timer.h @@ -36,6 +36,7 @@ namespace bx /// Binary arithmetic operators. constexpr Ticks operator+ (Ticks _rhs) const; + constexpr Ticks operator- () const; constexpr Ticks operator- (Ticks _rhs) const; constexpr Ticks operator* (float _rhs) const; constexpr Ticks& operator+=(Ticks _rhs); diff --git a/src/string.cpp b/src/string.cpp index 1f2e9fc..ec467f0 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -1280,7 +1280,7 @@ namespace bx return total; } - int32_t formatHumanNumber(char* _out, int32_t _count, double _value, int32_t _numFrac) + int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, const StringView& _unit, char _prefix) { char temp[64]; int32_t len = snprintf(temp, sizeof(temp), "%.*f", _numFrac, _value); @@ -1309,7 +1309,7 @@ namespace bx const int32_t commas = (intPartLen > 3) ? (intPartLen - 1) / 3 : 0; const int32_t total = intPartLen + fracPartLen + commas; - if (_count < total) + if (_count < uint32_t(total) ) { if (0 < _count) { @@ -1320,7 +1320,15 @@ namespace bx } char* out = _out + total; - *out = '\0'; + if (_unit.isEmpty() + && ' ' == _prefix) + { + *out = '\0'; + } + else + { + snprintf(out, _count - total, " %c%S", _prefix, &_unit); + } if (0 != fracPartLen) { @@ -1344,39 +1352,41 @@ namespace bx return total; } - static const char s_units[] = { 'B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; - - template - inline int32_t prettify(char* _out, int32_t _count, uint64_t _value) + int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, double _unitStep, const StringView& _unit, const StringView& _prefix, uint8_t _basePrefix) { - uint8_t idx = 0; + uint8_t idx = _basePrefix; double value = double(_value); - while (_value != (_value&0x7ff) - && idx < BX_COUNTOF(s_units) ) + const double invUnitStep = 1.0/double(_unitStep); + const uint8_t numPrefixes = narrowCast(_prefix.getLength() ); + + if (0.0 != _value) { - _value /= Kilo; - value *= 1.0/double(Kilo); - ++idx; + while (value >= _unitStep + && idx < numPrefixes) + { + value *= invUnitStep; + ++idx; + } + + while (value < 1.0 + && idx > 0) + { + value *= _unitStep; + --idx; + } } - char human[32]; - formatHumanNumber(human, sizeof(human), value, 2); - - return snprintf(_out, _count, "%s %c%c%c", human - , fn(s_units[idx]) - , idx > 0 ? KiloCh0 : '\0' - , KiloCh1 - ); + return formatHumanNumber(_out, _count, value, _numFrac, 0 == idx ? "" : _unit, _prefix.getPtr()[idx]); } int32_t prettify(char* _out, int32_t _count, uint64_t _value, Units::Enum _units) { if (Units::Kilo == _units) { - return prettify<1000, 'B', '\0', toNoop>(_out, _count, _value); + return formatHumanNumber(_out, _count, double(_value), 0, 1000.0, "B", "BkMGTPEZY"); } - return prettify<1024, 'i', 'B', toUpper>(_out, _count, _value); + return formatHumanNumber(_out, _count, double(_value), 0, 1024.0, "iB", "BKMGTPEZY"); } } // namespace bx diff --git a/tests/handle_test.cpp b/tests/handle_test.cpp index ade5a5e..afce746 100644 --- a/tests/handle_test.cpp +++ b/tests/handle_test.cpp @@ -163,26 +163,26 @@ TEST_CASE("HandleHashTable", "") bx::StringView sv0("test0"); - bool ok = hm.insert(bx::hash(sv0), 0); + bool ok = hm.insert(bx::hash(sv0), 0); REQUIRE(ok); - ok = hm.insert(bx::hash(sv0), 0); + ok = hm.insert(bx::hash(sv0), 0); REQUIRE(!ok); REQUIRE(1 == hm.getNumElements() ); bx::StringView sv1("test1"); - ok = hm.insert(bx::hash(sv1), 0); + ok = hm.insert(bx::hash(sv1), 0); REQUIRE(ok); REQUIRE(2 == hm.getNumElements() ); hm.removeByHandle(0); REQUIRE(0 == hm.getNumElements() ); - ok = hm.insert(bx::hash(sv0), 0); + ok = hm.insert(bx::hash(sv0), 0); REQUIRE(ok); - hm.removeByKey(bx::hash(sv0) ); + hm.removeByKey(bx::hash(sv0) ); REQUIRE(0 == hm.getNumElements() ); for (uint32_t ii = 0, num = hm.getMaxCapacity(); ii < num; ++ii) diff --git a/tests/string_test.cpp b/tests/string_test.cpp index fcbfcc7..2ae6e9b 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -26,7 +26,7 @@ TEST_CASE("prettify", "[string]") prettify(tmp, BX_COUNTOF(tmp), 4000, bx::Units::Kilo); REQUIRE(0 == bx::strCmp(tmp, "4 kB") ); - prettify(tmp, BX_COUNTOF(tmp), 4096, bx::Units::Kibi); + prettify(tmp, BX_COUNTOF(tmp), 4096, bx::Units::KibiByte); REQUIRE(0 == bx::strCmp(tmp, "4 KiB") ); }