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 : 1086094 : static inline int to_lower(unsigned char c) { return c|(char_props[c]&0x20); }
9 : 114996 : static inline int to_upper(unsigned char c) { return c&~(char_props[c]&0x20); }
10 : :
11 : 230153 : inline bool is_space(unsigned char c) { return char_props[c] & 0x80; } // C standard says \f \v are space, but this one disagrees
12 : 51494 : 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 : 3318 : inline bool is_lower(unsigned char c) { return char_props[c] & 0x04; }
15 : 4458 : 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 : 13462 : inline bool is_ualpha(unsigned char c) { return char_props[c] & 0x28; }
18 : 137140 : inline bool is_ualnum(unsigned char c) { return char_props[c] & 0x68; }
19 : 28930 : inline bool is_xdigit(unsigned char c) { return char_props[c] & 0x01; }
20 : :
21 : 596030 : inline char *copy(const char *source, int copy_length, char *dest)
22 : : {
23 : 1038199 : memcpy(dest, source, copy_length*sizeof(char));
24 : 596030 : return dest;
25 : : }
26 : :
27 : : class string {
28 : : public:
29 : 2276859 : const char *data() const
30 : : {
31 : 2597079 : return cached_data;
32 : : }
33 : :
34 : 62179 : char *temp_raw() const //things to cleanup and take a look at
35 : : {
36 : 113110 : return cached_data;
37 : : }
38 : :
39 : 1206510 : char *raw() const
40 : : {
41 : 1901579 : return cached_data;
42 : : }
43 : :
44 : 2146227 : int length() const
45 : : {
46 [ + + ]: 3344701 : return is_inlined() ? inlined.len : allocated.len;
47 : : }
48 : :
49 : 1593847 : void set_length(int length)
50 : : {
51 [ + + ]: 1593847 : if(length > max_inline_length_){
52 : 4724 : inlined.len = (unsigned char)-1;
53 : 4724 : allocated.len = length;
54 : : }else{
55 : 1589123 : inlined.len = length;
56 : : }
57 : 1593847 : }
58 : :
59 : 483 : void truncate(int newlen)
60 : : {
61 : 966 : resize(newlen);
62 : 966 : }
63 : :
64 : 490498 : void assign(const char * newstr)
65 : : {
66 [ + + ]: 490498 : if (!newstr) newstr = "";
67 : 490498 : assign(newstr, strlen(newstr));
68 : 490498 : }
69 : :
70 : 281321 : void assign(const string &newstr)
71 : : {
72 : 281321 : assign(newstr, newstr.length());
73 : 281321 : }
74 : :
75 : 838059 : void assign(const char * newstr, int end)
76 : : {
77 : 838059 : resize(end);
78 : 838059 : copy(newstr, length(), cached_data);
79 : 838059 : }
80 : :
81 : :
82 : 141216 : string& operator=(const char * newstr)
83 : : {
84 : 238687 : assign(newstr);
85 : 143843 : return *this;
86 : : }
87 : :
88 : 134237 : string& operator=(const string &newstr)
89 : : {
90 : 147243 : assign(newstr);
91 : 142129 : 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 : 3174 : string& append(const char *other, int start, int end)
103 : : {
104 : 1587 : int current_end = length();
105 : 3174 : resize(length() + end - start);
106 : 3174 : copy(other + start, end - start, cached_data + current_end);
107 : 3174 : return *this;
108 : : }
109 : :
110 : 72236 : string& operator+=(const string& other)
111 : : {
112 : 51888 : int current_end = length();
113 : 72236 : resize(length() + other.length());
114 : 92584 : copy(other.cached_data, other.length(), cached_data + current_end);
115 : 72236 : return *this;
116 : : }
117 : :
118 : 121070 : string& operator+=(const char *other)
119 : : {
120 : 64957 : int current_end = length();
121 : 121070 : int otherlen=(int)strlen(other);
122 : 121070 : resize(length() + otherlen);
123 : 121070 : copy(other, otherlen, cached_data + current_end);
124 : 121070 : return *this;
125 : : }
126 : :
127 : 558326 : string& operator+=(char c)
128 : : {
129 : 558326 : resize(length() + 1);
130 : 761747 : cached_data[length() - 1] = c;
131 : 558326 : return *this;
132 : : }
133 : :
134 : : string operator+(char right) const
135 : : {
136 : : string ret=*this;
137 : : ret+=right;
138 : : return ret;
139 : : }
140 : :
141 : 4113 : string operator+(const char * right) const
142 : : {
143 : 8226 : string ret=*this;
144 : 8226 : ret+=right;
145 : 4439 : return ret;
146 : 0 : }
147 : :
148 : 8750 : bool operator==(const char * right) const
149 : : {
150 : 8750 : return !strcmp(data(), right);
151 : : }
152 : :
153 : 74 : bool operator==(const string& right) const
154 : : {
155 : 74 : return !strcmp(data(), right.data());
156 : : }
157 : :
158 : 4481 : bool operator!=(const char * right) const
159 : : {
160 : 4481 : 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 : 1110715 : operator const char*() const
169 : : {
170 : 1110715 : return data();
171 : : }
172 : :
173 : 12686 : explicit operator bool() const
174 : : {
175 : 12686 : return length();
176 : : }
177 : :
178 : 1101055 : string()
179 : 1101055 : {
180 : : //todo reduce I know this isn't all needed
181 : 1101055 : allocated.bufferlen = 0;
182 : 1101055 : allocated.str = 0;
183 : 1101055 : allocated.len = 0;
184 : 1101055 : inlined.len = 0;
185 : 1101055 : cached_data = inlined.str;
186 : 1101055 : next_resize = max_inline_length_+1;
187 : :
188 : 1101055 : }
189 : 235139 : string(const char * newstr) : string()
190 : : {
191 : 235139 : assign(newstr);
192 : 235139 : }
193 : 55504 : string(const char * newstr, int newlen) : string()
194 : : {
195 : 55504 : assign(newstr, newlen);
196 : 55504 : }
197 : 16672 : string(const string& old) : string()
198 : : {
199 : 16672 : assign(old.data());
200 : 16672 : }
201 : :
202 : 3721 : string(string &&move) : string()
203 : : {
204 : 2966 : *this = move;
205 : 3721 : }
206 : :
207 : 39504 : string& operator=(string&& move)
208 : : {
209 [ - + ]: 29125 : if(!is_inlined()) free(allocated.str);
210 [ + + ]: 39504 : if(!move.is_inlined()){
211 : 16 : allocated.str = move.allocated.str;
212 : 16 : allocated.bufferlen = move.allocated.bufferlen;
213 : 16 : set_length(move.allocated.len);
214 : :
215 : 16 : move.inlined.len = 0;
216 : 16 : move.inlined.str[0] = 0;
217 : 16 : cached_data = allocated.str;
218 : 16 : next_resize = move.next_resize;
219 : :
220 : : }else{
221 : 39488 : inlined.len = 0;
222 : 39488 : cached_data = inlined.str;
223 : 39488 : next_resize = max_inline_length_+1;
224 : 39488 : assign(move);
225 : : }
226 : 39504 : return *this;
227 : : }
228 : :
229 : 1074638 : ~string()
230 : : {
231 [ + + ]: 1074638 : if(!is_inlined()){
232 : 3624 : free(allocated.str);
233 : : }
234 : 1074638 : }
235 : :
236 : : //maybe these should return refs to chain. but also good not to encourage chaining
237 : 3118 : void strip_prefix(char c)
238 : : {
239 [ + + ]: 3118 : if(cached_data[0] == c){
240 : 1278 : *this = string(cached_data + 1, length() - 1);
241 : : }
242 : 3118 : }
243 : :
244 : 126 : void strip_suffix(char c)
245 : : {
246 [ + - ]: 126 : if(cached_data[length() - 1] == c){
247 : 126 : truncate(length() - 1);
248 : : }
249 : 126 : }
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 : 0 : 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 : 0 : 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 : 1593831 : void resize(int new_length)
299 : : {
300 : 949573 : const char *old_data = data();
301 [ + + + + : 1594347 : if(new_length >= next_resize || (!is_inlined() && new_length <= max_inline_length_)) {
+ + + + ]
302 [ + + + + : 3720 : if(new_length > max_inline_length_ && (is_inlined() || allocated.bufferlen <= new_length)){ //SSO or big to big
+ - + + ]
303 : 3682 : int new_size = bitround(new_length + 1);
304 [ + + ]: 3682 : if(old_data == inlined.str){
305 : 5448 : allocated.str = copy(old_data, min(length(), new_length), (char *)malloc(new_size));
306 : : }else{
307 : 40 : allocated.str = (char *)realloc(allocated.str, new_size);
308 : 20 : old_data = inlined.str; //this will prevent freeing a dead realloc ptr
309 : : }
310 : 3682 : allocated.bufferlen = new_size;
311 : 3682 : cached_data = allocated.str;
312 : 3682 : next_resize = allocated.bufferlen;
313 [ + + + + : 18 : }else if(length() > max_inline_length_ && new_length <= max_inline_length_){ //big to SSO
+ - ]
314 : 18 : copy(old_data, new_length, inlined.str);
315 : 18 : cached_data = inlined.str;
316 : 18 : next_resize = max_inline_length_+1;
317 : : }
318 [ + + + - : 3700 : if(old_data != inlined.str && old_data != data()){
+ + ]
319 : 18 : free((char *)old_data);
320 : : }
321 : : }
322 : 1593831 : set_length(new_length);
323 : :
324 : 1593831 : raw()[new_length] = 0; //always ensure null terminator
325 : 1593831 : }
326 : :
327 : 3737059 : bool is_inlined() const
328 : : {
329 [ + + + + : 6079993 : 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 : 6 : inline string hex(unsigned int value)
347 : : {
348 : : char buffer[64];
349 : : if(0);
350 [ - + ]: 6 : else if (value<=0x000000FF) sprintf(buffer, "%.2X", value);
351 [ + - ]: 6 : 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 : 9 : return buffer;
355 : : }
356 : :
357 : 8 : inline string hex(unsigned int value, int width)
358 : : {
359 : : char buffer[64];
360 : 8 : sprintf(buffer, "%.*X", width, value);
361 : 12 : return buffer;
362 : : }
363 : :
364 : 1990 : inline string dec(int value)
365 : : {
366 : : char buffer[64];
367 : 1990 : sprintf(buffer, "%i", value);
368 : 2985 : return buffer;
369 : : }
370 : :
371 : 2166 : 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 : 2166 : sprintf(rval, "%.100f", value);
379 [ + - ]: 2166 : if (strchr(rval, '.'))//nuke useless zeroes
380 : : {
381 : 2166 : char * end=strrchr(rval, '\0')-1;
382 [ + + ]: 218418 : while (*end=='0')
383 : : {
384 : 216252 : *end='\0';
385 : 216252 : end--;
386 : : }
387 [ + + ]: 2166 : if (*end=='.') *end='\0';
388 : : }
389 : 3249 : return rval;
390 : : }
391 : :
392 : : // Same as above, but with variable precision
393 : 46 : inline string ftostrvar(double value, int precision)
394 : : {
395 : 23 : int clampedprecision = precision;
396 [ - + ]: 23 : if (clampedprecision < 0) clampedprecision = 0;
397 [ - + ]: 46 : if (clampedprecision > 100) clampedprecision = 100;
398 : :
399 : : // see above
400 : : char rval[512];
401 : 46 : sprintf(rval, "%.*f", clampedprecision, (double)value);
402 [ + + ]: 46 : if (strchr(rval, '.'))//nuke useless zeroes
403 : : {
404 : 44 : char * end = strrchr(rval, '\0') - 1;
405 [ + + ]: 182 : while (*end == '0')
406 : : {
407 : 138 : *end = '\0';
408 : 138 : end--;
409 : : }
410 [ + + ]: 44 : if (*end == '.') *end = '\0';
411 : : }
412 : 69 : return rval;
413 : : }
414 : :
415 : 95769 : inline bool stribegin(const char * str, const char * key)
416 : : {
417 [ + + ]: 126743 : for (int i=0;key[i];i++)
418 : : {
419 [ + + ]: 119251 : if (to_lower(str[i])!=to_lower(key[i])) return false;
420 : : }
421 : 3746 : return true;
422 : : }
423 : :
424 : 7758 : inline bool striend(const char * str, const char * key)
425 : : {
426 : 3879 : const char * keyend=strrchr(key, '\0');
427 : 3879 : const char * strend=strrchr(str, '\0');
428 [ + + ]: 7758 : if(keyend-key > strend-str) return false;
429 : :
430 [ + + ]: 9852 : while (key!=keyend)
431 : : {
432 : 8886 : keyend--;
433 : 8886 : strend--;
434 [ + + ]: 8886 : if (to_lower(*strend)!=to_lower(*keyend)) return false;
435 : : }
436 : 483 : return true;
437 : : }
438 : :
439 : 101804 : inline bool stricmpwithupper(const char *word1, const char *word2)
440 : : {
441 [ + + ]: 118778 : while(*word2)
442 : : {
443 [ + + ]: 114996 : if(to_upper(*word1++) != *word2++) return true;
444 : : }
445 : 3782 : return *word1;
446 : : }
447 : :
448 : 424140 : inline bool stricmpwithlower(const char *word1, const char *word2)
449 : : {
450 [ + + ]: 645222 : while(*word2)
451 : : {
452 [ + + ]: 612145 : if(to_lower(*word1++) != *word2++) return true;
453 : : }
454 : 33077 : 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 : 5101 : inline const char * dequote(char * str)
463 : : {
464 [ + + ]: 5101 : if (*str!='"') return str;
465 : 3284 : char *end = strrchr(str, '"');
466 [ + - ]: 4357 : if (end)
467 : : {
468 : 4357 : *end = 0;
469 : 4357 : char *quote = str+1;
470 [ + + ]: 4501 : while((quote = strstr(quote, "\"\""))) memmove(quote, quote+1, strlen(quote));
471 : 3284 : return str + 1;
472 : : }
473 : 0 : return nullptr;
474 : : }
475 : :
476 : 11411 : inline char * strqchr(const char * str, char key)
477 : : {
478 [ + + ]: 117096 : while (*str != '\0')
479 : : {
480 [ + + ]: 109144 : if (*str == key) { return const_cast<char*>(str); }
481 [ + + + + ]: 105687 : 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 [ + + + + : 797 : if (str[0] == '\'' && str[1] == '\'' && str[2] == '\'') { str += 2; }
+ - ]
486 : : else
487 : : {
488 : 404 : char delimiter = *str;
489 : :
490 : : do
491 : : {
492 : 9416 : str++;
493 : :
494 : : // If we want to support backslash escapes, we'll have to add that right here.
495 [ + + + + ]: 9416 : } 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 [ + + ]: 787 : if (*str == '\0') { return nullptr; }
499 : : }
500 : : }
501 : :
502 : 105685 : str++;
503 : : }
504 : :
505 : 3976 : return nullptr;
506 : : }
507 : :
508 : 1276 : inline string substr(const char * str, int len)
509 : : {
510 : 2552 : 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 : 62035 : inline char *strip_whitespace(char *str)
519 : : {
520 [ + + ]: 65589 : while(is_space(*str)) str++;
521 [ + + ]: 78121 : for(int i = strlen(str) - 1; i >= 0; i--)
522 : : {
523 [ + + ]: 58829 : if(!is_space(str[i]))
524 : : {
525 : 42743 : str[i + 1] = 0;
526 : 42743 : return str;
527 : : }
528 : : }
529 : 9646 : return str;
530 : : }
531 : 3242 : inline void strip_whitespace(string &str)
532 : : {
533 : 3242 : str = string(strip_whitespace(str.temp_raw()));
534 : 3242 : }
535 : :
536 : : string &itrim(string &str, const char * left, const char * right);
537 : :
538 : 6742 : inline string &lower(string &old)
539 : : {
540 : 6742 : int length = old.length();
541 [ + + ]: 203993 : for (int i=0;i<length;i++) old.raw()[i]=(char)to_lower(old.data()[i]);
542 : 6742 : return old;
543 : : }
544 : :
545 : :
546 : : // Returns number of connected lines - 1
547 : : template<typename stringarraytype>
548 : 41745 : inline int getconnectedlines(stringarraytype& lines, int startline, string& out)
549 : : {
550 : 21978 : int count = 0;
551 : :
552 [ + - ]: 41763 : for (int i = startline; lines[i]; i++)
553 : : {
554 : : // The line should already be stripped of any comments at this point
555 : 41763 : int linestartpos = (int)strlen(lines[i]);
556 : :
557 [ + + + + ]: 41763 : if(linestartpos && lines[i][linestartpos - 1] == '\\')
558 : : {
559 : 18 : count++;
560 : 18 : out += string(lines[i], linestartpos - 1);
561 : 9 : continue;
562 : : }
563 : : else
564 : : {
565 : 41745 : out += string(lines[i], linestartpos);
566 : 41745 : return count;
567 : : }
568 : : }
569 : :
570 : 0 : return count;
571 : : }
|