|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef WINPTY_STRING_BUILDER_H |
|
#define WINPTY_STRING_BUILDER_H |
|
|
|
#include <array> |
|
#include <string> |
|
#include <type_traits> |
|
|
|
#ifdef STRING_BUILDER_TESTING |
|
#include <assert.h> |
|
#define STRING_BUILDER_CHECK(cond) assert(cond) |
|
#else |
|
#define STRING_BUILDER_CHECK(cond) |
|
#endif |
|
|
|
#include "WinptyAssert.h" |
|
|
|
template <typename C, size_t sz> |
|
struct ValueString { |
|
std::array<C, sz> m_array; |
|
size_t m_offset; |
|
size_t m_size; |
|
|
|
const C *c_str() const { return m_array.data() + m_offset; } |
|
const C *data() const { return m_array.data() + m_offset; } |
|
size_t size() const { return m_size; } |
|
std::basic_string<C> str() const { |
|
return std::basic_string<C>(data(), m_size); |
|
} |
|
}; |
|
|
|
#ifdef _MSC_VER |
|
|
|
|
|
|
|
#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) \ |
|
( \ |
|
__pragma(warning(push)) \ |
|
__pragma(warning(disable:4146)) \ |
|
(x) \ |
|
__pragma(warning(pop)) \ |
|
) |
|
#else |
|
#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) (x) |
|
#endif |
|
|
|
|
|
template <typename C, typename I> |
|
ValueString<C, sizeof(I) * 3 + 1 + 1> gdecOfInt(const I value) { |
|
typedef typename std::make_unsigned<I>::type U; |
|
auto unsValue = static_cast<U>(value); |
|
const bool isNegative = (value < 0); |
|
if (isNegative) { |
|
unsValue = STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(-unsValue); |
|
} |
|
decltype(gdecOfInt<C, I>(value)) out; |
|
auto &arr = out.m_array; |
|
C *const endp = arr.data() + arr.size(); |
|
C *outp = endp; |
|
*(--outp) = '\0'; |
|
STRING_BUILDER_CHECK(outp >= arr.data()); |
|
do { |
|
const int digit = unsValue % 10; |
|
unsValue /= 10; |
|
*(--outp) = '0' + digit; |
|
STRING_BUILDER_CHECK(outp >= arr.data()); |
|
} while (unsValue != 0); |
|
if (isNegative) { |
|
*(--outp) = '-'; |
|
STRING_BUILDER_CHECK(outp >= arr.data()); |
|
} |
|
out.m_offset = outp - arr.data(); |
|
out.m_size = endp - outp - 1; |
|
return out; |
|
} |
|
|
|
template <typename I> decltype(gdecOfInt<char, I>(0)) decOfInt(I i) { |
|
return gdecOfInt<char>(i); |
|
} |
|
|
|
template <typename I> decltype(gdecOfInt<wchar_t, I>(0)) wdecOfInt(I i) { |
|
return gdecOfInt<wchar_t>(i); |
|
} |
|
|
|
|
|
template <typename C, bool leadingZeros=false, typename I> |
|
ValueString<C, sizeof(I) * 2 + 1> ghexOfInt(const I value) { |
|
typedef typename std::make_unsigned<I>::type U; |
|
const auto unsValue = static_cast<U>(value); |
|
static const C hex[16] = {'0','1','2','3','4','5','6','7', |
|
'8','9','a','b','c','d','e','f'}; |
|
decltype(ghexOfInt<C, leadingZeros, I>(value)) out; |
|
auto &arr = out.m_array; |
|
C *outp = arr.data(); |
|
int inIndex = 0; |
|
int shift = sizeof(I) * 8 - 4; |
|
const int len = sizeof(I) * 2; |
|
if (!leadingZeros) { |
|
for (; inIndex < len - 1; ++inIndex, shift -= 4) { |
|
STRING_BUILDER_CHECK(shift >= 0 && shift < sizeof(unsValue) * 8); |
|
const int digit = (unsValue >> shift) & 0xF; |
|
if (digit != 0) { |
|
break; |
|
} |
|
} |
|
} |
|
for (; inIndex < len; ++inIndex, shift -= 4) { |
|
const int digit = (unsValue >> shift) & 0xF; |
|
*(outp++) = hex[digit]; |
|
STRING_BUILDER_CHECK(outp <= arr.data() + arr.size()); |
|
} |
|
*(outp++) = '\0'; |
|
STRING_BUILDER_CHECK(outp <= arr.data() + arr.size()); |
|
out.m_offset = 0; |
|
out.m_size = outp - arr.data() - 1; |
|
return out; |
|
} |
|
|
|
template <bool leadingZeros=false, typename I> |
|
decltype(ghexOfInt<char, leadingZeros, I>(0)) hexOfInt(I i) { |
|
return ghexOfInt<char, leadingZeros, I>(i); |
|
} |
|
|
|
template <bool leadingZeros=false, typename I> |
|
decltype(ghexOfInt<wchar_t, leadingZeros, I>(0)) whexOfInt(I i) { |
|
return ghexOfInt<wchar_t, leadingZeros, I>(i); |
|
} |
|
|
|
template <typename C> |
|
class GStringBuilder { |
|
public: |
|
typedef std::basic_string<C> StringType; |
|
|
|
GStringBuilder() {} |
|
GStringBuilder(size_t capacity) { |
|
m_out.reserve(capacity); |
|
} |
|
|
|
GStringBuilder &operator<<(C ch) { m_out.push_back(ch); return *this; } |
|
GStringBuilder &operator<<(const C *str) { m_out.append(str); return *this; } |
|
GStringBuilder &operator<<(const StringType &str) { m_out.append(str); return *this; } |
|
|
|
template <size_t sz> |
|
GStringBuilder &operator<<(const ValueString<C, sz> &str) { |
|
m_out.append(str.data(), str.size()); |
|
return *this; |
|
} |
|
|
|
private: |
|
|
|
|
|
|
|
|
|
template <typename P> |
|
typename std::enable_if< |
|
(std::is_same<P, char>::value || std::is_same<P, wchar_t>::value) && |
|
!std::is_same<C, P>::value, GStringBuilder&>::type |
|
operator<<(P ch) { |
|
ASSERT(false && "Method was not supposed to be reachable."); |
|
return *this; |
|
} |
|
|
|
public: |
|
GStringBuilder &operator<<(short i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(unsigned short i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(int i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(unsigned int i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(long i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(unsigned long i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(long long i) { return *this << gdecOfInt<C>(i); } |
|
GStringBuilder &operator<<(unsigned long long i) { return *this << gdecOfInt<C>(i); } |
|
|
|
GStringBuilder &operator<<(const void *p) { |
|
m_out.push_back(static_cast<C>('0')); |
|
m_out.push_back(static_cast<C>('x')); |
|
*this << ghexOfInt<C>(reinterpret_cast<uintptr_t>(p)); |
|
return *this; |
|
} |
|
|
|
StringType str() { return m_out; } |
|
StringType str_moved() { return std::move(m_out); } |
|
const C *c_str() const { return m_out.c_str(); } |
|
|
|
private: |
|
StringType m_out; |
|
}; |
|
|
|
typedef GStringBuilder<char> StringBuilder; |
|
typedef GStringBuilder<wchar_t> WStringBuilder; |
|
|
|
#endif |
|
|