Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #include "std-includes.h"
4 : : #include "libmisc.h"
5 : : #include <cstdint>
6 : : //ty alcaro
7 : : extern const unsigned char char_props[256];
8 : 394404 : static inline int to_lower(unsigned char c) { return c|(char_props[c]&0x20); }
9 : 57498 : static inline int to_upper(unsigned char c) { return c&~(char_props[c]&0x20); }
10 : :
11 : 109549 : inline bool is_space(unsigned char c) { return char_props[c] & 0x80; } // C standard says \f \v are space, but this one disagrees
12 : 25747 : inline bool is_digit(unsigned char c) { return char_props[c] & 0x40; }
13 : : inline bool is_alpha(unsigned char c) { return char_props[c] & 0x20; }
14 : 1659 : inline bool is_lower(unsigned char c) { return char_props[c] & 0x04; }
15 : 2229 : inline bool is_upper(unsigned char c) { return char_props[c] & 0x02; }
16 : : inline bool is_alnum(unsigned char c) { return char_props[c] & 0x60; }
17 : 6731 : inline bool is_ualpha(unsigned char c) { return char_props[c] & 0x28; }
18 : 68570 : inline bool is_ualnum(unsigned char c) { return char_props[c] & 0x68; }
19 : 14465 : inline bool is_xdigit(unsigned char c) { return char_props[c] & 0x01; }
20 : :
21 : : inline char *copy(const char *source, int copy_length, char *dest)
22 : : {
23 : 442169 : memcpy(dest, source, copy_length*sizeof(char));
24 : : return dest;
25 : : }
26 : :
27 : : class string {
28 : : public:
29 : : const char *data() const
30 : : {
31 : 320220 : return cached_data;
32 : : }
33 : :
34 : : char *temp_raw() const //things to cleanup and take a look at
35 : : {
36 : 50931 : return cached_data;
37 : : }
38 : :
39 : : char *raw() const
40 : : {
41 : 695069 : return cached_data;
42 : : }
43 : :
44 : : int length() const
45 : : {
46 : 1198474 : return is_inlined() ? inlined.len : allocated.len;
47 : : }
48 : :
49 : 644263 : void set_length(int length)
50 : : {
51 [ + + ]: 644263 : if(length > max_inline_length_){
52 : 2338 : inlined.len = (unsigned char)-1;
53 : 2338 : allocated.len = length;
54 : : }else{
55 : 641925 : inlined.len = length;
56 : : }
57 : 644263 : }
58 : :
59 : : void truncate(int newlen)
60 : : {
61 : 483 : resize(newlen);
62 : 483 : }
63 : :
64 : 213427 : void assign(const char * newstr)
65 : : {
66 [ + + ]: 213427 : if (!newstr) newstr = "";
67 : 213427 : assign(newstr, strlen(newstr));
68 : 213427 : }
69 : :
70 : 117970 : void assign(const string &newstr)
71 : : {
72 : 117970 : assign(newstr, newstr.length());
73 : 117970 : }
74 : :
75 : 362306 : void assign(const char * newstr, int end)
76 : : {
77 : 362306 : resize(end);
78 : 362306 : copy(newstr, length(), cached_data);
79 : 362306 : }
80 : :
81 : :
82 : : string& operator=(const char * newstr)
83 : : {
84 : 97471 : assign(newstr);
85 : 2627 : return *this;
86 : : }
87 : :
88 : : string& operator=(const string &newstr)
89 : : {
90 : 13006 : assign(newstr);
91 : 7892 : return *this;
92 : : }
93 : :
94 : : string& append(const string& other, int start, int end)
95 : : {
96 : : int current_end = length();
97 : : resize(length() + end - start);
98 : : copy(other.cached_data + start, end - start, cached_data + current_end);
99 : : return *this;
100 : : }
101 : :
102 : 1587 : string& append(const char *other, int start, int end)
103 : : {
104 : : int current_end = length();
105 : 1587 : resize(length() + end - start);
106 : 1587 : copy(other + start, end - start, cached_data + current_end);
107 : 1587 : return *this;
108 : : }
109 : :
110 : 20348 : string& operator+=(const string& other)
111 : : {
112 : : int current_end = length();
113 : 20348 : resize(length() + other.length());
114 : 40696 : copy(other.cached_data, other.length(), cached_data + current_end);
115 : 20348 : return *this;
116 : : }
117 : :
118 : 56113 : string& operator+=(const char *other)
119 : : {
120 : : int current_end = length();
121 : 56113 : int otherlen=(int)strlen(other);
122 : 56113 : resize(length() + otherlen);
123 : 56113 : copy(other, otherlen, cached_data + current_end);
124 : 56113 : return *this;
125 : : }
126 : :
127 : 203421 : string& operator+=(char c)
128 : : {
129 : 203421 : resize(length() + 1);
130 : 406842 : cached_data[length() - 1] = c;
131 : 203421 : return *this;
132 : : }
133 : :
134 : : string operator+(char right) const
135 : : {
136 : : string ret=*this;
137 : : ret+=right;
138 : : return ret;
139 : : }
140 : :
141 : : string operator+(const char * right) const
142 : : {
143 : 4113 : string ret=*this;
144 : 4113 : ret+=right;
145 : 326 : return ret;
146 : : }
147 : :
148 : 4375 : bool operator==(const char * right) const
149 : : {
150 : 4375 : return !strcmp(data(), right);
151 : : }
152 : :
153 : 37 : bool operator==(const string& right) const
154 : : {
155 : 37 : return !strcmp(data(), right.data());
156 : : }
157 : :
158 : 1135 : bool operator!=(const char * right) const
159 : : {
160 : 1135 : return (strcmp(data(), right) != 0);
161 : : }
162 : :
163 : : bool operator!=(const string& right) const
164 : : {
165 : : return (strcmp(data(), right.data()) != 0);
166 : : }
167 : :
168 : : operator const char*() const
169 : : {
170 : : return data();
171 : : }
172 : :
173 : 6343 : explicit operator bool() const
174 : : {
175 : 6343 : return length();
176 : : }
177 : :
178 : 493798 : string()
179 : 493798 : {
180 : : //todo reduce I know this isn't all needed
181 : 493798 : allocated.bufferlen = 0;
182 : 493798 : allocated.str = 0;
183 : 493798 : allocated.len = 0;
184 : 493798 : inlined.len = 0;
185 : 493798 : cached_data = inlined.str;
186 : 493798 : next_resize = max_inline_length_+1;
187 : :
188 : 493798 : }
189 : 107620 : string(const char * newstr) : string()
190 : : {
191 : 107620 : assign(newstr);
192 : 107620 : }
193 : 25541 : string(const char * newstr, int newlen) : string()
194 : : {
195 : 25541 : assign(newstr, newlen);
196 : 25541 : }
197 : 8336 : string(const string& old) : string()
198 : : {
199 : 8336 : assign(old.data());
200 : 8336 : }
201 : :
202 : 755 : string(string &&move) : string()
203 : : {
204 : : *this = move;
205 : 755 : }
206 : :
207 : 10379 : string& operator=(string&& move)
208 : : {
209 : 0 : if(!is_inlined()) free(allocated.str);
210 : 10379 : if(!move.is_inlined()){
211 : 5 : allocated.str = move.allocated.str;
212 : 5 : allocated.bufferlen = move.allocated.bufferlen;
213 : 5 : set_length(move.allocated.len);
214 : :
215 : 5 : move.inlined.len = 0;
216 : 5 : move.inlined.str[0] = 0;
217 : 5 : cached_data = allocated.str;
218 : 5 : next_resize = move.next_resize;
219 : :
220 : : }else{
221 : 10374 : inlined.len = 0;
222 : 10374 : cached_data = inlined.str;
223 : 10374 : next_resize = max_inline_length_+1;
224 : 10374 : assign(move);
225 : : }
226 : 10379 : return *this;
227 : : }
228 : :
229 : 491629 : ~string()
230 : : {
231 : 491629 : if(!is_inlined()){
232 : 1797 : free(allocated.str);
233 : : }
234 : 491629 : }
235 : :
236 : : //maybe these should return refs to chain. but also good not to encourage chaining
237 : 1559 : void strip_prefix(char c)
238 : : {
239 [ + + ]: 1559 : if(cached_data[0] == c){
240 : 639 : *this = string(cached_data + 1, length() - 1);
241 : : }
242 : 1559 : }
243 : :
244 : 63 : void strip_suffix(char c)
245 : : {
246 [ + - ]: 63 : if(cached_data[length() - 1] == c){
247 : 63 : truncate(length() - 1);
248 : : }
249 : 63 : }
250 : :
251 : : string& qreplace(const char * instr, const char * outstr);
252 : : string& qnormalize();
253 : :
254 : : // RPG Hacker: My hack shmeck to get around no longer supporting text mode.
255 : : // Symbol files are currently the only thing that use text mode, anyways, and I don't even know
256 : : // if the emulators that read them care about line endings.
257 : : string& convert_line_endings_to_native()
258 : : {
259 : : #if defined(windows)
260 : : // RPG Hacker: This is quite stinky, but doing the replacement directly will lead to a dead-lock.
261 : : // \x08 = backspace should never appear inside a string, so I'm abusing it here.
262 : : return qreplace("\n", "\x08").qreplace("\x08", "\r\n");
263 : : #else
264 : : return *this;
265 : : #endif
266 : : }
267 : :
268 : : #ifdef SERIALIZER
269 : : void serialize(serializer & s)
270 : : {
271 : : s(str, allocated.bufferlen);
272 : : set_length(strlen(str));
273 : : }
274 : : #endif
275 : : #define SERIALIZER_BANNED
276 : :
277 : : private:
278 : : static const int scale_factor = 4; //scale sso
279 : : static const int max_inline_length_ = ((sizeof(char *) + sizeof(int) * 2) * scale_factor) - 2;
280 : : char *cached_data;
281 : : int next_resize;
282 : : struct si{
283 : : char str[max_inline_length_ + 1];
284 : : unsigned char len;
285 : : };
286 : :
287 : : struct sa{
288 : : char *str;
289 : : int len;
290 : : int bufferlen ;
291 : : };
292 : : union{
293 : : si inlined;
294 : : sa allocated;
295 : : };
296 : :
297 : :
298 : 644258 : void resize(int new_length)
299 : : {
300 : : const char *old_data = data();
301 [ + + + + ]: 644774 : if(new_length >= next_resize || (!is_inlined() && new_length <= max_inline_length_)) {
302 [ + + - + ]: 1855 : if(new_length > max_inline_length_ && (is_inlined() || allocated.bufferlen <= new_length)){ //SSO or big to big
303 : 1826 : int new_size = bitround(new_length + 1);
304 [ + + ]: 1826 : if(old_data == inlined.str){
305 : 3612 : allocated.str = copy(old_data, min(length(), new_length), (char *)malloc(new_size));
306 : : }else{
307 : 20 : allocated.str = (char *)realloc(allocated.str, new_size);
308 : : old_data = inlined.str; //this will prevent freeing a dead realloc ptr
309 : : }
310 : 1826 : allocated.bufferlen = new_size;
311 : 1826 : cached_data = allocated.str;
312 : 1826 : next_resize = allocated.bufferlen;
313 [ - + - + ]: 9 : }else if(length() > max_inline_length_ && new_length <= max_inline_length_){ //big to SSO
314 : 9 : copy(old_data, new_length, inlined.str);
315 : 9 : cached_data = inlined.str;
316 : 9 : next_resize = max_inline_length_+1;
317 : : }
318 [ + + + - ]: 1835 : if(old_data != inlined.str && old_data != data()){
319 : 9 : free((char *)old_data);
320 : : }
321 : : }
322 : 644258 : set_length(new_length);
323 : :
324 : 644258 : raw()[new_length] = 0; //always ensure null terminator
325 : 644258 : }
326 : :
327 : : bool is_inlined() const
328 : : {
329 [ + + + + : 2342934 : return inlined.len != (unsigned char)-1;
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ - - -
- ]
330 : : }
331 : : };
332 : : #define STR (string)
333 : :
334 : : char * readfile(const char * fname, const char * basepath);
335 : : char * readfilenative(const char * fname);
336 : : bool readfile(const char * fname, const char * basepath, char ** data, int * len);//if you want an uchar*, cast it
337 : : char ** split(char * str, char key, int * len= nullptr);
338 : : char ** qsplit(char * str, char key, int * len= nullptr);
339 : : char ** qpsplit(char * str, char key, int * len= nullptr);
340 : : char ** qsplitstr(char * str, const char * key, int * len= nullptr);
341 : : bool confirmquotes(const char * str);
342 : : bool confirmqpar(const char * str);
343 : : char* strqpchr(char* str, char key);
344 : : char* strqpstr(char* str, const char* key);
345 : :
346 : 3 : inline string hex(unsigned int value)
347 : : {
348 : : char buffer[64];
349 : : if(0);
350 [ - + ]: 3 : else if (value<=0x000000FF) sprintf(buffer, "%.2X", value);
351 [ + - ]: 3 : else if (value<=0x0000FFFF) sprintf(buffer, "%.4X", value);
352 [ # # ]: 0 : else if (value<=0x00FFFFFF) sprintf(buffer, "%.6X", value);
353 : 0 : else sprintf(buffer, "%.8X", value);
354 : 3 : return buffer;
355 : : }
356 : :
357 : 4 : inline string hex(unsigned int value, int width)
358 : : {
359 : : char buffer[64];
360 : 4 : sprintf(buffer, "%.*X", width, value);
361 : 4 : return buffer;
362 : : }
363 : :
364 : 995 : inline string dec(int value)
365 : : {
366 : : char buffer[64];
367 : 995 : sprintf(buffer, "%i", value);
368 : 995 : return buffer;
369 : : }
370 : :
371 : 1083 : inline string ftostr(double value)
372 : : {
373 : : // randomdude999: With 100 digits of precision, the buffer needs to be approx. 311+100,
374 : : // but let's be safe here https://stackoverflow.com/questions/7235456
375 : : char rval[512];
376 : : // RPG Hacker: Ridiculously high precision, I know, but we're working with doubles
377 : : // here and can afford it, so no need to waste any precision
378 : 1083 : sprintf(rval, "%.100f", value);
379 [ + - ]: 1083 : if (strchr(rval, '.'))//nuke useless zeroes
380 : : {
381 : 1083 : char * end=strrchr(rval, '\0')-1;
382 [ + + ]: 109209 : while (*end=='0')
383 : : {
384 : 108126 : *end='\0';
385 : 108126 : end--;
386 : : }
387 [ + + ]: 1083 : if (*end=='.') *end='\0';
388 : : }
389 : 1083 : return rval;
390 : : }
391 : :
392 : : // Same as above, but with variable precision
393 : 23 : inline string ftostrvar(double value, int precision)
394 : : {
395 : : int clampedprecision = precision;
396 : : if (clampedprecision < 0) clampedprecision = 0;
397 : 23 : if (clampedprecision > 100) clampedprecision = 100;
398 : :
399 : : // see above
400 : : char rval[512];
401 : 23 : sprintf(rval, "%.*f", clampedprecision, (double)value);
402 [ + + ]: 23 : if (strchr(rval, '.'))//nuke useless zeroes
403 : : {
404 : 22 : char * end = strrchr(rval, '\0') - 1;
405 [ + + ]: 91 : while (*end == '0')
406 : : {
407 : 69 : *end = '\0';
408 : 69 : end--;
409 : : }
410 [ + + ]: 22 : if (*end == '.') *end = '\0';
411 : : }
412 : 23 : return rval;
413 : : }
414 : :
415 : 46779 : inline bool stribegin(const char * str, const char * key)
416 : : {
417 [ + + ]: 62131 : for (int i=0;key[i];i++)
418 : : {
419 [ + + ]: 58385 : if (to_lower(str[i])!=to_lower(key[i])) return false;
420 : : }
421 : : return true;
422 : : }
423 : :
424 : 3879 : inline bool striend(const char * str, const char * key)
425 : : {
426 : : const char * keyend=strrchr(key, '\0');
427 : : const char * strend=strrchr(str, '\0');
428 [ + + ]: 3879 : if(keyend-key > strend-str) return false;
429 : :
430 [ + + ]: 4926 : while (key!=keyend)
431 : : {
432 : 4443 : keyend--;
433 : 4443 : strend--;
434 [ + + ]: 4443 : if (to_lower(*strend)!=to_lower(*keyend)) return false;
435 : : }
436 : : return true;
437 : : }
438 : :
439 : 50902 : inline bool stricmpwithupper(const char *word1, const char *word2)
440 : : {
441 [ + + ]: 59389 : while(*word2)
442 : : {
443 [ + + ]: 57498 : if(to_upper(*word1++) != *word2++) return true;
444 : : }
445 : 1891 : return *word1;
446 : : }
447 : :
448 : 174483 : inline bool stricmpwithlower(const char *word1, const char *word2)
449 : : {
450 [ + + ]: 273969 : while(*word2)
451 : : {
452 [ + + ]: 258536 : if(to_lower(*word1++) != *word2++) return true;
453 : : }
454 : 15433 : return *word1;
455 : : }
456 : :
457 : : //function: return the string without quotes around it, if any exists
458 : : //if they don't exist, return it unaltered
459 : : //it is not guaranteed to return str
460 : : //it is not guaranteed to not edit str
461 : : //the input must be freed even though it's garbage, the output must not
462 : 1445 : inline const char * dequote(char * str)
463 : : {
464 [ + + ]: 1445 : if (*str!='"') return str;
465 : : char *end = strrchr(str, '"');
466 [ + - ]: 1073 : if (end)
467 : : {
468 : 1073 : *end = 0;
469 : 1073 : char *quote = str+1;
470 [ + + ]: 1145 : while((quote = strstr(quote, "\"\""))) memmove(quote, quote+1, strlen(quote));
471 : : return str + 1;
472 : : }
473 : : return nullptr;
474 : : }
475 : :
476 : 5695 : inline char * strqchr(const char * str, char key)
477 : : {
478 [ + + ]: 58527 : while (*str != '\0')
479 : : {
480 [ + + ]: 54551 : if (*str == key) { return const_cast<char*>(str); }
481 [ + + + + ]: 52833 : else if (*str == '"' || *str == '\'')
482 : : {
483 : : // Special case hack for ''', which is currently our official way of handling the ' character.
484 : : // Even though it really stinks.
485 [ + + + + : 388 : if (str[0] == '\'' && str[1] == '\'' && str[2] == '\'') { str += 2; }
+ - ]
486 : : else
487 : : {
488 : : char delimiter = *str;
489 : :
490 : : do
491 : : {
492 : 4537 : str++;
493 : :
494 : : // If we want to support backslash escapes, we'll have to add that right here.
495 [ + + + + ]: 4537 : } while (*str != delimiter && *str != '\0');
496 : :
497 : : // This feels like a superfluous check, but I can't really find a clean way to avoid it.
498 [ + + ]: 383 : if (*str == '\0') { return nullptr; }
499 : : }
500 : : }
501 : :
502 : 52832 : str++;
503 : : }
504 : :
505 : : return nullptr;
506 : : }
507 : :
508 : : inline string substr(const char * str, int len)
509 : : {
510 : 1276 : return string(str, len);
511 : : }
512 : :
513 : : //todo make these members
514 : : string &strip_prefix(string &str, char c);
515 : : string &strip_suffix(string &str, char c);
516 : :
517 : :
518 : 29912 : inline char *strip_whitespace(char *str)
519 : : {
520 [ + + ]: 31689 : while(is_space(*str)) str++;
521 [ + + ]: 37955 : for(int i = strlen(str) - 1; i >= 0; i--)
522 : : {
523 [ + + ]: 28309 : if(!is_space(str[i]))
524 : : {
525 : 20266 : str[i + 1] = 0;
526 : 20266 : return str;
527 : : }
528 : : }
529 : : return str;
530 : : }
531 : 1621 : inline void strip_whitespace(string &str)
532 : : {
533 : 1621 : str = string(strip_whitespace(str.temp_raw()));
534 : 1621 : }
535 : :
536 : : string &itrim(string &str, const char * left, const char * right);
537 : :
538 : 0 : inline string &lower(string &old)
539 : : {
540 : : int length = old.length();
541 [ # # ]: 0 : for (int i=0;i<length;i++) old.raw()[i]=(char)to_lower(old.data()[i]);
542 : 0 : return old;
543 : : }
544 : :
545 : :
546 : : // Returns number of connected lines - 1
547 : : template<typename stringarraytype>
548 : 19767 : inline int getconnectedlines(stringarraytype& lines, int startline, string& out)
549 : : {
550 : : int count = 0;
551 : :
552 [ + - ]: 19776 : for (int i = startline; lines[i]; i++)
553 : : {
554 : : // The line should already be stripped of any comments at this point
555 : 19776 : int linestartpos = (int)strlen(lines[i]);
556 : :
557 [ + + + + ]: 19776 : if(linestartpos && lines[i][linestartpos - 1] == '\\')
558 : : {
559 : 9 : count++;
560 : 9 : out += string(lines[i], linestartpos - 1);
561 : : continue;
562 : : }
563 : : else
564 : : {
565 : 19767 : out += string(lines[i], linestartpos);
566 : 19767 : return count;
567 : : }
568 : : }
569 : :
570 : : return count;
571 : : }
|