Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Characters/CString/Utilities.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cstdarg>
7DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wformat-truncation\""); // for g++-13 g++-release-sanitize_address_undefined x86_64-linux-gnu/bits/stdio2.h:68:36: warning: null format string [-Wformat-truncation=]
8#include <cstdio>
9DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wformat-truncation\"");
10#include <iomanip>
11#include <sstream>
12
13#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
14#include <clocale>
15#include <xlocale.h>
16#endif
17
18#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
20#endif
22#include "Stroika/Foundation/Math/Common.h"
24
25#include "Utilities.h"
26
27using namespace Stroika::Foundation;
29using namespace Stroika::Foundation::Characters::CString;
30
31/*
32 ********************************************************************************
33 ************************* Characters::CString::FormatV *************************
34 ********************************************************************************
35 */
36string Characters::CString::FormatV (const char* format, va_list argsList)
37{
38 RequireNotNull (format);
39 Memory::StackBuffer<char, 10 * 1024> msgBuf{Memory::eUninitialized, 10 * 1024};
40 // SUBTLE: va_list looks like it is passed by value, but its not really,
41 // and vswprintf, at least on GCC munges it. So we must use va_copy() to do this safely
42 // @see http://en.cppreference.com/w/cpp/utility/variadic/va_copy
43 va_list argListCopy;
44 va_copy (argListCopy, argsList);
45
46#if __STDC_WANT_SECURE_LIB__
47 while (::vsnprintf_s (msgBuf.data (), msgBuf.GetSize (), msgBuf.GetSize () - 1, format, argListCopy) < 0) {
48 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
49 va_end (argListCopy);
50 va_copy (argListCopy, argsList);
51 }
52#else
53 while (vsnprintf (msgBuf.data (), msgBuf.GetSize (), format, argListCopy) < 0) {
54 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
55 va_end (argListCopy);
56 va_copy (argListCopy, argsList);
57 }
58#endif
59 va_end (argListCopy);
60 Assert (::strlen (msgBuf.data ()) < msgBuf.GetSize ());
61 return string{msgBuf.data ()};
62}
63
64u8string Characters::CString::FormatV (const char8_t* format, va_list argsList)
65{
66 RequireNotNull (format);
67 Memory::StackBuffer<char8_t, 10 * 1024> msgBuf{Memory::eUninitialized, 10 * 1024};
68 // SUBTLE: va_list looks like it is passed by value, but its not really,
69 // and vswprintf, at least on GCC munges it. So we must use va_copy() to do this safely
70 // @see http://en.cppreference.com/w/cpp/utility/variadic/va_copy
71 va_list argListCopy;
72 va_copy (argListCopy, argsList);
73
74#if __STDC_WANT_SECURE_LIB__
75 while (::vsnprintf_s (reinterpret_cast<char*> (msgBuf.data ()), msgBuf.GetSize (), msgBuf.GetSize () - 1,
76 reinterpret_cast<const char*> (format), argListCopy) < 0) {
77 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
78 va_end (argListCopy);
79 va_copy (argListCopy, argsList);
80 }
81#else
82 while (vsnprintf (reinterpret_cast<char*> (msgBuf.data ()), msgBuf.GetSize (), reinterpret_cast<const char*> (format), argListCopy) < 0) {
83 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
84 va_end (argListCopy);
85 va_copy (argListCopy, argsList);
86 }
87#endif
88 va_end (argListCopy);
89 Assert (::strlen (reinterpret_cast<char*> (msgBuf.data ())) < msgBuf.GetSize ());
90 return u8string{msgBuf.data ()};
91}
92
94#if qCompilerAndStdLib_arm_asan_FaultStackUseAfterScope_Buggy
95Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_ADDRESS
96#endif
97 wstring
98 Characters::CString::FormatV (const wchar_t* format, va_list argsList)
99{
100 RequireNotNull (format);
101 Memory::StackBuffer<wchar_t, 10 * 1024> msgBuf{Memory::eUninitialized, 10 * 1024};
102 const wchar_t* useFormat = format;
103 wchar_t newFormat[5 * 1024];
104 {
105 size_t origFormatLen = ::wcslen (format);
106 Require (origFormatLen < Memory::NEltsOf (newFormat) / 2); // just to be sure safe - this is already crazy-big for format string...
107 // Could use Memory::StackBuffer<> but I doubt this will ever get triggered
108 bool lookingAtFmtCvt = false;
109 size_t newFormatIdx = 0;
110 for (size_t i = 0; i < origFormatLen; ++i) {
111 if (lookingAtFmtCvt) {
112 switch (format[i]) {
113 case '%': {
114 lookingAtFmtCvt = false;
115 } break;
116 case 's': {
117 newFormat[newFormatIdx] = 'l';
118 ++newFormatIdx;
119 } break;
120 case '.': {
121 // could still be part for format string
122 } break;
123 default: {
124 if (isdigit (format[i])) {
125 // could still be part for format string
126 }
127 else {
128 lookingAtFmtCvt = false; // DONE
129 }
130 } break;
131 }
132 }
133 else {
134 if (format[i] == '%') {
135 lookingAtFmtCvt = true;
136 }
137 }
138 newFormat[newFormatIdx] = format[i];
139 ++newFormatIdx;
140 }
141 Assert (newFormatIdx >= origFormatLen);
142 if (newFormatIdx > origFormatLen) {
143 newFormat[newFormatIdx] = '\0';
144 useFormat = newFormat;
145 }
146 }
147
148 // SUBTLE: va_list looks like it is passed by value, but its not really,
149 // and vswprintf, at least on GCC munges it. So we must use va_copy() to do this safely
150 // @see http://en.cppreference.com/w/cpp/utility/variadic/va_copy
151 va_list argListCopy;
152 va_copy (argListCopy, argsList);
153
154#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
155 locale_t tmpLocale{};
156 locale_t prevLocale{};
157 [[maybe_unused]] auto&& cleanup = Execution::Finally ([&tmpLocale, &prevLocale] () noexcept {
158 if (prevLocale != nullptr) {
159 ::uselocale (prevLocale);
160 }
161 if (tmpLocale != nullptr) {
162 ::freelocale (tmpLocale);
163 }
164 });
165#endif
166
167 // Assume only reason for failure is not enuf bytes, so allocate more.
168 // If I'm wrong, we'll just runout of memory and throw out...
169 while (::vswprintf (msgBuf.data (), msgBuf.GetSize (), useFormat, argListCopy) < 0) {
170#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
171 if (errno == EILSEQ and tmpLocale == nullptr) {
172 tmpLocale = ::newlocale (LC_CTYPE, "en_US.UTF-8", NULL);
173 prevLocale = ::uselocale (tmpLocale);
174 continue;
175 }
176#endif
177 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
178 va_end (argListCopy);
179 va_copy (argListCopy, argsList);
180 }
181 va_end (argListCopy);
182 Assert (::wcslen (msgBuf.data ()) < msgBuf.GetSize ());
183 return wstring{msgBuf.data ()};
184}
185DISABLE_COMPILER_MSC_WARNING_END (6262)
186
187/*
188 ********************************************************************************
189 ************************* Characters::CString::Format **************************
190 ********************************************************************************
191 */
192string Characters::CString::Format (const char* format, ...)
193{
194 va_list argsList;
195 va_start (argsList, format);
196 string tmp = FormatV (format, argsList);
197 va_end (argsList);
198 return tmp;
199}
200
201u8string Characters::CString::Format (const char8_t* format, ...)
202{
203 va_list argsList;
204 va_start (argsList, format);
205 u8string tmp = FormatV (format, argsList);
206 va_end (argsList);
207 return tmp;
208}
209
210wstring Characters::CString::Format (const wchar_t* format, ...)
211{
212 va_list argsList;
213 va_start (argsList, format);
214 wstring tmp = FormatV (format, argsList);
215 va_end (argsList);
216 return tmp;
217}
218
219/*
220 ********************************************************************************
221 ********************************* LimitLength **********************************
222 ********************************************************************************
223 */
224namespace {
225 template <typename STRING>
226 inline STRING LimitLength_HLPR_ (const STRING& str, size_t maxLen, bool keepLeft, const STRING& kELIPSIS)
227 {
228 if (str.length () <= maxLen) {
229 return str;
230 }
231 size_t useLen = maxLen;
232 if (useLen > kELIPSIS.length ()) {
233 useLen -= kELIPSIS.length ();
234 }
235 else {
236 useLen = 0;
237 }
238 if (keepLeft) {
239 return str.substr (0, useLen) + kELIPSIS;
240 }
241 else {
242 return kELIPSIS + str.substr (str.length () - useLen);
243 }
244 }
245}
246string Characters::CString::LimitLength (const string& str, size_t maxLen, bool keepLeft)
247{
248 return LimitLength_HLPR_<string> (str, maxLen, keepLeft, "...");
249}
250
251wstring Characters::CString::LimitLength (const wstring& str, size_t maxLen, bool keepLeft)
252{
253 return LimitLength_HLPR_<wstring> (str, maxLen, keepLeft, L"...");
254}
255
256/*
257 ********************************************************************************
258 *************************** StripTrailingCharIfAny *****************************
259 ********************************************************************************
260 */
261namespace {
262 template <typename STRING>
263 inline STRING StripTrailingCharIfAny_HLPR_ (const STRING& str, typename STRING::value_type c)
264 {
265 if (str.size () > 0 and str[str.size () - 1] == c) {
266 STRING tmp = str;
267 tmp.erase (tmp.size () - 1);
268 return tmp;
269 }
270 return str;
271 }
272}
273
274string Characters::CString::StripTrailingCharIfAny (const string& s, char c)
275{
276 return StripTrailingCharIfAny_HLPR_ (s, c);
277}
278
279wstring Characters::CString::StripTrailingCharIfAny (const wstring& s, wchar_t c)
280{
281 return StripTrailingCharIfAny_HLPR_ (s, c);
282}
283
284/*
285 ********************************************************************************
286 ****************************** HexString2Int ***********************************
287 ********************************************************************************
288 */
289unsigned int Characters::CString::HexString2Int (const string& s)
290{
291 unsigned long l = strtoul (s.c_str (), nullptr, 16);
292 if (l >= numeric_limits<unsigned int>::max ()) {
293 return numeric_limits<unsigned int>::max ();
294 }
295 return l;
296}
297
298unsigned int Characters::CString::HexString2Int (const wchar_t* s)
299{
300 RequireNotNull (s);
301 unsigned long l = wcstoul (s, nullptr, 16);
302 if (l >= numeric_limits<unsigned int>::max ()) {
303 return numeric_limits<unsigned int>::max ();
304 }
305 return l;
306}
307
308unsigned int Characters::CString::HexString2Int (const wstring& s)
309{
310 unsigned long l = wcstoul (s.c_str (), nullptr, 16);
311 if (l >= numeric_limits<unsigned int>::max ()) {
312 return numeric_limits<unsigned int>::max ();
313 }
314 return l;
315}
316
317/*
318 ********************************************************************************
319 ********************************* String2Int ***********************************
320 ********************************************************************************
321 */
322long long int Characters::CString::Private_::String2Int_ (const string& s)
323{
324 // nothing needed todo to pin the value to min/max
325 return strtoll (s.c_str (), nullptr, 10);
326}
327
328long long int Characters::CString::Private_::String2Int_ (const wstring& s)
329{
330 unsigned long long int l = wcstoll (s.c_str (), nullptr, 10);
331 return l;
332}
333
334/*
335 ********************************************************************************
336 ********************************* String2UInt ***********************************
337 ********************************************************************************
338 */
339unsigned long long int Characters::CString::Private_::String2UInt_ (const string& s)
340{
341 // nothing needed todo to pin the value to min/max
342 unsigned long long int l = strtoull (s.c_str (), nullptr, 10);
343 return l;
344}
345
346unsigned long long int Characters::CString::Private_::String2UInt_ (const wstring& s)
347{
348 long long int l = wcstoull (s.c_str (), nullptr, 10);
349 return l;
350}
#define RequireNotNull(p)
Definition Assertions.h:347
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31