Skip to content

Commit 454e7ff

Browse files
String Tools: Add AppendInt function
1 parent 17d601e commit 454e7ff

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

Common/interface/StringTools.hpp

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ std::vector<std::string> SplitString(IterType Start, IterType End, const char* D
277277

278278

279279
/// Returns the print width of a number Num
280-
template <typename Type>
280+
template <typename Type, typename = std::enable_if_t<std::is_integral<Type>::value>>
281281
size_t GetPrintWidth(Type Num, Type Base = 10)
282282
{
283283
if (Num == 0)
@@ -293,4 +293,64 @@ size_t GetPrintWidth(Type Num, Type Base = 10)
293293
return W;
294294
}
295295

296+
template <typename Type>
297+
inline std::make_unsigned_t<Type> GetAbsUnsigned(Type Num, bool& IsNegative, std::true_type /*is_signed*/)
298+
{
299+
using Unsigned = std::make_unsigned_t<Type>;
300+
301+
if (Num < 0)
302+
{
303+
IsNegative = true;
304+
// Avoid overflow on minimum value:
305+
// -(Num + 1) is safe, then add 1 back in unsigned.
306+
return static_cast<Unsigned>(-(Num + Type{1})) + Unsigned{1};
307+
}
308+
else
309+
{
310+
IsNegative = false;
311+
return static_cast<Unsigned>(Num);
312+
}
313+
}
314+
315+
template <typename Type>
316+
inline std::make_unsigned_t<Type> GetAbsUnsigned(Type Num, bool& IsNegative, std::false_type /*is_signed*/)
317+
{
318+
using Unsigned = std::make_unsigned_t<Type>;
319+
IsNegative = false;
320+
return static_cast<Unsigned>(Num);
321+
}
322+
323+
/// Appends the integer number Num in the specified Base to the string Str
324+
template <typename Type, typename = std::enable_if_t<std::is_integral<Type>::value>>
325+
std::string& AppendInt(std::string& Str, Type Num, Type Base = 10)
326+
{
327+
VERIFY(Base >= 2 && Base <= 36, "Base must be in the range [2, 36]");
328+
329+
const size_t PrintWidth = GetPrintWidth(Num, Base);
330+
const size_t StartPos = Str.length();
331+
Str.resize(StartPos + PrintWidth);
332+
333+
bool IsNegative = false;
334+
using Unsigned = std::make_unsigned_t<Type>;
335+
Unsigned AbsNum = GetAbsUnsigned(
336+
Num,
337+
IsNegative,
338+
std::is_signed<Type>{});
339+
340+
if (IsNegative)
341+
{
342+
Str[StartPos] = '-';
343+
}
344+
345+
size_t DigitPos = StartPos + PrintWidth - 1;
346+
for (size_t i = 0; i < PrintWidth - (IsNegative ? 1u : 0u); ++i)
347+
{
348+
const Unsigned Digit = AbsNum % static_cast<Unsigned>(Base);
349+
Str[DigitPos--] = static_cast<char>(Digit < 10u ? '0' + Digit : 'A' + (Digit - 10u));
350+
AbsNum /= Base;
351+
}
352+
353+
return Str;
354+
}
355+
296356
} // namespace Diligent

Tests/DiligentCoreTest/src/Common/StringToolsTest.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
#include "gtest/gtest.h"
3131

32+
#include <limits.h>
33+
3234
using namespace Diligent;
3335

3436
namespace
@@ -276,4 +278,140 @@ TEST(Common_StringTools, GetPrintWidth)
276278
EXPECT_EQ(GetPrintWidth(-999), size_t{4});
277279
}
278280

