-
Notifications
You must be signed in to change notification settings - Fork 1
/
str.h
540 lines (458 loc) · 13.5 KB
/
str.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
// vim: set et ts=4 sw=4 cino={0s:
#pragma once
#include <string>
inline size_t StrLen(const char* p) { return strlen(p); }
inline size_t StrLen(const WCHAR* p) { return wcslen(p); }
inline int StrCmp(const char* pa, const char* pb) { return strcmp(pa, pb); }
inline int StrCmp(const WCHAR* pa, const WCHAR* pb) { return wcscmp(pa, pb); }
inline int StrCmpI(const char* pa, const char* pb) { return _strcmpi(pa, pb); }
inline int StrCmpI(const WCHAR* pa, const WCHAR* pb) { return _wcsicmp(pa, pb); }
inline bool IsPathSeparator(WCHAR ch) { return ch == '/' || ch == '\\'; }
const WCHAR* StripLineStyles(const WCHAR* color);
// This is the extended max path length, including the NUL terminator.
// An explanation behind why can be found here:
// https://stackoverflow.com/questions/15262110/what-happens-internally-when-a-file-path-exceeds-approx-32767-characters-in-win
inline unsigned MaxPath() { return 32744; }
template <class T>
class Str
{
public:
Str<T>() : m_p(s_empty) {}
Str<T>(const T* p) { Set(p); }
Str<T>(const Str<T>& s) { Set(s); }
Str<T>(Str<T>&& s) { Set(std::move(s)); }
~Str<T>() { if (m_capacity) free(m_p); }
Str<T>& operator=(const T* p) { Set(p); return *this; }
Str<T>& operator=(const Str<T>& s) { Set(s); return *this; }
Str<T>& operator=(Str<T>&& s) { Set(std::move(s)); return *this; }
const T* Text() const { return m_p; }
unsigned Length() const;
unsigned Capacity() const { return m_capacity; }
bool Empty() const { return !m_p[0]; }
void Clear();
void Free();
WCHAR* Detach();
bool Equal(const T* p) const { return !StrCmp(p, Text()); }
bool Equal(const Str<T>* s) const;
bool Equal(const Str<T>& s) const { return Equal(&s); }
bool EqualI(const T* p) const { return !StrCmpI(p, Text()); }
bool EqualI(const Str<T>* s) const;
bool EqualI(const Str<T>& s) const { return EqualI(&s); }
void SetEnd(const T* end);
void SetLength(size_t len);
void Set(const T* p, size_t len=-1);
void Set(const Str<T>& s) { Set(s.Text(), s.Length()); }
void Set(Str<T>&& s);
T* Reserve(size_t capacity=0);
T* ReserveAtEnd(size_t more=0);
T* ReserveMaxPath();
void ResyncLength();
void SetAt(const T* p, T ch);
void Append(T ch);
void Append(const T* p, size_t len=-1);
void Append(const Str<T>& s) { Append(s.Text(), s.Length()); }
void AppendSpaces(int spaces);
void AppendColor(const WCHAR* color) { if (color) Printf(L"\x1b[0;%sm", color); };
void AppendColorOverlay(const WCHAR* color, const WCHAR* overlay);
void AppendColorFallback(const WCHAR* color1, const WCHAR* color2);
void AppendColorNoLineStyles(const WCHAR* color);
void AppendColorElseNormal(const WCHAR* color1);
void AppendColorElseNormalIf(const WCHAR* color1, const WCHAR* color2);
void AppendNormalIf(const WCHAR* color) { if (color) Append(L"\x1b[m"); };
void AppendNormalIf(bool yes) { if (yes) Append(L"\x1b[m"); };
void PrintfV(const T* format, va_list args);
void Printf(const T* format, ...);
void TrimRight();
void ToLower();
void ToUpper();
void Swap(Str<T>& s);
protected:
void Transform(DWORD dwMapFlags);
static int IsSpace(T ch);
protected:
T* m_p;
mutable unsigned m_length = 0;
unsigned m_capacity = 0;
static T s_empty[1];
static const T c_spaces[33];
};
class StrA;
class StrW;
inline int Str<char>::IsSpace(char ch) { return isspace(static_cast<unsigned char>(ch)); }
inline int Str<WCHAR>::IsSpace(WCHAR ch) { return iswspace(ch); }
class StrA : public Str<char>
{
public:
StrA() = default;
StrA(const char* p) { Set(p); }
StrA(const StrA& s) { Set(s); }
StrA(StrA&& s) { Set(std::move(s)); }
StrA& operator=(StrA&& s) { Set(std::move(s)); return *this; }
void SetA(const char* p, size_t len=-1) { Set(p, len); }
void SetW(const WCHAR* p, size_t len=-1);
void SetA(const StrA& s) { SetA(s.Text(), s.Length()); }
void SetW(const StrW& s);
};
class StrW : public Str<WCHAR>
{
public:
StrW() = default;
StrW(const WCHAR* p) { Set(p); }
StrW(const StrW& s) { Set(s); }
StrW(StrW&& s) { Set(std::move(s)); }
StrW& operator=(StrW&& s) { Set(std::move(s)); return *this; }
void SetA(const char* p, size_t len=-1);
void SetW(const WCHAR* p, size_t len=-1) { Set(p, len); }
void SetA(const StrA& s);
void SetW(const StrW& s) { SetW(s.Text(), s.Length()); }
};
template <class T>
unsigned Str<T>::Length() const
{
if (m_length == unsigned(-1))
m_length = unsigned(StrLen(m_p));
return m_length;
}
template <class T>
void Str<T>::Clear()
{
m_length = 0;
if (m_capacity)
m_p[0] = '\0';
}
template <class T>
void Str<T>::Free()
{
if (m_capacity)
free(m_p);
m_p = s_empty;
m_length = 0;
m_capacity = 0;
}
template <class T>
WCHAR* Str<T>::Detach()
{
WCHAR* p = Capacity() ? m_p : nullptr;
m_p = nullptr;
Free();
return p;
}
template <class T>
bool Str<T>::Equal(const Str<T>* s) const
{
assert(s);
if (Length() != s->Length())
return false;
if (StrCmp(Text(), s->Text()))
return false;
return true;
}
template <class T>
bool Str<T>::EqualI(const Str<T>* s) const
{
assert(s);
if (Length() != s->Length())
return false;
if (StrCmpI(Text(), s->Text()))
return false;
return true;
}
template <class T>
void Str<T>::SetEnd(const T* end)
{
SetLength(end - m_p);
}
template <class T>
void Str<T>::SetLength(size_t len)
{
assert(len <= Length());
if (len < Length())
{
m_length = unsigned(len);
m_p[len] = '\0';
}
}
template <class T>
void Str<T>::Set(const T* p, size_t len)
{
assert(p != m_p || !m_capacity);
Clear();
Append(p, len);
}
template <class T>
void Str<T>::Set(Str<T>&& s)
{
m_p = s.m_p;
m_length = s.m_length;
m_capacity = s.m_capacity;
s.m_p = s.s_empty;
s.m_length = 0;
s.m_capacity = 0;
}
template <class T>
void Str<T>::SetAt(const T* p, T ch)
{
assert(p);
assert(ch);
assert(m_p <= p);
assert(p < m_p + StrLen(m_p));
*const_cast<T*>(p) = ch;
}
template <class T>
void Str<T>::Append(T ch)
{
assert(ch);
Append(&ch, 1);
}
template <class T>
void Str<T>::Append(const T* p, size_t len)
{
if (p)
{
if (int(len) < 0)
len = StrLen(p);
T* concat = ReserveAtEnd(len + 1);
memcpy(concat, p, len * sizeof(T));
m_length += unsigned(len);
m_p[m_length] = '\0';
}
}
template <class T>
void Str<T>::AppendSpaces(int spaces)
{
while (spaces > 0)
{
unsigned add = std::min<unsigned>(spaces, _countof(c_spaces) - 1);
Append(c_spaces, add);
spaces -= add;
}
}
template <class T>
void Str<T>::AppendColorOverlay(const WCHAR* color, const WCHAR* overlay)
{
if (color)
{
if (overlay && *overlay)
Printf(L"\x1b[0;%s;%sm", color, overlay);
else
Printf(L"\x1b[0;%sm", color);
}
else
{
if (overlay && *overlay)
Printf(L"\x1b[%sm", overlay);
}
}
template <class T>
void Str<T>::AppendColorFallback(const WCHAR* color1, const WCHAR* color2)
{
if (color1)
Printf(L"\x1b[0;%sm", color1);
else if (color2)
Printf(L"\x1b[0;%sm", color2);
}
template <class T>
void Str<T>::AppendColorNoLineStyles(const WCHAR* color)
{
if (color)
Printf(L"\x1b[0;%sm", StripLineStyles(color));
}
template <class T>
void Str<T>::AppendColorElseNormal(const WCHAR* color1)
{
if (color1)
Printf(L"\x1b[0;%sm", color1);
else
Append(L"\x1b[m");
}
template <class T>
void Str<T>::AppendColorElseNormalIf(const WCHAR* color1, const WCHAR* color2)
{
if (color1)
Printf(L"\x1b[0;%sm", color1);
else if (color2)
Append(L"\x1b[m");
}
template <class T>
T* Str<T>::Reserve(size_t capacity)
{
if (capacity > m_capacity)
{
T* const old = m_capacity ? m_p : nullptr;
T* const p = static_cast<T*>(realloc(old, capacity * sizeof(T)));
if (p)
{
m_p = p;
m_capacity = unsigned(capacity);
}
}
return m_p;
}
template <class T>
T* Str<T>::ReserveAtEnd(size_t more)
{
unsigned len = Length();
T* p = Reserve(len + more);
if (p)
p += len;
return p;
}
template <class T>
T* Str<T>::ReserveMaxPath()
{
Clear();
Reserve(MaxPath());
ResyncLength();
return m_p;
}
template <class T>
void Str<T>::ResyncLength()
{
m_length = unsigned(-1);
}
int __vsnprintf(char* buffer, size_t len, const char* format, va_list args);
int __vsnprintf(WCHAR* buffer, size_t len, const WCHAR* format, va_list args);
template <class T>
void Str<T>::PrintfV(const T* format, va_list args)
{
const size_t len = Length();
size_t cap = Capacity() - Length();
int res = -1;
if (cap > 1)
{
errno = 0;
res = __vsnprintf(m_p + len, cap, format, args);
if (errno)
{
assert(false);
return;
}
}
if (res < 0)
{
cap = std::max<size_t>(cap * 2, 100);
while (res < 0)
{
// Subtract 1 from the max character count to ensure room for null
// terminator; see MSDN for idiosyncracy of the snprintf family of
// functions.
errno = 0;
res = __vsnprintf(Reserve(len + cap) + len, cap, format, args);
if (errno)
{
assert(false);
return;
}
cap *= 2;
}
}
m_length += res;
assert(m_length == StrLen(m_p));
assert(m_length < m_capacity);
}
template <class T>
void Str<T>::Printf(const T* format, ...)
{
va_list args;
va_start(args, format);
PrintfV(format, args);
va_end(args);
}
template <class T>
void Str<T>::TrimRight()
{
if (Length())
{
const T* last = Text() + Length() - 1;
while (last >= Text() && IsSpace(*last))
--last;
SetEnd(last + 1);
}
}
template <>
inline void Str<char>::ToLower()
{
StrW tmp;
tmp.SetA(Text(), Length());
tmp.ToLower();
StrA tmp2;
tmp2.SetW(tmp.Text(), tmp.Length());
Set(std::move(tmp2));
}
template <>
inline void Str<WCHAR>::ToLower()
{
Transform(LCMAP_LOWERCASE);
}
template <>
inline void Str<char>::ToUpper()
{
StrW tmp;
tmp.SetA(Text(), Length());
tmp.ToUpper();
StrA tmp2;
tmp2.SetW(tmp.Text(), tmp.Length());
Set(std::move(tmp2));
}
template <>
inline void Str<WCHAR>::ToUpper()
{
Transform(LCMAP_UPPERCASE);
}
template <>
inline void Str<WCHAR>::Transform(DWORD dwMapFlags)
{
int len = Length();
Str<WCHAR> tmp;
WCHAR* p = tmp.Reserve(len + std::max<size_t>(len / 10, 10));
len = LCMapStringW(LOCALE_USER_DEFAULT, dwMapFlags, Text(), int(Length()), p, int(tmp.Capacity()));
if (!len)
{
len = LCMapStringW(LOCALE_USER_DEFAULT, dwMapFlags, Text(), int(Length()), nullptr, 0);
p = tmp.Reserve(len + 1);
len = LCMapStringW(LOCALE_USER_DEFAULT, dwMapFlags, Text(), int(Length()), p, int(tmp.Capacity()));
}
Swap(tmp);
m_length = len;
m_p[len] = '\0';
assert(m_length < m_capacity);
}
template <class T>
void Str<T>::Swap(Str<T>& s)
{
T* p = m_p;
m_p = s.m_p;
s.m_p = p;
unsigned len = m_length;
m_length = s.m_length;
s.m_length = len;
unsigned cap = m_capacity;
m_capacity = s.m_capacity;
s.m_capacity = cap;
}
/*
* StrA and StrW classes.
*/
inline void StrA::SetW(const StrW& s)
{
SetW(s.Text(), s.Length());
}
inline void StrW::SetA(const StrA& s)
{
SetA(s.Text(), s.Length());
}
/*
* String helpers.
*/
WCHAR* CopyStr(const WCHAR* p);
void StripTrailingSlashes(StrW& s);
void EnsureTrailingSlash(StrW& s);
void PathJoin(StrW& out, const WCHAR* dir, const WCHAR* file);
void PathJoin(StrW& out, const WCHAR* dir, const StrW& file);
unsigned TruncateWcwidth(StrW& s, unsigned truncate_width, WCHAR truncation_char);
struct SortCase { bool operator()(const WCHAR* a, const WCHAR* b) const noexcept; };
struct SortCaseless { bool operator()(const WCHAR* a, const WCHAR* b) const noexcept; };
struct EqualCase { bool operator()(const WCHAR* a, const WCHAR* b) const noexcept; };
struct EqualCaseless { bool operator()(const WCHAR* a, const WCHAR* b) const noexcept; };
struct HashCase { _NODISCARD size_t operator()(const WCHAR* key) const noexcept; };
struct HashCaseless { _NODISCARD size_t operator()(const WCHAR* key) const noexcept; };