Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SqlFixedString.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlColumnTypeDefinitions.hpp"
6#include "Core.hpp"
7#include "StringInterface.hpp"
8#include "UnicodeConverter.hpp"
9
10#include <format>
11#include <ranges>
12#include <stdexcept>
13#include <utility>
14
15namespace Lightweight
16{
17
18/// Enum to define the behavior of SqlFixedString after retrieval from the database, in case the retrieved string is shorter
19/// than the fixed capacity.
20enum class SqlFixedStringMode : uint8_t
21{
22 /// The string is treated as fixed-size, and the size is always set to the fixed capacity.
23 FIXED_SIZE,
24 /// The string is treated as fixed-size, but the size is set to the actual size of the retrieved string. If the
25 /// retrieved string is shorter than the fixed capacity, the remaining characters are right-trimmed and the size is set
26 /// to the actual size of the retrieved string.
27 FIXED_SIZE_RIGHT_TRIMMED,
28 /// The string is treated as variable-size, and the size is set to the actual size of the retrieved string.
29 VARIABLE_SIZE,
30};
31
32/// SQL fixed-capacity string that mimmicks standard library string/string_view with a fixed-size underlying
33/// buffer.
34///
35/// The underlying storage will not be guaranteed to be `\0`-terminated unless
36/// a call to mutable/const c_str() has been performed.
37///
38/// @ingroup DataTypes
39template <std::size_t N, typename T = char, SqlFixedStringMode Mode = SqlFixedStringMode::FIXED_SIZE>
41{
42 private:
43 T _data[N + 1] {};
44 std::size_t _size = 0;
45
46 public:
47 /// The element type of the string.
48 using value_type = T;
49
50 /// Iterator type for the string.
51 using iterator = T*;
52
53 /// Const iterator type for the string.
54 using const_iterator = T const*;
55
56 /// Pointer type for the string.
57 using pointer_type = T*;
58
59 /// Const pointer type for the string.
60 using const_pointer_type = T const*;
61
62 /// The specified width of the string, which is also the maximum size of the string.
63 static constexpr std::size_t Capacity = N;
64
65 /// Handling mode for the post-retrieval operation that defines how the string
66 /// should be treated when it is retrieved from the database.
67 static constexpr SqlFixedStringMode PostRetrieveOperation = Mode;
68
69 /// Constructs a fixed-size string from a string literal.
70 template <std::size_t SourceSize>
71 constexpr LIGHTWEIGHT_FORCE_INLINE SqlFixedString(T const (&text)[SourceSize]):
72 _size { SourceSize - 1 }
73 {
74 static_assert(SourceSize <= N + 1, "RHS string size must not exceed target string's capacity.");
75 std::copy_n(text, SourceSize, _data);
76 }
77
78 /// Defaulted default constructor.
79 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString() noexcept = default;
80
81 /// Defaulted copy constructor.
82 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(SqlFixedString const&) noexcept = default;
83
84 /// Defaulted copy assignment operator.
85 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString& operator=(SqlFixedString const&) noexcept = default;
86
87 /// Defaulted move constructor.
88 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(SqlFixedString&&) noexcept = default;
89
90 /// Defaulted move assignment operator.
91 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString& operator=(SqlFixedString&&) noexcept = default;
92
93 /// Defaulted destructor.
94 LIGHTWEIGHT_FORCE_INLINE constexpr ~SqlFixedString() noexcept = default;
95
96 /// Constructs a fixed-size string from a string view.
97 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string_view<T> s) noexcept:
98 _size { (std::min) (N, s.size()) }
99 {
100 std::copy_n(s.data(), _size, _data); // NOLINT(bugprone-suspicious-stringview-data-usage)
101 }
102
103 /// Constructs a fixed-size string from a string.
104 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string<T> const& s) noexcept:
105 _size { (std::min) (N, s.size()) }
106 {
107 std::copy_n(s.data(), _size, _data);
108 }
109
110 /// Constructs a fixed-size string from a string pointer and length.
111 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const* s, std::size_t len) noexcept:
112 _size { (std::min) (N, len) }
113 {
114 std::copy_n(s, _size, _data);
115 }
116
117 /// Constructs a fixed-size string from a string pointer and end pointer.
118 LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const* s, T const* e) noexcept:
119 _size { (std::min) (N, static_cast<std::size_t>(e - s)) }
120 {
121 std::copy(s, e, _data);
122 }
123
124 /// Reserves capacity for the string. Throws if capacity exceeds the maximum.
125 LIGHTWEIGHT_FORCE_INLINE void reserve(std::size_t capacity)
126 {
127 if (capacity > N)
128 throw std::length_error(std::format("SqlFixedString: capacity {} exceeds maximum capacity {}", capacity, N));
129 }
130
131 /// Tests if the string is empty.
132 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
133 {
134 return _size == 0;
135 }
136
137 /// Returns the size of the string.
138 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
139 {
140 return _size;
141 }
142
143 /// Sets the size of the string, capped at the maximum capacity.
144 // NOLINTNEXTLINE(readability-identifier-naming)
145 LIGHTWEIGHT_FORCE_INLINE /*TODO constexpr*/ void setsize(std::size_t n) noexcept
146 {
147 auto const newSize = (std::min) (n, N);
148 _size = newSize;
149 _data[newSize] = '\0';
150 }
151
152 /// Resizes the string.
153 ///
154 /// This sets the size of the string to `n`. If `n` is greater than the current size,
155 /// capped at the maximum capacity.
156 LIGHTWEIGHT_FORCE_INLINE constexpr void resize(std::size_t n) noexcept
157 {
158 auto const newSize = (std::min) (n, N);
159 _size = newSize;
160 _data[newSize] = '\0';
161 }
162
163 /// Returns the maximum capacity of the string.
164 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t capacity() const noexcept
165 {
166 return N;
167 }
168
169 /// Clears the string.
170 LIGHTWEIGHT_FORCE_INLINE constexpr void clear() noexcept
171 {
172 _size = 0;
173 }
174
175 // /// Assigns a string literal to the string.
176 // template <std::size_t SourceSize>
177 // LIGHTWEIGHT_FORCE_INLINE constexpr void assign(T const (&source)[SourceSize]) noexcept
178 // {
179 // static_assert(SourceSize <= N + 1, "Source string must not overflow the target string's capacity.");
180 // _size = SourceSize - 1;
181 // std::copy_n(source, SourceSize, _data);
182 // }
183
184 // /// Assigns a string view to the string.
185 // LIGHTWEIGHT_FORCE_INLINE constexpr void assign(std::basic_string_view<T> s) noexcept
186 // {
187 // _size = (std::min) (N, s.size());
188 // std::copy_n(s.data(), _size, _data); // NOLINT(bugprone-suspicious-stringview-data-usage)
189 // }
190
191 /// Appends a character to the string.
192 LIGHTWEIGHT_FORCE_INLINE constexpr void push_back(T c) noexcept
193 {
194 if (_size < N)
195 {
196 _data[_size] = c;
197 ++_size;
198 }
199 }
200
201 /// Removes the last character from the string.
202 LIGHTWEIGHT_FORCE_INLINE constexpr void pop_back() noexcept
203 {
204 if (_size > 0)
205 --_size;
206 }
207
208 /// Returns a sub string of the string.
209 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> substr(
210 std::size_t offset = 0, std::size_t count = (std::numeric_limits<std::size_t>::max)()) const noexcept
211 {
212 if (offset >= _size)
213 return {};
214 if (count == (std::numeric_limits<std::size_t>::max)())
215 return std::basic_string_view<T>(_data + offset, _size - offset);
216 if (offset + count > _size)
217 return std::basic_string_view<T>(_data + offset, _size - offset);
218 return std::basic_string_view<T>(_data + offset, count);
219 }
220
221 /// Returns a string view of the string.
222 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> str() const noexcept
223 {
224 return std::basic_string_view<T> { _data, _size };
225 }
226
227 // clang-format off
228 /// Returns a pointer to the underlying mutable data.
229 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr pointer_type data() noexcept { return _data; }
230 /// Returns a mutable iterator to the beginning.
231 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr iterator begin() noexcept { return _data; }
232 /// Returns a mutable iterator to the end.
233 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr iterator end() noexcept { return _data + size(); }
234 /// Returns a mutable reference to the element at the given index.
235 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T& operator[](std::size_t i) noexcept { return _data[i]; }
236
237 /// Returns a pointer to the null-terminated C string.
238 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type c_str() const noexcept { return _data; }
239 /// Returns a const pointer to the underlying data.
240 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type data() const noexcept { return _data; }
241 /// Returns a const iterator to the beginning.
242 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator begin() const noexcept { return _data; }
243 /// Returns a const iterator to the end.
244 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator end() const noexcept { return _data + size(); }
245 /// Returns a const reference to the element at the given index.
246 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr T const& operator[](std::size_t i) const noexcept { return _data[i]; }
247 // clang-format on
248
249 /// Returns a std::basic_string<T> from the string.
250 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string<T> ToString() const noexcept
251 {
252 return { _data, _size };
253 }
254
255 /// Returns a std::basic_string<T> from the string.
256 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr explicit operator std::basic_string<T>() const noexcept
257 {
258 return ToString();
259 }
260
261 /// Returns a std::basic_string_view<T> from the string.
262 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view<T> ToStringView() const noexcept
263 {
264 return { _data, _size };
265 }
266
267 /// Returns a std::basic_string_view<T> from the string.
268 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE constexpr explicit operator std::basic_string_view<T>() const noexcept
269 {
270 return ToStringView();
271 }
272
273 /// Three-way comparison operator.
274 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
275 LIGHTWEIGHT_FORCE_INLINE std::weak_ordering operator<=>(
276 SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
277 {
278 if ((void*) this == (void*) &other) [[unlikely]]
279 return std::weak_ordering::equivalent;
280
281 for (auto const i: std::views::iota(0U, (std::min) (size(), other.size())))
282 if (auto const cmp = _data[i] <=> other._data[i]; cmp != std::weak_ordering::equivalent) [[unlikely]]
283 return cmp;
284 return size() <=> other.size();
285 }
286
287 /// Equality comparison operator.
288 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
289 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
290 {
291 return (*this <=> other) == std::weak_ordering::equivalent;
292 }
293
294 /// Inequality comparison operator.
295 template <std::size_t OtherSize, SqlFixedStringMode OtherMode>
296 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(SqlFixedString<OtherSize, T, OtherMode> const& other) const noexcept
297 {
298 return !(*this == other);
299 }
300
301 /// Equality comparison with a string view.
302 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(std::basic_string_view<T> other) const noexcept
303 {
304 return (substr() <=> other) == std::weak_ordering::equivalent;
305 }
306
307 /// Inequality comparison with a string view.
308 LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(std::basic_string_view<T> other) const noexcept
309 {
310 return !(*this == other);
311 }
312};
313
314static_assert(SqlStringInterface<SqlFixedString<10>>);
315
316template <std::size_t N, typename CharT, SqlFixedStringMode Mode>
317struct detail::SqlViewHelper<SqlFixedString<N, CharT, Mode>>
318{
319 static LIGHTWEIGHT_FORCE_INLINE std::basic_string_view<CharT> View(SqlFixedString<N, CharT, Mode> const& str) noexcept
320 {
321 return { str.data(), str.size() };
322 }
323};
324
325namespace detail
326{
327
328 template <typename>
329 struct IsSqlFixedStringTypeImpl: std::false_type
330 {
331 };
332
333 template <std::size_t N, typename T, SqlFixedStringMode Mode>
334 struct IsSqlFixedStringTypeImpl<SqlFixedString<N, T, Mode>>: std::true_type
335 {
336 };
337
338} // namespace detail
339
340template <typename T>
341constexpr bool IsSqlFixedString = detail::IsSqlFixedStringTypeImpl<T>::value;
342
343/// Fixed-size string of element type `char` with a capacity of `N` characters.
344///
345/// @ingroup DataTypes
346template <std::size_t N>
348
349/// Fixed-size string of element type `char16_t` with a capacity of `N` characters.
350///
351/// @ingroup DataTypes
352template <std::size_t N>
354
355/// Fixed-size string of element type `char32_t` with a capacity of `N` characters.
356///
357/// @ingroup DataTypes
358template <std::size_t N>
360
361/// Fixed-size string of element type `wchar_t` with a capacity of `N` characters.
362///
363/// @ingroup DataTypes
364template <std::size_t N>
366
367/// Fixed-size (right-trimmed) string of element type `char` with a capacity of `N` characters.
368///
369/// @ingroup DataTypes
370template <std::size_t N>
372
373/// Fixed-size (right-trimmed) string of element type `wchar_t` with a capacity of `N` characters.
374///
375/// @ingroup DataTypes
376template <std::size_t N>
378
379template <std::size_t N, typename T = char>
381
382template <std::size_t N, typename T, SqlFixedStringMode Mode>
383struct SqlBasicStringOperations<SqlFixedString<N, T, Mode>>
384{
385 using CharType = T;
386 using ValueType = SqlFixedString<N, CharType, Mode>;
387 static constexpr auto ColumnType = []() constexpr -> SqlColumnTypeDefinition {
388 if constexpr (std::same_as<CharType, char>)
389 {
390 if constexpr (Mode == SqlFixedStringMode::VARIABLE_SIZE)
391 return SqlColumnTypeDefinitions::Varchar { N };
392 else
393 return SqlColumnTypeDefinitions::Char { N };
394 }
395 else
396 {
397 if constexpr (Mode == SqlFixedStringMode::VARIABLE_SIZE)
398 return SqlColumnTypeDefinitions::NVarchar { N };
399 else
400 return SqlColumnTypeDefinitions::NChar { N };
401 }
402 }();
403
404 static CharType const* Data(ValueType const* str) noexcept
405 {
406 return str->data();
407 }
408
409 static CharType* Data(ValueType* str) noexcept
410 {
411 return str->data();
412 }
413
414 static SQLULEN Size(ValueType const* str) noexcept
415 {
416 return str->size();
417 }
418
419 static void Clear(ValueType* str) noexcept
420 {
421 str->clear();
422 }
423
424 static void Reserve(ValueType* str, size_t capacity) noexcept
425 {
426 str->reserve((std::min) (N, capacity));
427 str->resize((std::min) (N, capacity));
428 }
429
430 static void Resize(ValueType* str, SQLLEN indicator) noexcept
431 {
432 str->resize(static_cast<size_t>(indicator));
433 }
434
435 static void PostProcessOutputColumn(ValueType* result, SQLLEN indicator)
436 {
437 switch (indicator)
438 {
439 case SQL_NULL_DATA:
440 result->clear();
441 break;
442 case SQL_NO_TOTAL:
443 result->resize(N);
444 break;
445 default: {
446 auto const len = (std::min) (N, static_cast<std::size_t>(indicator) / sizeof(CharType));
447 result->setsize(len);
448
449 if constexpr (Mode == SqlFixedStringMode::FIXED_SIZE_RIGHT_TRIMMED)
450 {
451 TrimRight(result, indicator);
452 }
453 else
454 {
455 // Strip trailing null characters for all modes
456 StripTrailingNulls(result);
457 }
458 break;
459 }
460 }
461 }
462
463 LIGHTWEIGHT_FORCE_INLINE static void StripTrailingNulls(ValueType* str) noexcept
464 {
465 size_t n = str->size();
466 while (n > 0 && (*str)[n - 1] == '\0')
467 --n;
468 str->setsize(n);
469 }
470
471 LIGHTWEIGHT_FORCE_INLINE static void TrimRight(ValueType* boundOutputString, SQLLEN indicator) noexcept
472 {
473#if defined(_WIN32)
474 size_t n = (std::min) (static_cast<size_t>(indicator) / sizeof(CharType), N - 1);
475#else
476 size_t n = std::min(static_cast<size_t>(indicator), N - 1);
477#endif
478 // Strip trailing whitespace and null characters
479 while (n > 0 && (std::isspace((*boundOutputString)[n - 1]) || (*boundOutputString)[n - 1] == '\0'))
480 --n;
481 boundOutputString->setsize(n);
482 }
483};
484
485} // namespace Lightweight
486
487template <std::size_t N, typename T, Lightweight::SqlFixedStringMode P>
488struct std::formatter<Lightweight::SqlFixedString<N, T, P>>: std::formatter<std::string>
489{
490 using value_type = Lightweight::SqlFixedString<N, T, P>;
491 auto format(value_type const& text, format_context& ctx) const -> format_context::iterator
492 {
493 if constexpr (std::same_as<T, char16_t> || std::same_as<T, char32_t> || std::same_as<T, wchar_t>)
494 {
495 auto const utf8 = Lightweight::ToUtf8(text.ToStringView());
496 auto const stdstring = std::string(reinterpret_cast<char const*>(utf8.data()), utf8.size());
497 return std::formatter<std::string>::format(stdstring, ctx);
498 }
499 else
500 return std::formatter<std::string>::format(std::string(text.data(), text.size()), ctx);
501 }
502};
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const *s, std::size_t len) noexcept
Constructs a fixed-size string from a string pointer and length.
LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator end() const noexcept
Returns a const iterator to the end.
static constexpr SqlFixedStringMode PostRetrieveOperation
LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t capacity() const noexcept
Returns the maximum capacity of the string.
static constexpr std::size_t Capacity
The specified width of the string, which is also the maximum size of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(T const *s, T const *e) noexcept
Constructs a fixed-size string from a string pointer and end pointer.
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(SqlFixedString< OtherSize, T, OtherMode > const &other) const noexcept
Equality comparison operator.
T * pointer_type
Pointer type for the string.
LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type c_str() const noexcept
Returns a pointer to the null-terminated C string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > str() const noexcept
Returns a string view of the string.
T const * const_pointer_type
Const pointer type for the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void clear() noexcept
Clears the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void resize(std::size_t n) noexcept
LIGHTWEIGHT_FORCE_INLINE constexpr void push_back(T c) noexcept
Appends a character to the string.
LIGHTWEIGHT_FORCE_INLINE void reserve(std::size_t capacity)
Reserves capacity for the string. Throws if capacity exceeds the maximum.
T * iterator
Iterator type for the string.
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(SqlFixedString< OtherSize, T, OtherMode > const &other) const noexcept
Inequality comparison operator.
LIGHTWEIGHT_FORCE_INLINE constexpr bool empty() const noexcept
Tests if the string is empty.
LIGHTWEIGHT_FORCE_INLINE constexpr const_iterator begin() const noexcept
Returns a const iterator to the beginning.
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator==(std::basic_string_view< T > other) const noexcept
Equality comparison with a string view.
constexpr LIGHTWEIGHT_FORCE_INLINE SqlFixedString(T const (&text)[SourceSize])
Constructs a fixed-size string from a string literal.
LIGHTWEIGHT_FORCE_INLINE constexpr const_pointer_type data() const noexcept
Returns a const pointer to the underlying data.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > ToStringView() const noexcept
Returns a std::basic_string_view<T> from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string_view< T > substr(std::size_t offset=0, std::size_t count=(std::numeric_limits< std::size_t >::max)()) const noexcept
Returns a sub string of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr std::basic_string< T > ToString() const noexcept
Returns a std::basic_string<T> from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString() noexcept=default
Defaulted default constructor.
LIGHTWEIGHT_FORCE_INLINE constexpr bool operator!=(std::basic_string_view< T > other) const noexcept
Inequality comparison with a string view.
T value_type
The element type of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr T & operator[](std::size_t i) noexcept
Returns a mutable reference to the element at the given index.
LIGHTWEIGHT_FORCE_INLINE void setsize(std::size_t n) noexcept
Sets the size of the string, capped at the maximum capacity.
T const * const_iterator
Const iterator type for the string.
LIGHTWEIGHT_FORCE_INLINE constexpr SqlFixedString(std::basic_string< T > const &s) noexcept
Constructs a fixed-size string from a string.
LIGHTWEIGHT_FORCE_INLINE constexpr iterator begin() noexcept
Returns a mutable iterator to the beginning.
LIGHTWEIGHT_FORCE_INLINE std::weak_ordering operator<=>(SqlFixedString< OtherSize, T, OtherMode > const &other) const noexcept
Three-way comparison operator.
LIGHTWEIGHT_FORCE_INLINE constexpr std::size_t size() const noexcept
Returns the size of the string.
LIGHTWEIGHT_FORCE_INLINE constexpr void pop_back() noexcept
Removes the last character from the string.
LIGHTWEIGHT_FORCE_INLINE constexpr pointer_type data() noexcept
Returns a pointer to the underlying mutable data.
LIGHTWEIGHT_FORCE_INLINE constexpr T const & operator[](std::size_t i) const noexcept
Returns a const reference to the element at the given index.
LIGHTWEIGHT_FORCE_INLINE constexpr iterator end() noexcept
Returns a mutable iterator to the end.
LIGHTWEIGHT_API std::u8string ToUtf8(std::u32string_view u32InputString)