281+
template <typename T>
282+
void TestAppendInt(T Value, T Base, const char* RefStr)
283+
{
284+
std::string Str;
285+
AppendInt(Str, Value, Base);
286+
EXPECT_STREQ(Str.c_str(), RefStr);
287+
}
288+
289+
template <typename T>
290+
void TestAppendInt(T Value, const char* RefStr)
291+
{
292+
TestAppendInt(Value, static_cast<T>(10), RefStr);
293+
}
294+
295+
TEST(Common_StringTools, AppendInt)
296+
{
297+
// Decimal
298+
TestAppendInt<int>(0, "0");
299+
TestAppendInt<int>(1, "1");
300+
TestAppendInt<int>(9, "9");
301+
TestAppendInt<int>(10, "10");
302+
TestAppendInt<int>(98, "98");
303+
TestAppendInt<int>(123, "123");
304+
TestAppendInt<int>(-1, "-1");
305+
TestAppendInt<int>(-9, "-9");
306+
TestAppendInt<int>(-12, "-12");
307+
TestAppendInt<int>(-98, "-98");
308+
TestAppendInt<int>(-123, "-123");
309+
TestAppendInt<int>(INT_MAX, "2147483647");
310+
TestAppendInt<int>(INT_MIN, "-2147483648");
311+
312+
TestAppendInt<Uint8>(0, "0");
313+
TestAppendInt<Uint8>(1, "1");
314+
TestAppendInt<Uint8>(9, "9");
315+
TestAppendInt<Uint8>(128, "128");
316+
TestAppendInt<Uint8>(255, "255");
317+
318+
TestAppendInt<Int8>(0, "0");
319+
TestAppendInt<Int8>(1, "1");
320+
TestAppendInt<Int8>(-1, "-1");
321+
TestAppendInt<Int8>(127, "127");
322+
TestAppendInt<Int8>(-128, "-128");
323+
324+
TestAppendInt<Uint16>(0, "0");
325+
TestAppendInt<Uint16>(1, "1");
326+
TestAppendInt<Uint16>(9, "9");
327+
TestAppendInt<Uint16>(32768, "32768");
328+
TestAppendInt<Uint16>(65535, "65535");
329+
330+
TestAppendInt<Int16>(0, "0");
331+
TestAppendInt<Int16>(1, "1");
332+
TestAppendInt<Int16>(9, "9");
333+
TestAppendInt<Int16>(-1, "-1");
334+
TestAppendInt<Int16>(-32768, "-32768");
335+
TestAppendInt<Int16>(32767, "32767");
336+
337+
TestAppendInt<Int64>(0, "0");
338+
TestAppendInt<Int64>(1, "1");
339+
TestAppendInt<Int64>(9, "9");
340+
TestAppendInt<Int64>(10, "10");
341+
TestAppendInt<Int64>(-1, "-1");
342+
TestAppendInt<Int64>(-9, "-9");
343+
TestAppendInt<Int64>(-10, "-10");
344+
TestAppendInt<Int64>(LLONG_MAX, "9223372036854775807");
345+
TestAppendInt<Int64>(LLONG_MIN, "-9223372036854775808");
346+
TestAppendInt<Uint64>(18446744073709551615ull, "18446744073709551615");
347+
348+
// Octal
349+
TestAppendInt<int>(0, 8, "0");
350+
TestAppendInt<int>(7, 8, "7");
351+
TestAppendInt<int>(8, 8, "10");
352+
TestAppendInt<int>(63, 8, "77");
353+
TestAppendInt<int>(64, 8, "100");
354+
TestAppendInt<int>(-7, 8, "-7");
355+
TestAppendInt<int>(-8, 8, "-10");
356+
TestAppendInt<int>(-63, 8, "-77");
357+
TestAppendInt<int>(-64, 8, "-100");
358+
TestAppendInt<int>(INT_MIN, 8, "-20000000000");
359+
TestAppendInt<int>(INT_MAX, 8, "17777777777");
360+
TestAppendInt<Uint8>(255u, 8, "377");
361+
TestAppendInt<Uint16>(65535u, 8, "177777");
362+
363+
// Hexadecimal
364+
TestAppendInt<int>(0, 16, "0");
365+
TestAppendInt<int>(15, 16, "F");
366+
TestAppendInt<int>(16, 16, "10");
367+
TestAppendInt<int>(255, 16, "FF");
368+
TestAppendInt<int>(256, 16, "100");
369+
TestAppendInt<int>(-15, 16, "-F");
370+
TestAppendInt<int>(-16, 16, "-10");
371+
TestAppendInt<int>(-255, 16, "-FF");
372+
TestAppendInt<int>(-256, 16, "-100");
373+
TestAppendInt<int>(INT_MIN, 16, "-80000000");
374+
TestAppendInt<int>(INT_MAX, 16, "7FFFFFFF");
375+
TestAppendInt<Uint8>(255u, 16, "FF");
376+
TestAppendInt<Uint16>(65535u, 16, "FFFF");
377+
TestAppendInt<Uint64>(18446744073709551615ull, 16, "FFFFFFFFFFFFFFFF");
378+
379+
TestAppendInt<Uint32>(UINT_MAX, 10, "4294967295");
380+
TestAppendInt<Uint32>(UINT_MAX, 8, "37777777777");
381+
TestAppendInt<Uint32>(UINT_MAX, 16, "FFFFFFFF");
382+
383+
// Base 2
384+
TestAppendInt<int>(0, 2, "0");
385+
TestAppendInt<int>(1, 2, "1");
386+
TestAppendInt<int>(2, 2, "10");
387+
TestAppendInt<int>(-1, 2, "-1");
388+
TestAppendInt<int>(-2, 2, "-10");
389+
TestAppendInt<int>(INT_MAX, 2, "1111111111111111111111111111111");
390+
TestAppendInt<int>(INT_MIN, 2, "-10000000000000000000000000000000");
391+
392+
// Base 36
393+
TestAppendInt<int>(0, 36, "0");
394+
TestAppendInt<int>(9, 36, "9");
395+
TestAppendInt<int>(10, 36, "A");
396+
TestAppendInt<int>(35, 36, "Z");
397+
TestAppendInt<int>(36, 36, "10");
398+
TestAppendInt<int>(-35, 36, "-Z");
399+
TestAppendInt<int>(-36, 36, "-10");
400+
401+
{
402+
std::string s = "X:";
403+
AppendInt<int>(s, 42);
404+
EXPECT_EQ(s, "X:42");
405+
406+
AppendInt<int>(s, -7);
407+
EXPECT_EQ(s, "X:42-7");
408+
}
409+
410+
{
411+
std::string s;
412+
AppendInt(AppendInt(s, 12), 34);
413+
EXPECT_EQ(s, "1234");
414+
}
415+
}
416+
279417
} // namespace

0 commit comments

Comments
 (0)