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