source: cpp/common/util-string.cpp @ 1284

Last change on this file since 1284 was 1276, checked in by Maciej Komosinski, 15 months ago

Added ends_with() for std::string

  • Property svn:eol-style set to native
File size: 5.8 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "util-string.h"
6#include <stdarg.h>
7#include "nonstd_stdio.h"
8#include "nonstd.h"
9#include <assert.h>
10#include <cstdlib> //malloc()
11#include <algorithm>
12#ifdef USE_VIRTFILE
13#include <common/virtfile/virtfile.h>
14#endif
15#ifdef __ANDROID__
16#include <android/log.h> //only needed to print error messages related to a workaround for Android bug
17#endif
18
19string repr(const char *str)
20{
21        string out = "";
22        char hex[4];
23        while (*str)
24        {
25                unsigned char ch = *str;
26                if (ch >= 32 && ch < 128)
27                        out += ch;
28                else
29                {
30                        if (ch == 10) out += "\\n"; else
31                                if (ch == 13) out += "\\r"; else
32                                {
33                                        sprintf(hex, "%X", ch);
34                                        out += "\\x";
35                                        out += hex;
36                                }
37                }
38                str++;
39        }
40        return out;
41}
42
43
44string ssprintf_va(const char* format, va_list ap)
45{
46        string s; //clang crashed when this declaration was in s=buf
47        int size = 256;
48        char* buf;
49        va_list ap_copy; // "va_list ap" can only by used once by printf-type functions as they advance the current argument pointer (crashed on linux x86_64)
50        // (does not apply to SString::sprintf, it does not have the va_list variant)
51
52        //almost like SString::sprintf, but there is no common code to share because SString can use its directWrite to avoid double allocating/copying
53#ifdef USE_VSCPRINTF
54        va_copy(ap_copy, ap);
55        size = _vscprintf(format, ap_copy) + 1; //+1 for terminating null character
56        va_end(ap_copy);
57#endif
58
59        while (true)
60        {
61                buf = (char*)malloc(size);
62                assert(buf != NULL);
63                va_copy(ap_copy, ap);
64                int n = vsnprintf(buf, size, format, ap_copy);
65                va_end(ap_copy);
66
67#ifdef __ANDROID__
68                //Workaround for Android bug. /system/lib64/libc.so? maybe only arm 64-bit? "If an encoding error occurs, a negative number is returned". On some devices keeps returning -1 forever.
69                //https://github.com/android-ndk/ndk/issues/879 but unfortunately during google play tests (Firebase Test Lab) this problem turned out to be not limited to Chinese devices and occurred in Mate 9, Galaxy S9, Pixel, Pixel 2, Moto Z (even with the en_GB locale; the locale is not important but the problem seem to be utf8 non-ascii chars in the format string).
70                if (n < 0 && size >= (1 << 24)) //wants more than 16M
71                {
72                        buf[size - 1] = 0; //just to ensure there is at least some ending \0 in memory... who knows what buggy vsnprintf() did.
73                        __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "Giving up due to Android bug: vsnprintf() wants more than %d bytes, it used %zu bytes, for format='%s'", size, strlen(buf), format);
74                        //in my tests, it always used 0 bytes, so it produced a 0-length string: ""
75                        va_copy(ap_copy, ap);
76                        n = vsprintf(buf, format, ap_copy); //hoping 16M is enough
77                        va_end(ap_copy);
78                        __android_log_print(ANDROID_LOG_INFO, LOG_APP_NAME, "Fallback to vsprintf() produced string: '%s'", buf);
79                        if (n < 0) //vsprintf was also buggy. If we were strict, we should abort the app now.
80                        {
81                                strcpy(buf, "[STR_ERR] "); //a special prefix just to indicate the returned string is incorrect
82                                strcat(buf, format); //append and return the original formatting string
83                                __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "vsprintf() also failed, using the incorrect resulting string: '%s'", buf);
84                        }
85                        n = strlen(buf); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop
86                }
87#endif
88
89                if (n > -1 && n < size)
90                {
91                        s = buf;
92                        free(buf);
93                        return s;
94                }
95#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
96                if (n > -1)    /* glibc 2.1 */
97                        size = n + 1; /* precisely what is needed */
98                else           /* glibc 2.0 */
99#endif
100                        size *= 2;  /* twice the old size */
101                free(buf);
102        }
103}
104
105bool str_starts_with(const char *str, const char *prefix)
106{
107        return strncmp(str, prefix, strlen(prefix)) == 0;
108}
109
110bool ends_with(std::string_view str, std::string_view suffix)
111{
112        return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
113}
114
115
116char* strmove(char *a, char *b) //strcpy that works well for overlapping strings ("Source and destination overlap")
117{
118        if (a == NULL || b == NULL)
119                return NULL;
120        memmove(a, b, strlen(b) + 1);
121        return a;
122}
123
124string ssprintf(const char* format, ...)
125{
126        va_list ap;
127        va_start(ap, format);
128        string ret = ssprintf_va(format, ap); //is it too wasteful? copying the string again... unless the compiler can handle it better
129        va_end(ap);
130        return ret;
131}
132
133string stripExt(const string& filename)
134{
135        size_t dot = filename.rfind('.');
136        if (dot == string::npos) return filename;
137        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
138        if ((sep == string::npos) || (sep < dot))
139                return filename.substr(0, dot);
140        return filename;
141}
142
143string stripFileDir(const string& filename)
144{
145        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
146        if (sep == string::npos) return filename;
147        return filename.substr(sep + 1);
148}
149
150string getFileExt(const string& filename)
151{
152        size_t dot = filename.rfind('.');
153        if (dot == string::npos) return string("");
154        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
155        if ((sep == string::npos) || (sep < dot))
156                return filename.substr(dot);
157        return string("");
158}
159
160string getFileDir(const string& filename)
161{
162        size_t slash = filename.rfind(PATH_SEPARATOR_CHAR);
163        if (slash == string::npos) return string("");
164        return filename.substr(0, slash);
165}
166
167//trimming functions, https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
168
169void ltrim_inplace(string &s)
170{
171        s.erase(s.begin(), find_if(s.begin(), s.end(), [](int ch) {
172                return !isspace(ch);
173                }));
174}
175
176void rtrim_inplace(string &s)
177{
178        s.erase(find_if(s.rbegin(), s.rend(), [](int ch) {
179                return !isspace(ch);
180                }).base(), s.end());
181}
182
183void trim_inplace(string &s)
184{
185        ltrim_inplace(s);
186        rtrim_inplace(s);
187}
188
189string ltrim(string s)
190{
191        ltrim_inplace(s);
192        return s;
193}
194
195string rtrim(string s)
196{
197        rtrim_inplace(s);
198        return s;
199}
200
201string trim(string s)
202{
203        trim_inplace(s);
204        return s;
205}
Note: See TracBrowser for help on using the repository browser.