string: add snprintf

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2025-01-13 09:54:50 +01:00
parent db413bb5ce
commit a7543cf9ab
6 changed files with 73 additions and 5 deletions

View File

@ -28,6 +28,7 @@ int main(int, const char *[])
UnitTests::stream::replicator(); UnitTests::stream::replicator();
UnitTests::string::strncpy(); UnitTests::string::strncpy();
UnitTests::string::sprintf(); UnitTests::string::sprintf();
UnitTests::string::snprintf();
UnitTests::unicode::charset_encoder(); UnitTests::unicode::charset_encoder();
UnitTests::unicode::normalize(); UnitTests::unicode::normalize();
UnitTests::unicode::str2wstr(); UnitTests::unicode::str2wstr();

View File

@ -109,6 +109,7 @@ namespace UnitTests
public: public:
TEST_METHOD(strncpy); TEST_METHOD(strncpy);
TEST_METHOD(sprintf); TEST_METHOD(sprintf);
TEST_METHOD(snprintf);
}; };
TEST_CLASS(unicode) TEST_CLASS(unicode)

View File

@ -42,4 +42,31 @@ namespace UnitTests
Assert::AreEqual(str.c_str(), stdex::sprintf("%s", locale, str.data()).c_str()); Assert::AreEqual(str.c_str(), stdex::sprintf("%s", locale, str.data()).c_str());
Assert::AreEqual(str.size(), stdex::sprintf("%s", locale, str.data()).size()); Assert::AreEqual(str.size(), stdex::sprintf("%s", locale, str.data()).size());
} }
void string::snprintf()
{
stdex::locale locale(stdex::create_locale(LC_ALL, "en_US.UTF-8"));
{
wchar_t buf[0x100];
Assert::IsTrue(stdex::snprintf(buf, _countof(buf), L"This is %ls.", locale, L"a test") == 15);
Assert::AreEqual(L"This is a test.", buf);
}
{
char buf[0x100];
Assert::IsTrue(stdex::snprintf(buf, _countof(buf), "This is %s.", locale, "a test") == 15);
Assert::AreEqual("This is a test.", buf);
}
{
wchar_t buf[8];
Assert::IsTrue(stdex::snprintf(buf, _countof(buf), L"This is %ls.", locale, L"a test") == 8);
Assert::IsTrue(stdex::strncmp(L"This is a test.", buf, _countof(buf)) == 0);
}
{
char buf[8];
Assert::IsTrue(stdex::snprintf(buf, _countof(buf), "This is %s.", locale, "a test") == 8);
Assert::IsTrue(stdex::strncmp("This is a test.", buf, _countof(buf)) == 0);
}
}
} }

View File

@ -620,7 +620,7 @@ namespace stdex
dst.append(1, static_cast<char>(src[i++])); dst.append(1, static_cast<char>(src[i++]));
else { else {
char tmp[3 + 8 + 1 + 1]; char tmp[3 + 8 + 1 + 1];
snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(src[i++])); ::snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(src[i++]));
dst.append(tmp); dst.append(tmp);
} }
} }
@ -638,7 +638,7 @@ namespace stdex
dst.append(1, static_cast<char>(src[i++])); dst.append(1, static_cast<char>(src[i++]));
else { else {
char tmp[3 + 8 + 1 + 1]; char tmp[3 + 8 + 1 + 1];
snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(wstr_to_utf32(src, i, end))); ::snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(wstr_to_utf32(src, i, end)));
dst.append(tmp); dst.append(tmp);
} }
} }
@ -742,7 +742,7 @@ namespace stdex
} }
else { else {
char tmp[3 + 8 + 1 + 1]; char tmp[3 + 8 + 1 + 1];
int m = snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(src[i++])); int m = ::snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(src[i++]));
stdex_assert(m >= 0); stdex_assert(m >= 0);
if (static_cast<size_t>(m) >= count_dst) if (static_cast<size_t>(m) >= count_dst)
throw buffer_overrun; throw buffer_overrun;
@ -770,7 +770,7 @@ namespace stdex
} }
else { else {
char tmp[3 + 8 + 1 + 1]; char tmp[3 + 8 + 1 + 1];
int m = snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(wstr_to_utf32(src, i, end))); int m = ::snprintf(tmp, _countof(tmp), "&#x%x;", static_cast<unsigned int>(wstr_to_utf32(src, i, end)));
stdex_assert(m >= 0); stdex_assert(m >= 0);
if (static_cast<size_t>(m) >= count_dst) if (static_cast<size_t>(m) >= count_dst)
throw buffer_overrun; throw buffer_overrun;

View File

@ -2655,6 +2655,45 @@ namespace stdex
} }
/// \endcond /// \endcond
///
/// Formats string using `printf()`.
///
/// \param[out] str Output string
/// \param[in ] capacity Number of available code units in str
/// \param[in ] format String template using `printf()` style
/// \param[in ] locale Stdlib locale used to perform formatting. Use `NULL` to use locale globally set by `setlocale()`.
///
/// \return Number of output code units
///
template<class T>
inline size_t snprintf(_Out_z_cap_(capacity) T* str, _In_ size_t capacity, _In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
{
va_list arg;
va_start(arg, locale);
int count = vsnprintf(str, capacity, format, locale, arg);
va_end(arg);
if (0 <= count && static_cast<size_t>(count) <= capacity)
return static_cast<size_t>(count);
#ifdef _WIN32
if (count < 0) {
switch (errno) {
case 0: return capacity;
case EINVAL: throw std::invalid_argument("invalid snprintf arguments");
case EILSEQ: throw std::runtime_error("encoding error");
default: throw std::runtime_error("failed to format string");
}
} else
return capacity;
#else
switch (errno) {
case EOVERFLOW: return capacity;
case EINVAL: throw std::invalid_argument("invalid snprintf arguments");
case EILSEQ: throw std::runtime_error("encoding error");
default: throw std::runtime_error("failed to format string");
}
#endif
}
/// ///
/// Formats string using `printf()`. /// Formats string using `printf()`.
/// ///

View File

@ -43,7 +43,7 @@ namespace stdex
static_cast<unsigned int>(id.Data4[0]), static_cast<unsigned int>(id.Data4[1]), static_cast<unsigned int>(id.Data4[0]), static_cast<unsigned int>(id.Data4[1]),
static_cast<unsigned int>(id.Data4[2]), static_cast<unsigned int>(id.Data4[3]), static_cast<unsigned int>(id.Data4[4]), static_cast<unsigned int>(id.Data4[5]), static_cast<unsigned int>(id.Data4[6]), static_cast<unsigned int>(id.Data4[7])); static_cast<unsigned int>(id.Data4[2]), static_cast<unsigned int>(id.Data4[3]), static_cast<unsigned int>(id.Data4[4]), static_cast<unsigned int>(id.Data4[5]), static_cast<unsigned int>(id.Data4[6]), static_cast<unsigned int>(id.Data4[7]));
#else #else
snprintf(str, uuid_str_max, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", ::snprintf(str, uuid_str_max, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
*reinterpret_cast<const uint32_t*>(&id[0]), *reinterpret_cast<const uint32_t*>(&id[0]),
static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[4])), static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[4])),
static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[6])), static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[6])),