JsonBuilder - convert string values to UTF-8 (#27)
* JsonBuilder - convert string values to UTF-8 Add support for converting data from UTF-NN to UTF-8 when the data is added to the JsonBuilder, i.e. you can now `push_back` a UTF-16 or UTF-32 string and it will be converted to UTF-8 as part of the `push_back` process. Alternative design would be to add new data types for JsonUtf16 and JsonUtf32, and then store the string in the JsonBuilder using its original encoding. This seems problematic -- anything reading string values would need to check the string's encoding and have separate code paths for each of the 3 encodings. * Also support converting latin1 and cp1252 input.
This commit is contained in:
Родитель
8eac958fc5
Коммит
1932340cd8
|
@ -1493,10 +1493,74 @@ class JsonBuilder
|
|||
_In_reads_bytes_opt_(cbData) void const* pbData)
|
||||
noexcept(false); // may throw bad_alloc, length_error
|
||||
|
||||
/*
|
||||
Advanced scenarios: Should only be called by JsonImplementType<T>::AddValueCommit
|
||||
that itself was called by JsonBuilder. This is the same as _newValueCommit but it
|
||||
converts SBCS data into UTF-8.
|
||||
|
||||
type should usually be JsonUtf8.
|
||||
|
||||
sbcsData must contain data encoded using a single-byte character set (SBCS).
|
||||
Characters < 0x80 are interpreted as ASCII. Characters >= 0x80 are looked up in the
|
||||
provided high128 table, char16 = high128[(UCHAR)char8 - 0x80]. Values in the
|
||||
high128 table should not be in the surrogate range 0xD800..0xDFFF.
|
||||
*/
|
||||
iterator
|
||||
_newValueCommitSbcsAsUtf8(
|
||||
JsonType type,
|
||||
std::string_view sbcsData,
|
||||
_In_reads_(128) char16_t const* high128)
|
||||
noexcept(false); // may throw bad_alloc, length_error
|
||||
|
||||
/*
|
||||
Advanced scenarios: Should only be called by JsonImplementType<T>::AddValueCommit
|
||||
that itself was called by JsonBuilder. This is the same as _newValueCommit but it
|
||||
converts UTF-8/16/32 data into UTF-8.
|
||||
|
||||
type should usually be JsonUtf8.
|
||||
|
||||
utfData must be a string-view-like thing (pointer to nul-terminated
|
||||
array of CHARs, or contiguous container of CHARs), where CHAR is one
|
||||
of char, char8_t, char16_t, char32_t, or wchar_t. The string is
|
||||
assumed to be UTF-8, UTF-16, or UTF-32 based on sizeof(CHAR).
|
||||
*/
|
||||
template<
|
||||
class DataStringView,
|
||||
class DataChar = typename JsonInternal::StringTypeOk<DataStringView>::type>
|
||||
iterator
|
||||
_newValueCommitUtfAsUtf8(
|
||||
JsonType type,
|
||||
DataStringView const& utfData)
|
||||
noexcept(false) // may throw bad_alloc, length_error
|
||||
{
|
||||
std::basic_string_view<DataChar> dataView{ utfData };
|
||||
using char_type = typename JsonInternal::CharTypeOk<DataChar>::char_type;
|
||||
return NewValueCommitUtfAsUtf8Impl(type, dataView.size(), reinterpret_cast<char_type const*>(dataView.data()));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void CreateRoot() noexcept(false);
|
||||
|
||||
iterator
|
||||
NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char const* pchDataUtf8)
|
||||
noexcept(false); // may throw bad_alloc, length_error
|
||||
iterator
|
||||
NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char16_t const* pchDataUtf16)
|
||||
noexcept(false); // may throw bad_alloc, length_error
|
||||
iterator
|
||||
NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char32_t const* pchDataUtf32)
|
||||
noexcept(false); // may throw bad_alloc, length_error
|
||||
|
||||
/*
|
||||
Common implementation for NewValueInit.
|
||||
If reallocation occurred and pName pointed into old storage, returns corresponding
|
||||
|
@ -1743,6 +1807,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
String view using Latin-1 (ISO-8859-1) encoding.
|
||||
*/
|
||||
struct latin1_view : std::string_view
|
||||
{
|
||||
using std::string_view::string_view;
|
||||
};
|
||||
|
||||
/*
|
||||
String view using cp1252 encoding.
|
||||
*/
|
||||
struct cp1252_view : std::string_view
|
||||
{
|
||||
using std::string_view::string_view;
|
||||
};
|
||||
|
||||
// JsonImplementType
|
||||
|
||||
/*
|
||||
|
@ -1817,6 +1897,17 @@ class JsonImplementType
|
|||
static JsonIterator AddValueCommit(JsonBuilder& builder, T InRef data); \
|
||||
}
|
||||
|
||||
#define JSON_DECLARE_JsonImplementType_AddValueSz(CH) \
|
||||
template<> \
|
||||
class JsonImplementType<CH*> \
|
||||
{ \
|
||||
public: \
|
||||
static JsonIterator AddValueCommit(JsonBuilder& builder, _In_z_ CH const* psz); \
|
||||
}; \
|
||||
template<> \
|
||||
class JsonImplementType<CH const*> \
|
||||
: public JsonImplementType<CH*> {}; \
|
||||
|
||||
JSON_DECLARE_JsonImplementType(bool,, );
|
||||
|
||||
JSON_DECLARE_JsonImplementType(unsigned char,, );
|
||||
|
@ -1837,17 +1928,77 @@ JSON_DECLARE_JsonImplementType(double,, );
|
|||
JSON_DECLARE_JsonImplementType(TimeStruct,, );
|
||||
JSON_DECLARE_JsonImplementType(std::chrono::system_clock::time_point,, );
|
||||
JSON_DECLARE_JsonImplementType(UuidStruct, const&, const&);
|
||||
JSON_DECLARE_JsonImplementType(std::string_view,, );
|
||||
JSON_DECLARE_JsonImplementType_AddValue(latin1_view, );
|
||||
JSON_DECLARE_JsonImplementType_AddValue(cp1252_view, );
|
||||
|
||||
JSON_DECLARE_JsonImplementType(std::string_view,, );
|
||||
JSON_DECLARE_JsonImplementType_AddValue(std::wstring_view, );
|
||||
JSON_DECLARE_JsonImplementType_AddValue(std::u16string_view, );
|
||||
JSON_DECLARE_JsonImplementType_AddValue(std::u32string_view, );
|
||||
|
||||
JSON_DECLARE_JsonImplementType_AddValueSz(char);
|
||||
JSON_DECLARE_JsonImplementType_AddValueSz(wchar_t);
|
||||
JSON_DECLARE_JsonImplementType_AddValueSz(char16_t);
|
||||
JSON_DECLARE_JsonImplementType_AddValueSz(char32_t);
|
||||
|
||||
#ifdef __cpp_lib_char8_t // Support u8string_view and char8_t, inline so they work even if lib builds as C++17.
|
||||
|
||||
// JSON_DECLARE_JsonImplementType(std::u8string_view,, ):
|
||||
template<>
|
||||
class JsonImplementType<char*>
|
||||
class JsonImplementType<std::u8string_view>
|
||||
{
|
||||
public:
|
||||
static JsonIterator AddValueCommit(JsonBuilder& builder, _In_z_ char const* psz);
|
||||
|
||||
static std::u8string_view
|
||||
GetUnchecked(JsonValue const& value) noexcept
|
||||
{
|
||||
auto charResult = JsonImplementType<std::string_view>::GetUnchecked(value);
|
||||
return { reinterpret_cast<char8_t const*>(charResult.data()), charResult.size() };
|
||||
}
|
||||
|
||||
static bool
|
||||
ConvertTo(JsonValue const& value, std::u8string_view& result) noexcept
|
||||
{
|
||||
bool success;
|
||||
|
||||
if (value.Type() == JsonUtf8)
|
||||
{
|
||||
result = GetUnchecked(value);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = std::u8string_view();
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static JsonIterator
|
||||
AddValueCommit(JsonBuilder& builder, std::u8string_view data)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, data);
|
||||
}
|
||||
};
|
||||
|
||||
// JSON_DECLARE_JsonImplementType_AddValueSz(char8_t):
|
||||
template<>
|
||||
class JsonImplementType<char8_t*>
|
||||
{
|
||||
public:
|
||||
|
||||
static JsonIterator
|
||||
AddValueCommit(JsonBuilder& builder, _In_z_ char8_t const* psz)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, psz);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class JsonImplementType<char const*> : public JsonImplementType<char*>
|
||||
{};
|
||||
class JsonImplementType<char8_t const*>
|
||||
: public JsonImplementType<char8_t*> {};
|
||||
|
||||
#endif // __cpp_lib_char8_t
|
||||
|
||||
} // namespace jsonbuilder
|
||||
|
|
|
@ -115,6 +115,47 @@ Utf32ToUtf8(
|
|||
return iDest;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
SbcsToUtf8(
|
||||
_Out_writes_to_(cchSrc * 3, return) char unsigned* pchDest,
|
||||
_In_reads_(cchSrc) char const* pchSrc,
|
||||
unsigned cchSrc,
|
||||
_In_reads_(128) char16_t const* high128)
|
||||
noexcept
|
||||
{
|
||||
unsigned iDest = 0;
|
||||
for (unsigned iSrc = 0; iSrc != cchSrc; iSrc += 1)
|
||||
{
|
||||
char unsigned const ch8 = pchSrc[iSrc];
|
||||
if (ch8 < 0x80)
|
||||
{
|
||||
// ASCII character - don't use lookup table.
|
||||
pchDest[iDest++] = static_cast<char unsigned>(ch8);
|
||||
continue;
|
||||
}
|
||||
|
||||
char16_t const ch = high128[ch8 - 0x80];
|
||||
if (ch < 0x80)
|
||||
{
|
||||
pchDest[iDest++] = static_cast<char unsigned>(ch);
|
||||
}
|
||||
else if (ch < 0x800)
|
||||
{
|
||||
pchDest[iDest++] = static_cast<char unsigned>(0xC0 | (ch >> 6));
|
||||
pchDest[iDest++] = static_cast<char unsigned>(0x80 | (ch & 0x3F));
|
||||
}
|
||||
else
|
||||
{
|
||||
// It could be in the surrogate range, but we don't care.
|
||||
pchDest[iDest++] = static_cast<char unsigned>(0xE0 | (ch >> 12));
|
||||
pchDest[iDest++] = static_cast<char unsigned>(0x80 | ((ch >> 6) & 0x3F));
|
||||
pchDest[iDest++] = static_cast<char unsigned>(0x80 | (ch & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
return iDest;
|
||||
}
|
||||
|
||||
// JsonValue
|
||||
|
||||
namespace jsonbuilder {
|
||||
|
@ -1174,6 +1215,107 @@ JsonBuilder::_newValueCommit(
|
|||
return iterator(const_iterator(this, newIndex));
|
||||
}
|
||||
|
||||
JsonBuilder::iterator
|
||||
JsonBuilder::_newValueCommitSbcsAsUtf8(
|
||||
JsonType type,
|
||||
std::string_view sbcsData,
|
||||
_In_reads_(128) char16_t const* high128)
|
||||
noexcept(false) // may throw bad_alloc, length_error
|
||||
{
|
||||
auto constexpr WorstCaseMultiplier = 3u; // 1 UTF-16 code unit -> 3 UTF-8 code units.
|
||||
if (sbcsData.size() > DataMax / WorstCaseMultiplier)
|
||||
{
|
||||
JsonThrowLengthError("JsonBuilder - cchData too large");
|
||||
}
|
||||
|
||||
// Create node. Reserve worst-case size for data.
|
||||
auto const cchSrc = static_cast<unsigned>(sbcsData.size());
|
||||
auto const valueIt = _newValueCommit(type, cchSrc * WorstCaseMultiplier, nullptr);
|
||||
|
||||
// Copy data into node, converting to UTF-8.
|
||||
auto& value = GetValue(valueIt.m_index);
|
||||
auto const valueDataIndex = valueIt.m_index + DATA_OFFSET(value.m_cchName);
|
||||
auto const cbDest = SbcsToUtf8(reinterpret_cast<unsigned char*>(&m_storage[valueDataIndex]), sbcsData.data(), cchSrc, high128);
|
||||
|
||||
// Shrink to fit actual data size.
|
||||
value.m_cbData = cbDest; // Shrink
|
||||
m_storage.resize(valueDataIndex + (cbDest + StorageSize - 1) / StorageSize); // Shrink
|
||||
|
||||
return valueIt;
|
||||
}
|
||||
|
||||
JsonBuilder::iterator
|
||||
JsonBuilder::NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char const* pchDataUtf8)
|
||||
noexcept(false)
|
||||
{
|
||||
if (cchData > DataMax)
|
||||
{
|
||||
JsonThrowLengthError("JsonBuilder - cchData too large");
|
||||
}
|
||||
return _newValueCommit(type, static_cast<unsigned>(cchData), pchDataUtf8);
|
||||
}
|
||||
|
||||
JsonBuilder::iterator
|
||||
JsonBuilder::NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char16_t const* pchDataUtf16)
|
||||
noexcept(false) // may throw bad_alloc, length_error
|
||||
{
|
||||
auto constexpr WorstCaseMultiplier = 3u; // 1 UTF-16 code unit -> 3 UTF-8 code units.
|
||||
if (cchData > DataMax / WorstCaseMultiplier)
|
||||
{
|
||||
JsonThrowLengthError("JsonBuilder - cchData too large");
|
||||
}
|
||||
|
||||
// Create node. Reserve worst-case size for data.
|
||||
auto const cchSrc = static_cast<unsigned>(cchData);
|
||||
auto const valueIt = _newValueCommit(type, cchSrc * WorstCaseMultiplier, nullptr);
|
||||
|
||||
// Copy data into node, converting to UTF-8.
|
||||
auto& value = GetValue(valueIt.m_index);
|
||||
auto const valueDataIndex = valueIt.m_index + DATA_OFFSET(value.m_cchName);
|
||||
auto const cbDest = Utf16ToUtf8(reinterpret_cast<unsigned char*>(&m_storage[valueDataIndex]), pchDataUtf16, cchSrc);
|
||||
|
||||
// Shrink to fit actual data size.
|
||||
value.m_cbData = cbDest; // Shrink
|
||||
m_storage.resize(valueDataIndex + (cbDest + StorageSize - 1) / StorageSize); // Shrink
|
||||
|
||||
return valueIt;
|
||||
}
|
||||
|
||||
JsonBuilder::iterator
|
||||
JsonBuilder::NewValueCommitUtfAsUtf8Impl(
|
||||
JsonType type,
|
||||
JsonInternal::JSON_SIZE_T cchData,
|
||||
_In_reads_(cchData) char32_t const* pchDataUtf32)
|
||||
noexcept(false) // may throw bad_alloc, length_error
|
||||
{
|
||||
auto constexpr WorstCaseMultiplier = 4u; // 1 UTF-32 code unit -> 4 UTF-8 code units.
|
||||
if (cchData > DataMax / WorstCaseMultiplier)
|
||||
{
|
||||
JsonThrowLengthError("JsonBuilder - cchData too large");
|
||||
}
|
||||
|
||||
// Create node. Reserve worst-case size for data.
|
||||
auto const cchSrc = static_cast<unsigned>(cchData);
|
||||
auto const valueIt = _newValueCommit(type, cchSrc * WorstCaseMultiplier, nullptr);
|
||||
|
||||
// Copy data into node, converting to UTF-8.
|
||||
auto& value = GetValue(valueIt.m_index);
|
||||
auto const valueDataIndex = valueIt.m_index + DATA_OFFSET(value.m_cchName);
|
||||
auto const cbDest = Utf32ToUtf8(reinterpret_cast<unsigned char*>(&m_storage[valueDataIndex]), pchDataUtf32, cchSrc);
|
||||
|
||||
// Shrink to fit actual data size.
|
||||
value.m_cbData = cbDest; // Shrink
|
||||
m_storage.resize(valueDataIndex + (cbDest + StorageSize - 1) / StorageSize); // Shrink
|
||||
|
||||
return valueIt;
|
||||
}
|
||||
|
||||
void JsonBuilder::AssertNotEnd(Index index) noexcept
|
||||
{
|
||||
(void) index; // Unreferenced parameter in release builds.
|
||||
|
@ -1631,7 +1773,31 @@ JsonImplementType<char*>::AddValueCommit(
|
|||
JsonBuilder& builder,
|
||||
_In_z_ char const* psz)
|
||||
{
|
||||
return builder._newValueCommit(JsonUtf8, static_cast<unsigned>(strlen(psz)), psz);
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, psz);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<wchar_t*>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
_In_z_ wchar_t const* psz)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, psz);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<char16_t*>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
_In_z_ char16_t const* psz)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, psz);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<char32_t*>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
_In_z_ char32_t const* psz)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, psz);
|
||||
}
|
||||
|
||||
std::string_view
|
||||
|
@ -1668,7 +1834,69 @@ JsonImplementType<std::string_view>::AddValueCommit(
|
|||
JsonBuilder& builder,
|
||||
std::string_view data)
|
||||
{
|
||||
return builder._newValueCommit(JsonUtf8, static_cast<unsigned>(data.size()), data.data());
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, data);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<std::wstring_view>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
std::wstring_view data)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, data);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<std::u16string_view>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
std::u16string_view data)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, data);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<std::u32string_view>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
std::u32string_view data)
|
||||
{
|
||||
return builder._newValueCommitUtfAsUtf8(JsonUtf8, data);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<latin1_view>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
latin1_view data)
|
||||
{
|
||||
static constexpr char16_t High128[] = {
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
static_assert(sizeof(High128) == 128 * sizeof(High128[0]), "High128 size");
|
||||
return builder._newValueCommitSbcsAsUtf8(JsonUtf8, data, High128);
|
||||
}
|
||||
|
||||
JsonIterator
|
||||
JsonImplementType<cp1252_view>::AddValueCommit(
|
||||
JsonBuilder& builder,
|
||||
cp1252_view data)
|
||||
{
|
||||
static constexpr char16_t High128[] = {
|
||||
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
|
||||
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
static_assert(sizeof(High128) == 128 * sizeof(High128[0]), "High128 size");
|
||||
return builder._newValueCommitSbcsAsUtf8(JsonUtf8, data, High128);
|
||||
}
|
||||
|
||||
// JsonTime
|
||||
|
|
|
@ -7,7 +7,7 @@ if(NOT WIN32)
|
|||
endif()
|
||||
|
||||
add_executable(jsonbuilderTest CatchMain.cpp TestBuilder.cpp TestRenderer.cpp)
|
||||
target_compile_features(jsonbuilderTest PRIVATE cxx_std_17)
|
||||
target_compile_features(jsonbuilderTest PRIVATE cxx_std_20)
|
||||
target_link_libraries(jsonbuilderTest PRIVATE jsonbuilder Catch2::Catch2 ${LIB_TARGET_UUID})
|
||||
|
||||
include(CTest)
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include <jsonbuilder/JsonBuilder.h>
|
||||
#include <string.h>
|
||||
|
||||
#define USTRING(prefix) prefix ## "\u0024\u00A3\u0418\u0939\u20AC\uD55C\U00010348"
|
||||
#define CHAR_USTRING() reinterpret_cast<char const*>(USTRING(u8))
|
||||
|
||||
#ifdef _WIN32
|
||||
using uuid_t = char unsigned[16];
|
||||
static void uuid_generate(uuid_t uuid)
|
||||
|
@ -101,8 +104,6 @@ static void TestInputOutputScalar()
|
|||
|
||||
JsonBuilder b;
|
||||
|
||||
#define USTRING(prefix) prefix ## "\u0024\u00A3\u0418\u0939\u20AC\uD55C\U00010348"
|
||||
|
||||
b.push_back(b.root(), USTRING(u), InputLimits::lowest());
|
||||
b.push_back(b.root(), USTRING(U), InputLimits::min());
|
||||
b.push_back(b.root(), USTRING(L), InputLimits::max());
|
||||
|
@ -115,25 +116,25 @@ static void TestInputOutputScalar()
|
|||
InputType i;
|
||||
|
||||
auto it = b.begin();
|
||||
REQUIRE(it->Name() == USTRING(u8));
|
||||
REQUIRE(it->Name() == CHAR_USTRING());
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::lowest());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::lowest());
|
||||
|
||||
++it;
|
||||
REQUIRE(it->Name() == USTRING(u8));
|
||||
REQUIRE(it->Name() == CHAR_USTRING());
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::min());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::min());
|
||||
|
||||
++it;
|
||||
REQUIRE(it->Name() == USTRING(u8));
|
||||
REQUIRE(it->Name() == CHAR_USTRING());
|
||||
REQUIRE(it->GetUnchecked<InputType>() == InputLimits::max());
|
||||
REQUIRE(it->ConvertTo(i));
|
||||
REQUIRE(i == InputLimits::max());
|
||||
|
||||
++it;
|
||||
REQUIRE(it->Name() == USTRING(u8));
|
||||
REQUIRE(it->Name() == CHAR_USTRING());
|
||||
REQUIRE(
|
||||
it->GetUnchecked<InputType>() ==
|
||||
static_cast<InputType>(OutputLimits::lowest()));
|
||||
|
@ -242,6 +243,8 @@ TEST_CASE("JsonBuilder string push_back")
|
|||
{
|
||||
JsonBuilder b;
|
||||
|
||||
// char
|
||||
|
||||
SECTION("push_back std::string_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", std::string_view{ "ABCDE" });
|
||||
|
@ -268,9 +271,162 @@ TEST_CASE("JsonBuilder string push_back")
|
|||
|
||||
SECTION("push_back const char[]")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", "HIJ");
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "HIJ");
|
||||
auto itr = b.push_back(b.root(), "", CHAR_USTRING());
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == CHAR_USTRING());
|
||||
}
|
||||
|
||||
SECTION("push_back latin1_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", latin1_view{ "ABCDE\x80\x90\x9F\xA0\xB0\xF0\xFF" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == reinterpret_cast<char const*>(u8"ABCDE\u0080\u0090\u009F\u00A0\u00B0\u00F0\u00FF"));
|
||||
}
|
||||
|
||||
SECTION("push_back cp1252_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", cp1252_view{ "ABCDE\x80\x90\x9F\xA0\xB0\xF0\xFF" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == reinterpret_cast<char const*>(u8"ABCDE\u20AC\u0090\u0178\u00A0\u00B0\u00F0\u00FF"));
|
||||
}
|
||||
|
||||
// wchar_t
|
||||
|
||||
SECTION("push_back std::wstring_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", std::wstring_view{ L"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back std::wstring")
|
||||
{
|
||||
auto itr = b.push_back<std::wstring_view>(b.root(), "", std::wstring{ L"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back wchar_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", const_cast<wchar_t*>(L"ABC"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
}
|
||||
|
||||
SECTION("push_back const wchar_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", static_cast<const wchar_t*>(L"DEF"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "DEF");
|
||||
}
|
||||
|
||||
SECTION("push_back const wchar_t[]")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", USTRING(L));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == CHAR_USTRING());
|
||||
}
|
||||
|
||||
// char8_t
|
||||
|
||||
#ifdef __cpp_lib_char8_t // C++20
|
||||
|
||||
SECTION("push_back std::u8string_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", std::u8string_view{ u8"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
REQUIRE(itr->GetUnchecked<std::u8string_view>() == u8"ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back std::u8string")
|
||||
{
|
||||
auto itr = b.push_back<std::u8string_view>(b.root(), "", std::u8string{ u8"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
REQUIRE(itr->GetUnchecked<std::u8string_view>() == u8"ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back char8_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", const_cast<char8_t*>(u8"ABC"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
REQUIRE(itr->GetUnchecked<std::u8string_view>() == u8"ABC");
|
||||
}
|
||||
|
||||
SECTION("push_back const char8_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", static_cast<const char8_t*>(u8"DEF"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "DEF");
|
||||
REQUIRE(itr->GetUnchecked<std::u8string_view>() == u8"DEF");
|
||||
}
|
||||
|
||||
#endif // __cpp_lib_char8_t
|
||||
|
||||
SECTION("push_back const char8_t[]")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", USTRING(u8));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == CHAR_USTRING());
|
||||
#ifdef __cpp_lib_char8_t // C++20
|
||||
REQUIRE(itr->GetUnchecked<std::u8string_view>() == USTRING(u8));
|
||||
#endif // __cpp_lib_char8_t
|
||||
}
|
||||
|
||||
// char16_t
|
||||
|
||||
SECTION("push_back std::u16string_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", std::u16string_view{ u"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back std::u16string")
|
||||
{
|
||||
auto itr = b.push_back<std::u16string_view>(b.root(), "", std::u16string{ u"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back char16_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", const_cast<char16_t*>(u"ABC"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
}
|
||||
|
||||
SECTION("push_back const char16_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", static_cast<const char16_t*>(u"DEF"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "DEF");
|
||||
}
|
||||
|
||||
SECTION("push_back const char16_t[]")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", USTRING(u));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == CHAR_USTRING());
|
||||
}
|
||||
|
||||
// char32_t
|
||||
|
||||
SECTION("push_back std::u32string_view")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", std::u32string_view{ U"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back std::u32string")
|
||||
{
|
||||
auto itr = b.push_back<std::u32string_view>(b.root(), "", std::u32string{ U"ABCDE" });
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABCDE");
|
||||
}
|
||||
|
||||
SECTION("push_back char32_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", const_cast<char32_t*>(U"ABC"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "ABC");
|
||||
}
|
||||
|
||||
SECTION("push_back const char32_t*")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", static_cast<const char32_t*>(U"DEF"));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == "DEF");
|
||||
}
|
||||
|
||||
SECTION("push_back const char32_t[]")
|
||||
{
|
||||
auto itr = b.push_back(b.root(), "", USTRING(U));
|
||||
REQUIRE(itr->GetUnchecked<std::string_view>() == CHAR_USTRING());
|
||||
}
|
||||
|
||||
b.ValidateData();
|
||||
}
|
||||
|
||||
TEST_CASE("JsonBuilder chrono push_back", "[builder]")
|
||||
|
|
Загрузка…
Ссылка в новой задаче