Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Characters/CString/Utilities.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 Characters::CString::FormatV (const wchar_t* format, va_list argsList)
98{
99 RequireNotNull (format);
100 Memory::StackBuffer<wchar_t, 10 * 1024> msgBuf{Memory::eUninitialized, 10 * 1024};
101 const wchar_t* useFormat = format;
102 wchar_t newFormat[5 * 1024];
103 {
104 size_t origFormatLen = ::wcslen (format);
105 Require (origFormatLen < std::size (newFormat) / 2); // just to be sure safe - this is already crazy-big for format string...
106 // Could use Memory::StackBuffer<> but I doubt this will ever get triggered
107 bool lookingAtFmtCvt = false;
108 size_t newFormatIdx = 0;
109 for (size_t i = 0; i < origFormatLen; ++i) {
110 if (lookingAtFmtCvt) {
111 switch (format[i]) {
112 case '%': {
113 lookingAtFmtCvt = false;
114 } break;
115 case 's': {
116 newFormat[newFormatIdx] = 'l';
117 ++newFormatIdx;
118 } break;
119 case '.': {
120 // could still be part for format string
121 } break;
122 default: {
123 if (isdigit (format[i])) {
124 // could still be part for format string
125 }
126 else {
127 lookingAtFmtCvt = false; // DONE
128 }
129 } break;
130 }
131 }
132 else {
133 if (format[i] == '%') {
134 lookingAtFmtCvt = true;
135 }
136 }
137 newFormat[newFormatIdx] = format[i];
138 ++newFormatIdx;
139 }
140 Assert (newFormatIdx >= origFormatLen);
141 if (newFormatIdx > origFormatLen) {
142 newFormat[newFormatIdx] = '\0';
143 useFormat = newFormat;
144 }
145 }
146
147 // SUBTLE: va_list looks like it is passed by value, but its not really,
148 // and vswprintf, at least on GCC munges it. So we must use va_copy() to do this safely
149 // @see http://en.cppreference.com/w/cpp/utility/variadic/va_copy
150 va_list argListCopy;
151 va_copy (argListCopy, argsList);
152
153#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
154 locale_t tmpLocale{};
155 locale_t prevLocale{};
156 [[maybe_unused]] auto&& cleanup = Execution::Finally ([&tmpLocale, &prevLocale] () noexcept {
157 if (prevLocale != nullptr) {
158 ::uselocale (prevLocale);
159 }
160 if (tmpLocale != nullptr) {
161 ::freelocale (tmpLocale);
162 }
163 });
164#endif
165
166 // Assume only reason for failure is not enuf bytes, so allocate more.
167 // If I'm wrong, we'll just runout of memory and throw out...
168 while (::vswprintf (msgBuf.data (), msgBuf.GetSize (), useFormat, argListCopy) < 0) {
169#if qCompilerAndStdLib_vswprintf_errantDependencyOnLocale_Buggy
170 if (errno == EILSEQ and tmpLocale == nullptr) {
171 tmpLocale = ::newlocale (LC_CTYPE, "en_US.UTF-8", NULL);
172 prevLocale = ::uselocale (tmpLocale);
173 continue;
174 }
175#endif
176 msgBuf.GrowToSize_uninitialized (msgBuf.GetSize () * 2);
177 va_end (argListCopy);
178 va_copy (argListCopy, argsList);
179 }
180 va_end (argListCopy);
181 Assert (::wcslen (msgBuf.data ()) < msgBuf.GetSize ());
182 return wstring{msgBuf.data ()};
183}
184DISABLE_COMPILER_MSC_WARNING_END (6262)
185
186/*
187 ********************************************************************************
188 ************************* Characters::CString::Format **************************
189 ********************************************************************************
190 */
191string Characters::CString::Format (const char* format, ...)
192{
193 va_list argsList;
194 va_start (argsList, format);
195 string tmp = FormatV (format, argsList);
196 va_end (argsList);
197 return tmp;
198}
199
200u8string Characters::CString::Format (const char8_t* format, ...)
201{
202 va_list argsList;
203 va_start (argsList, format);
204 u8string tmp = FormatV (format, argsList);
205 va_end (argsList);
206 return tmp;
207}
208
209wstring Characters::CString::Format (const wchar_t* format, ...)
210{
211 va_list argsList;
212 va_start (argsList, format);
213 wstring tmp = FormatV (format, argsList);
214 va_end (argsList);
215 return tmp;
216}
217
218/*
219 ********************************************************************************
220 ********************************* LimitLength **********************************
221 ********************************************************************************
222 */
223namespace {
224 template <typename STRING>
225 inline STRING LimitLength_HLPR_ (const STRING& str, size_t maxLen, bool keepLeft, const STRING& kELIPSIS)
226 {
227 if (str.length () <= maxLen) {
228 return str;
229 }
230 size_t useLen = maxLen;
231 if (useLen > kELIPSIS.length ()) {
232 useLen -= kELIPSIS.length ();
233 }
234 else {
235 useLen = 0;
236 }
237 if (keepLeft) {
238 return str.substr (0, useLen) + kELIPSIS;
239 }
240 else {
241 return kELIPSIS + str.substr (str.length () - useLen);
242 }
243 }
244}
245string Characters::CString::LimitLength (const string& str, size_t maxLen, bool keepLeft)
246{
247 return LimitLength_HLPR_<string> (str, maxLen, keepLeft, "...");
248}
249
250wstring Characters::CString::LimitLength (const wstring& str, size_t maxLen, bool keepLeft)
251{
252 return LimitLength_HLPR_<wstring> (str, maxLen, keepLeft, L"...");
253}
254
255/*
256 ********************************************************************************
257 *************************** StripTrailingCharIfAny *****************************
258 ********************************************************************************
259 */
260namespace {
261 template <typename STRING>
262 inline STRING StripTrailingCharIfAny_HLPR_ (const STRING& str, typename STRING::value_type c)
263 {
264 if (str.size () > 0 and str[str.size () - 1] == c) {
265 STRING tmp = str;
266 tmp.erase (tmp.size () - 1);
267 return tmp;
268 }
269 return str;
270 }
271}
272
273string Characters::CString::StripTrailingCharIfAny (const string& s, char c)
274{
275 return StripTrailingCharIfAny_HLPR_ (s, c);
276}
277
278wstring Characters::CString::StripTrailingCharIfAny (const wstring& s, wchar_t c)
279{
280 return StripTrailingCharIfAny_HLPR_ (s, c);
281}
282
283/*
284 ********************************************************************************
285 ****************************** HexString2Int ***********************************
286 ********************************************************************************
287 */
288unsigned int Characters::CString::HexString2Int (const string& s)
289{
290 unsigned long l = strtoul (s.c_str (), nullptr, 16);
291 if (l >= numeric_limits<unsigned int>::max ()) {
292 return numeric_limits<unsigned int>::max ();
293 }
294 return l;
295}
296
297unsigned int Characters::CString::HexString2Int (const wchar_t* s)
298{
299 RequireNotNull (s);
300 unsigned long l = wcstoul (s, nullptr, 16);
301 if (l >= numeric_limits<unsigned int>::max ()) {
302 return numeric_limits<unsigned int>::max ();
303 }
304 return l;
305}
306
307unsigned int Characters::CString::HexString2Int (const wstring& s)
308{
309 unsigned long l = wcstoul (s.c_str (), nullptr, 16);
310 if (l >= numeric_limits<unsigned int>::max ()) {
311 return numeric_limits<unsigned int>::max ();
312 }
313 return l;
314}
315
316/*
317 ********************************************************************************
318 ********************************* String2Int ***********************************
319 ********************************************************************************
320 */
321long long int Characters::CString::Private_::String2Int_ (const string& s)
322{
323 // nothing needed todo to pin the value to min/max
324 return strtoll (s.c_str (), nullptr, 10);
325}
326
327long long int Characters::CString::Private_::String2Int_ (const wstring& s)
328{
329 unsigned long long int l = wcstoll (s.c_str (), nullptr, 10);
330 return l;
331}
332
333/*
334 ********************************************************************************
335 ********************************* String2UInt ***********************************
336 ********************************************************************************
337 */
338unsigned long long int Characters::CString::Private_::String2UInt_ (const string& s)
339{
340 // nothing needed todo to pin the value to min/max
341 unsigned long long int l = strtoull (s.c_str (), nullptr, 10);
342 return l;
343}
344
345unsigned long long int Characters::CString::Private_::String2UInt_ (const wstring& s)
346{
347 long long int l = wcstoull (s.c_str (), nullptr, 10);
348 return l;
349}
#define RequireNotNull(p)
Definition Assertions.h:348
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...