diff --git a/include/bx/inline/string.inl b/include/bx/inline/string.inl index 4bec9dd..d4fab31 100644 --- a/include/bx/inline/string.inl +++ b/include/bx/inline/string.inl @@ -124,6 +124,7 @@ namespace bx { m_len = INT32_MAX == _len ? strLen(_ptr) : _len; m_ptr = _ptr; + m_0terminated = INT32_MAX == _len; } } @@ -149,6 +150,7 @@ namespace bx { m_ptr = ""; m_len = 0; + m_0terminated = true; } inline const char* StringView::getPtr() const @@ -171,28 +173,31 @@ namespace bx return m_len; } + inline bool StringView::is0Terminated() const + { + return m_0terminated; + } + template inline StringT::StringT() : StringView() + , m_capacity(0) { + clear(); } template inline StringT::StringT(const StringT& _rhs) : StringView() + , m_capacity(0) { set(_rhs); } - template - inline StringT& StringT::operator=(const StringT& _rhs) - { - set(_rhs); - return *this; - } - template inline StringT::StringT(const StringView& _rhs) + : StringView() + , m_capacity(0) { set(_rhs); } @@ -203,6 +208,13 @@ namespace bx clear(); } + template + inline StringT& StringT::operator=(const StringT& _rhs) + { + set(_rhs); + return *this; + } + template inline void StringT::set(const StringView& _str) { @@ -215,13 +227,22 @@ namespace bx { if (0 != _str.getLength() ) { - int32_t old = m_len; - int32_t len = m_len + strLen(_str); - char* ptr = (char*)BX_REALLOC(*AllocatorT, 0 != m_len ? const_cast(m_ptr) : NULL, len+1); + const int32_t old = m_len; + const int32_t len = m_len + _str.getLength(); + + char* ptr = const_cast(m_ptr); + + if (len+1 > m_capacity) + { + const int32_t capacity = alignUp(len+1, 256); + ptr = (char*)BX_REALLOC(*AllocatorT, 0 != m_capacity ? ptr : NULL, capacity); + + *const_cast(&m_ptr) = ptr; + m_capacity = capacity; + } + m_len = len; strCopy(ptr + old, len-old+1, _str); - - *const_cast(&m_ptr) = ptr; } } @@ -234,11 +255,14 @@ namespace bx template inline void StringT::clear() { - if (0 != m_len) + m_0terminated = true; + + if (0 != m_capacity) { BX_FREE(*AllocatorT, const_cast(m_ptr) ); StringView::clear(); + m_capacity = 0; } } diff --git a/include/bx/string.h b/include/bx/string.h index 87910b6..5360e12 100644 --- a/include/bx/string.h +++ b/include/bx/string.h @@ -91,15 +91,19 @@ namespace bx /// const char* getTerm() const; - /// + /// Returns `true` if string is empty. bool isEmpty() const; - /// + /// Returns string length. int32_t getLength() const; + /// Returns `true` if string is zero terminated. + bool is0Terminated() const; + protected: const char* m_ptr; int32_t m_len; + bool m_0terminated; }; /// ASCII string @@ -113,15 +117,15 @@ namespace bx /// StringT(const StringT& _rhs); - /// - StringT& operator=(const StringT& _rhs); - /// StringT(const StringView& _rhs); /// ~StringT(); + /// + StringT& operator=(const StringT& _rhs); + /// void set(const StringView& _str); @@ -137,6 +141,9 @@ namespace bx /// Returns zero-terminated C string pointer. /// const char* getCPtr() const; + + protected: + int32_t m_capacity; }; /// Retruns true if character is part of space set. diff --git a/src/string.cpp b/src/string.cpp index c884948..fb5afed 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -813,6 +813,11 @@ namespace bx return write(_writer, _str, _param.prec, _param, _err); } + static int32_t write(WriterI* _writer, const StringView& _str, const Param& _param, Error* _err) + { + return write(_writer, _str.getPtr(), min(_param.prec, _str.getLength() ), _param, _err); + } + static int32_t write(WriterI* _writer, int32_t _i, const Param& _param, Error* _err) { char str[33]; @@ -939,12 +944,15 @@ namespace bx } else if ('%' == ch) { - // %[flags][width][.precision][length sub-specifier]specifier + // %[Flags][Width][.Precision][Leegth]Type read(&reader, ch, &err); Param param; - // flags + // Reference(s): + // - Flags field + // https://en.wikipedia.org/wiki/Printf_format_string#Flags_field + // while (err.isOk() && ( ' ' == ch || '-' == ch @@ -971,7 +979,10 @@ namespace bx param.fill = ' '; } - // width + // Reference(s): + // - Width field + // https://en.wikipedia.org/wiki/Printf_format_string#Width_field + // if ('*' == ch) { read(&reader, ch, &err); @@ -994,7 +1005,9 @@ namespace bx } } - // .precision + // Reference(s): + // - Precision field + // https://en.wikipedia.org/wiki/Printf_format_string#Precision_field if ('.' == ch) { read(&reader, ch, &err); @@ -1016,7 +1029,9 @@ namespace bx } } - // length sub-specifier + // Reference(s): + // - Length field + // https://en.wikipedia.org/wiki/Printf_format_string#Length_field while (err.isOk() && ( 'h' == ch || 'I' == ch @@ -1031,8 +1046,8 @@ namespace bx default: break; case 'j': param.bits = sizeof(intmax_t )*8; break; - case 't': param.bits = sizeof(size_t )*8; break; - case 'z': param.bits = sizeof(ptrdiff_t)*8; break; + case 't': param.bits = sizeof(ptrdiff_t)*8; break; + case 'z': param.bits = sizeof(size_t )*8; break; case 'h': case 'I': case 'l': switch (ch) @@ -1071,7 +1086,9 @@ namespace bx break; } - // specifier + // Reference(s): + // - Type field + // https://en.wikipedia.org/wiki/Printf_format_string#Type_field switch (toLower(ch) ) { case 'c': @@ -1079,7 +1096,14 @@ namespace bx break; case 's': - size += write(_writer, va_arg(_argList, const char*), param, _err); + if (isUpper(ch) ) + { + size += write(_writer, va_arg(_argList, const StringView), param, _err); + } + else + { + size += write(_writer, va_arg(_argList, const char*), param, _err); + } break; case 'o': @@ -1131,6 +1155,10 @@ namespace bx } break; + case 'n': + *va_arg(_argList, int32_t*) = size; + break; + default: size += write(_writer, ch, _err); break; diff --git a/tests/string_test.cpp b/tests/string_test.cpp index ca820c4..5524d91 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -555,3 +555,29 @@ TEST_CASE("suffix", "") REQUIRE(0 == bx::strCmp(bx::strTrimSuffix("abvgd-1389.0", "389.0"), "abvgd-1") ); REQUIRE(0 == bx::strCmp(bx::strTrimSuffix("abvgd-1389.0", "xyz"), "abvgd-1389.0") ); } + +TEST_CASE("0terminated", "") +{ + const bx::StringView t0("1389"); + REQUIRE(t0.is0Terminated() ); + const bx::StringView t1(strTrimPrefix(t0, "13") ); + REQUIRE(!t1.is0Terminated() ); + const bx::StringView t2(strTrimSuffix(t0, "89") ); + REQUIRE(!t2.is0Terminated() ); + + bx::DefaultAllocator crt; + g_allocator = &crt; + + typedef bx::StringT<&g_allocator> String; + + String st; + REQUIRE(st.is0Terminated() ); + + st = strTrimPrefix(t0, "13"); + REQUIRE(2 == st.getLength() ); + REQUIRE(st.is0Terminated() ); + + st = strTrimSuffix(t0, "89"); + REQUIRE(2 == st.getLength() ); + REQUIRE(st.is0Terminated() ); +} diff --git a/tests/vsnprintf_test.cpp b/tests/vsnprintf_test.cpp index e65b70c..2cf1416 100644 --- a/tests/vsnprintf_test.cpp +++ b/tests/vsnprintf_test.cpp @@ -184,6 +184,22 @@ TEST_CASE("vsnprintf t") size_t size = -1; REQUIRE(test("-1", "%td", size) ); + + REQUIRE(test("3221225472", "%td", size_t(3221225472) ) ); +} + +TEST_CASE("vsnprintf n") +{ + char temp[64]; + + int32_t p0, p1, p2; + bx::snprintf(temp, sizeof(temp), "%n", &p0); + REQUIRE(0 == p0); + + bx::snprintf(temp, sizeof(temp), "01%n23%n45%n", &p0, &p1, &p2); + REQUIRE(2 == p0); + REQUIRE(4 == p1); + REQUIRE(6 == p2); } TEST_CASE("vsnprintf g") @@ -215,6 +231,11 @@ TEST_CASE("vsnprintf") , hello.getLength(), hello.getPtr() , world.getLength(), world.getPtr() ) ); + + REQUIRE(test("hello, world!", "%S, %S!" + , hello + , world + ) ); } TEST_CASE("vsnprintf write")