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
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[1276]2// Copyright (C) 1999-2023  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[196]4
[860]5#include "util-string.h"
[180]6#include <stdarg.h>
7#include "nonstd_stdio.h"
8#include "nonstd.h"
[220]9#include <assert.h>
[862]10#include <cstdlib> //malloc()
[1130]11#include <algorithm>
[247]12#ifdef USE_VIRTFILE
[382]13#include <common/virtfile/virtfile.h>
[247]14#endif
[892]15#ifdef __ANDROID__
16#include <android/log.h> //only needed to print error messages related to a workaround for Android bug
17#endif
[180]18
[1025]19string repr(const char *str)
20{
21        string out = "";
22        char hex[4];
23        while (*str)
24        {
[1036]25                unsigned char ch = *str;
26                if (ch >= 32 && ch < 128)
27                        out += ch;
[1025]28                else
29                {
[1036]30                        if (ch == 10) out += "\\n"; else
31                                if (ch == 13) out += "\\r"; else
[1025]32                                {
[1036]33                                        sprintf(hex, "%X", ch);
[1025]34                                        out += "\\x";
35                                        out += hex;
36                                }
37                }
38                str++;
39        }
40        return out;
41}
42
43
[180]44string ssprintf_va(const char* format, va_list ap)
45{
[220]46        string s; //clang crashed when this declaration was in s=buf
[319]47        int size = 256;
[180]48        char* buf;
[247]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)
[220]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
[257]54        va_copy(ap_copy, ap);
[247]55        size = _vscprintf(format, ap_copy) + 1; //+1 for terminating null character
56        va_end(ap_copy);
[220]57#endif
58
[1025]59        while (true)
[246]60        {
61                buf = (char*)malloc(size);
62                assert(buf != NULL);
[257]63                va_copy(ap_copy, ap);
[247]64                int n = vsnprintf(buf, size, format, ap_copy);
65                va_end(ap_copy);
[892]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                        {
[897]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);
[892]84                        }
[897]85                        n = strlen(buf); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop
[892]86                }
87#endif
88
[246]89                if (n > -1 && n < size)
[220]90                {
[246]91                        s = buf;
[220]92                        free(buf);
93                        return s;
[246]94                }
[220]95#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
96                if (n > -1)    /* glibc 2.1 */
[867]97                        size = n + 1; /* precisely what is needed */
[220]98                else           /* glibc 2.0 */
99#endif
100                        size *= 2;  /* twice the old size */
101                free(buf);
[246]102        }
[180]103}
104
[913]105bool str_starts_with(const char *str, const char *prefix)
106{
[1016]107        return strncmp(str, prefix, strlen(prefix)) == 0;
[913]108}
109
[1276]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
[257]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
[180]124string ssprintf(const char* format, ...)
125{
126        va_list ap;
127        va_start(ap, format);
[246]128        string ret = ssprintf_va(format, ap); //is it too wasteful? copying the string again... unless the compiler can handle it better
[180]129        va_end(ap);
130        return ret;
131}
132
[246]133string stripExt(const string& filename)
[180]134{
[319]135        size_t dot = filename.rfind('.');
[246]136        if (dot == string::npos) return filename;
[319]137        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
[246]138        if ((sep == string::npos) || (sep < dot))
139                return filename.substr(0, dot);
[180]140        return filename;
141}
142
[460]143string stripFileDir(const string& filename)
144{
145        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
146        if (sep == string::npos) return filename;
[867]147        return filename.substr(sep + 1);
[460]148}
149
[246]150string getFileExt(const string& filename)
[180]151{
[319]152        size_t dot = filename.rfind('.');
[246]153        if (dot == string::npos) return string("");
[319]154        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
[246]155        if ((sep == string::npos) || (sep < dot))
[180]156                return filename.substr(dot);
157        return string("");
158}
[246]159
160string getFileDir(const string& filename)
161{
[319]162        size_t slash = filename.rfind(PATH_SEPARATOR_CHAR);
[246]163        if (slash == string::npos) return string("");
164        return filename.substr(0, slash);
165}
[1016]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.