summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h')
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h3086
1 files changed, 3086 insertions, 0 deletions
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h b/xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h
new file mode 100644
index 0000000..977c0e5
--- /dev/null
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/tools/StringUtils.h
@@ -0,0 +1,3086 @@
1/*
2 * Copyright (C) 2005-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9#pragma once
10
11#ifdef __cplusplus
12
13#if !defined(NOMINMAX)
14#define NOMINMAX
15#endif
16
17#include <algorithm>
18#include <array>
19#include <cassert>
20#include <cinttypes>
21#include <cmath>
22#include <cstdarg>
23#include <cstring>
24#include <iomanip>
25#include <regex>
26#include <sstream>
27#include <string>
28#include <vector>
29
30// # of bytes for initial allocation for printf
31#define FORMAT_BLOCK_SIZE 512
32
33// macros for gcc, clang & others
34#ifndef PARAM1_PRINTF_FORMAT
35#ifdef __GNUC__
36// for use in functions that take printf format string as first parameter and additional printf parameters as second parameter
37// for example: int myprintf(const char* format, ...) PARAM1_PRINTF_FORMAT;
38#define PARAM1_PRINTF_FORMAT __attribute__((format(printf, 1, 2)))
39
40// for use in functions that take printf format string as second parameter and additional printf parameters as third parameter
41// for example: bool log_string(int logLevel, const char* format, ...) PARAM2_PRINTF_FORMAT;
42// note: all non-static class member functions take pointer to class object as hidden first parameter
43#define PARAM2_PRINTF_FORMAT __attribute__((format(printf, 2, 3)))
44
45// for use in functions that take printf format string as third parameter and additional printf parameters as fourth parameter
46// note: all non-static class member functions take pointer to class object as hidden first parameter
47// for example: class A { bool log_string(int logLevel, const char* functionName, const char* format, ...) PARAM3_PRINTF_FORMAT; };
48#define PARAM3_PRINTF_FORMAT __attribute__((format(printf, 3, 4)))
49
50// for use in functions that take printf format string as fourth parameter and additional printf parameters as fith parameter
51// note: all non-static class member functions take pointer to class object as hidden first parameter
52// for example: class A { bool log_string(int logLevel, const char* functionName, int component, const char* format, ...) PARAM4_PRINTF_FORMAT; };
53#define PARAM4_PRINTF_FORMAT __attribute__((format(printf, 4, 5)))
54#else // ! __GNUC__
55#define PARAM1_PRINTF_FORMAT
56#define PARAM2_PRINTF_FORMAT
57#define PARAM3_PRINTF_FORMAT
58#define PARAM4_PRINTF_FORMAT
59#endif // ! __GNUC__
60#endif // PARAM1_PRINTF_FORMAT
61
62// macros for VC
63// VC check parameters only when "Code Analysis" is called
64#ifndef PRINTF_FORMAT_STRING
65#ifdef _MSC_VER
66#include <sal.h>
67
68// for use in any function that take printf format string and parameters
69// for example: bool log_string(int logLevel, PRINTF_FORMAT_STRING const char* format, ...);
70#define PRINTF_FORMAT_STRING _In_z_ _Printf_format_string_
71
72// specify that parameter must be zero-terminated string
73// for example: void SetName(IN_STRING const char* newName);
74#define IN_STRING _In_z_
75
76// specify that parameter must be zero-terminated string or NULL
77// for example: bool SetAdditionalName(IN_OPT_STRING const char* addName);
78#define IN_OPT_STRING _In_opt_z_
79#else // ! _MSC_VER
80#define PRINTF_FORMAT_STRING
81#define IN_STRING
82#define IN_OPT_STRING
83#endif // ! _MSC_VER
84#endif // PRINTF_FORMAT_STRING
85
86static constexpr wchar_t unicode_lowers[] = {
87 (wchar_t)0x0061, (wchar_t)0x0062, (wchar_t)0x0063, (wchar_t)0x0064, (wchar_t)0x0065,
88 (wchar_t)0x0066, (wchar_t)0x0067, (wchar_t)0x0068, (wchar_t)0x0069, (wchar_t)0x006A,
89 (wchar_t)0x006B, (wchar_t)0x006C, (wchar_t)0x006D, (wchar_t)0x006E, (wchar_t)0x006F,
90 (wchar_t)0x0070, (wchar_t)0x0071, (wchar_t)0x0072, (wchar_t)0x0073, (wchar_t)0x0074,
91 (wchar_t)0x0075, (wchar_t)0x0076, (wchar_t)0x0077, (wchar_t)0x0078, (wchar_t)0x0079,
92 (wchar_t)0x007A, (wchar_t)0x00E0, (wchar_t)0x00E1, (wchar_t)0x00E2, (wchar_t)0x00E3,
93 (wchar_t)0x00E4, (wchar_t)0x00E5, (wchar_t)0x00E6, (wchar_t)0x00E7, (wchar_t)0x00E8,
94 (wchar_t)0x00E9, (wchar_t)0x00EA, (wchar_t)0x00EB, (wchar_t)0x00EC, (wchar_t)0x00ED,
95 (wchar_t)0x00EE, (wchar_t)0x00EF, (wchar_t)0x00F0, (wchar_t)0x00F1, (wchar_t)0x00F2,
96 (wchar_t)0x00F3, (wchar_t)0x00F4, (wchar_t)0x00F5, (wchar_t)0x00F6, (wchar_t)0x00F8,
97 (wchar_t)0x00F9, (wchar_t)0x00FA, (wchar_t)0x00FB, (wchar_t)0x00FC, (wchar_t)0x00FD,
98 (wchar_t)0x00FE, (wchar_t)0x00FF, (wchar_t)0x0101, (wchar_t)0x0103, (wchar_t)0x0105,
99 (wchar_t)0x0107, (wchar_t)0x0109, (wchar_t)0x010B, (wchar_t)0x010D, (wchar_t)0x010F,
100 (wchar_t)0x0111, (wchar_t)0x0113, (wchar_t)0x0115, (wchar_t)0x0117, (wchar_t)0x0119,
101 (wchar_t)0x011B, (wchar_t)0x011D, (wchar_t)0x011F, (wchar_t)0x0121, (wchar_t)0x0123,
102 (wchar_t)0x0125, (wchar_t)0x0127, (wchar_t)0x0129, (wchar_t)0x012B, (wchar_t)0x012D,
103 (wchar_t)0x012F, (wchar_t)0x0131, (wchar_t)0x0133, (wchar_t)0x0135, (wchar_t)0x0137,
104 (wchar_t)0x013A, (wchar_t)0x013C, (wchar_t)0x013E, (wchar_t)0x0140, (wchar_t)0x0142,
105 (wchar_t)0x0144, (wchar_t)0x0146, (wchar_t)0x0148, (wchar_t)0x014B, (wchar_t)0x014D,
106 (wchar_t)0x014F, (wchar_t)0x0151, (wchar_t)0x0153, (wchar_t)0x0155, (wchar_t)0x0157,
107 (wchar_t)0x0159, (wchar_t)0x015B, (wchar_t)0x015D, (wchar_t)0x015F, (wchar_t)0x0161,
108 (wchar_t)0x0163, (wchar_t)0x0165, (wchar_t)0x0167, (wchar_t)0x0169, (wchar_t)0x016B,
109 (wchar_t)0x016D, (wchar_t)0x016F, (wchar_t)0x0171, (wchar_t)0x0173, (wchar_t)0x0175,
110 (wchar_t)0x0177, (wchar_t)0x017A, (wchar_t)0x017C, (wchar_t)0x017E, (wchar_t)0x0183,
111 (wchar_t)0x0185, (wchar_t)0x0188, (wchar_t)0x018C, (wchar_t)0x0192, (wchar_t)0x0199,
112 (wchar_t)0x01A1, (wchar_t)0x01A3, (wchar_t)0x01A5, (wchar_t)0x01A8, (wchar_t)0x01AD,
113 (wchar_t)0x01B0, (wchar_t)0x01B4, (wchar_t)0x01B6, (wchar_t)0x01B9, (wchar_t)0x01BD,
114 (wchar_t)0x01C6, (wchar_t)0x01C9, (wchar_t)0x01CC, (wchar_t)0x01CE, (wchar_t)0x01D0,
115 (wchar_t)0x01D2, (wchar_t)0x01D4, (wchar_t)0x01D6, (wchar_t)0x01D8, (wchar_t)0x01DA,
116 (wchar_t)0x01DC, (wchar_t)0x01DF, (wchar_t)0x01E1, (wchar_t)0x01E3, (wchar_t)0x01E5,
117 (wchar_t)0x01E7, (wchar_t)0x01E9, (wchar_t)0x01EB, (wchar_t)0x01ED, (wchar_t)0x01EF,
118 (wchar_t)0x01F3, (wchar_t)0x01F5, (wchar_t)0x01FB, (wchar_t)0x01FD, (wchar_t)0x01FF,
119 (wchar_t)0x0201, (wchar_t)0x0203, (wchar_t)0x0205, (wchar_t)0x0207, (wchar_t)0x0209,
120 (wchar_t)0x020B, (wchar_t)0x020D, (wchar_t)0x020F, (wchar_t)0x0211, (wchar_t)0x0213,
121 (wchar_t)0x0215, (wchar_t)0x0217, (wchar_t)0x0253, (wchar_t)0x0254, (wchar_t)0x0257,
122 (wchar_t)0x0258, (wchar_t)0x0259, (wchar_t)0x025B, (wchar_t)0x0260, (wchar_t)0x0263,
123 (wchar_t)0x0268, (wchar_t)0x0269, (wchar_t)0x026F, (wchar_t)0x0272, (wchar_t)0x0275,
124 (wchar_t)0x0283, (wchar_t)0x0288, (wchar_t)0x028A, (wchar_t)0x028B, (wchar_t)0x0292,
125 (wchar_t)0x03AC, (wchar_t)0x03AD, (wchar_t)0x03AE, (wchar_t)0x03AF, (wchar_t)0x03B1,
126 (wchar_t)0x03B2, (wchar_t)0x03B3, (wchar_t)0x03B4, (wchar_t)0x03B5, (wchar_t)0x03B6,
127 (wchar_t)0x03B7, (wchar_t)0x03B8, (wchar_t)0x03B9, (wchar_t)0x03BA, (wchar_t)0x03BB,
128 (wchar_t)0x03BC, (wchar_t)0x03BD, (wchar_t)0x03BE, (wchar_t)0x03BF, (wchar_t)0x03C0,
129 (wchar_t)0x03C1, (wchar_t)0x03C3, (wchar_t)0x03C4, (wchar_t)0x03C5, (wchar_t)0x03C6,
130 (wchar_t)0x03C7, (wchar_t)0x03C8, (wchar_t)0x03C9, (wchar_t)0x03CA, (wchar_t)0x03CB,
131 (wchar_t)0x03CC, (wchar_t)0x03CD, (wchar_t)0x03CE, (wchar_t)0x03E3, (wchar_t)0x03E5,
132 (wchar_t)0x03E7, (wchar_t)0x03E9, (wchar_t)0x03EB, (wchar_t)0x03ED, (wchar_t)0x03EF,
133 (wchar_t)0x0430, (wchar_t)0x0431, (wchar_t)0x0432, (wchar_t)0x0433, (wchar_t)0x0434,
134 (wchar_t)0x0435, (wchar_t)0x0436, (wchar_t)0x0437, (wchar_t)0x0438, (wchar_t)0x0439,
135 (wchar_t)0x043A, (wchar_t)0x043B, (wchar_t)0x043C, (wchar_t)0x043D, (wchar_t)0x043E,
136 (wchar_t)0x043F, (wchar_t)0x0440, (wchar_t)0x0441, (wchar_t)0x0442, (wchar_t)0x0443,
137 (wchar_t)0x0444, (wchar_t)0x0445, (wchar_t)0x0446, (wchar_t)0x0447, (wchar_t)0x0448,
138 (wchar_t)0x0449, (wchar_t)0x044A, (wchar_t)0x044B, (wchar_t)0x044C, (wchar_t)0x044D,
139 (wchar_t)0x044E, (wchar_t)0x044F, (wchar_t)0x0451, (wchar_t)0x0452, (wchar_t)0x0453,
140 (wchar_t)0x0454, (wchar_t)0x0455, (wchar_t)0x0456, (wchar_t)0x0457, (wchar_t)0x0458,
141 (wchar_t)0x0459, (wchar_t)0x045A, (wchar_t)0x045B, (wchar_t)0x045C, (wchar_t)0x045E,
142 (wchar_t)0x045F, (wchar_t)0x0461, (wchar_t)0x0463, (wchar_t)0x0465, (wchar_t)0x0467,
143 (wchar_t)0x0469, (wchar_t)0x046B, (wchar_t)0x046D, (wchar_t)0x046F, (wchar_t)0x0471,
144 (wchar_t)0x0473, (wchar_t)0x0475, (wchar_t)0x0477, (wchar_t)0x0479, (wchar_t)0x047B,
145 (wchar_t)0x047D, (wchar_t)0x047F, (wchar_t)0x0481, (wchar_t)0x0491, (wchar_t)0x0493,
146 (wchar_t)0x0495, (wchar_t)0x0497, (wchar_t)0x0499, (wchar_t)0x049B, (wchar_t)0x049D,
147 (wchar_t)0x049F, (wchar_t)0x04A1, (wchar_t)0x04A3, (wchar_t)0x04A5, (wchar_t)0x04A7,
148 (wchar_t)0x04A9, (wchar_t)0x04AB, (wchar_t)0x04AD, (wchar_t)0x04AF, (wchar_t)0x04B1,
149 (wchar_t)0x04B3, (wchar_t)0x04B5, (wchar_t)0x04B7, (wchar_t)0x04B9, (wchar_t)0x04BB,
150 (wchar_t)0x04BD, (wchar_t)0x04BF, (wchar_t)0x04C2, (wchar_t)0x04C4, (wchar_t)0x04C8,
151 (wchar_t)0x04CC, (wchar_t)0x04D1, (wchar_t)0x04D3, (wchar_t)0x04D5, (wchar_t)0x04D7,
152 (wchar_t)0x04D9, (wchar_t)0x04DB, (wchar_t)0x04DD, (wchar_t)0x04DF, (wchar_t)0x04E1,
153 (wchar_t)0x04E3, (wchar_t)0x04E5, (wchar_t)0x04E7, (wchar_t)0x04E9, (wchar_t)0x04EB,
154 (wchar_t)0x04EF, (wchar_t)0x04F1, (wchar_t)0x04F3, (wchar_t)0x04F5, (wchar_t)0x04F9,
155 (wchar_t)0x0561, (wchar_t)0x0562, (wchar_t)0x0563, (wchar_t)0x0564, (wchar_t)0x0565,
156 (wchar_t)0x0566, (wchar_t)0x0567, (wchar_t)0x0568, (wchar_t)0x0569, (wchar_t)0x056A,
157 (wchar_t)0x056B, (wchar_t)0x056C, (wchar_t)0x056D, (wchar_t)0x056E, (wchar_t)0x056F,
158 (wchar_t)0x0570, (wchar_t)0x0571, (wchar_t)0x0572, (wchar_t)0x0573, (wchar_t)0x0574,
159 (wchar_t)0x0575, (wchar_t)0x0576, (wchar_t)0x0577, (wchar_t)0x0578, (wchar_t)0x0579,
160 (wchar_t)0x057A, (wchar_t)0x057B, (wchar_t)0x057C, (wchar_t)0x057D, (wchar_t)0x057E,
161 (wchar_t)0x057F, (wchar_t)0x0580, (wchar_t)0x0581, (wchar_t)0x0582, (wchar_t)0x0583,
162 (wchar_t)0x0584, (wchar_t)0x0585, (wchar_t)0x0586, (wchar_t)0x10D0, (wchar_t)0x10D1,
163 (wchar_t)0x10D2, (wchar_t)0x10D3, (wchar_t)0x10D4, (wchar_t)0x10D5, (wchar_t)0x10D6,
164 (wchar_t)0x10D7, (wchar_t)0x10D8, (wchar_t)0x10D9, (wchar_t)0x10DA, (wchar_t)0x10DB,
165 (wchar_t)0x10DC, (wchar_t)0x10DD, (wchar_t)0x10DE, (wchar_t)0x10DF, (wchar_t)0x10E0,
166 (wchar_t)0x10E1, (wchar_t)0x10E2, (wchar_t)0x10E3, (wchar_t)0x10E4, (wchar_t)0x10E5,
167 (wchar_t)0x10E6, (wchar_t)0x10E7, (wchar_t)0x10E8, (wchar_t)0x10E9, (wchar_t)0x10EA,
168 (wchar_t)0x10EB, (wchar_t)0x10EC, (wchar_t)0x10ED, (wchar_t)0x10EE, (wchar_t)0x10EF,
169 (wchar_t)0x10F0, (wchar_t)0x10F1, (wchar_t)0x10F2, (wchar_t)0x10F3, (wchar_t)0x10F4,
170 (wchar_t)0x10F5, (wchar_t)0x1E01, (wchar_t)0x1E03, (wchar_t)0x1E05, (wchar_t)0x1E07,
171 (wchar_t)0x1E09, (wchar_t)0x1E0B, (wchar_t)0x1E0D, (wchar_t)0x1E0F, (wchar_t)0x1E11,
172 (wchar_t)0x1E13, (wchar_t)0x1E15, (wchar_t)0x1E17, (wchar_t)0x1E19, (wchar_t)0x1E1B,
173 (wchar_t)0x1E1D, (wchar_t)0x1E1F, (wchar_t)0x1E21, (wchar_t)0x1E23, (wchar_t)0x1E25,
174 (wchar_t)0x1E27, (wchar_t)0x1E29, (wchar_t)0x1E2B, (wchar_t)0x1E2D, (wchar_t)0x1E2F,
175 (wchar_t)0x1E31, (wchar_t)0x1E33, (wchar_t)0x1E35, (wchar_t)0x1E37, (wchar_t)0x1E39,
176 (wchar_t)0x1E3B, (wchar_t)0x1E3D, (wchar_t)0x1E3F, (wchar_t)0x1E41, (wchar_t)0x1E43,
177 (wchar_t)0x1E45, (wchar_t)0x1E47, (wchar_t)0x1E49, (wchar_t)0x1E4B, (wchar_t)0x1E4D,
178 (wchar_t)0x1E4F, (wchar_t)0x1E51, (wchar_t)0x1E53, (wchar_t)0x1E55, (wchar_t)0x1E57,
179 (wchar_t)0x1E59, (wchar_t)0x1E5B, (wchar_t)0x1E5D, (wchar_t)0x1E5F, (wchar_t)0x1E61,
180 (wchar_t)0x1E63, (wchar_t)0x1E65, (wchar_t)0x1E67, (wchar_t)0x1E69, (wchar_t)0x1E6B,
181 (wchar_t)0x1E6D, (wchar_t)0x1E6F, (wchar_t)0x1E71, (wchar_t)0x1E73, (wchar_t)0x1E75,
182 (wchar_t)0x1E77, (wchar_t)0x1E79, (wchar_t)0x1E7B, (wchar_t)0x1E7D, (wchar_t)0x1E7F,
183 (wchar_t)0x1E81, (wchar_t)0x1E83, (wchar_t)0x1E85, (wchar_t)0x1E87, (wchar_t)0x1E89,
184 (wchar_t)0x1E8B, (wchar_t)0x1E8D, (wchar_t)0x1E8F, (wchar_t)0x1E91, (wchar_t)0x1E93,
185 (wchar_t)0x1E95, (wchar_t)0x1EA1, (wchar_t)0x1EA3, (wchar_t)0x1EA5, (wchar_t)0x1EA7,
186 (wchar_t)0x1EA9, (wchar_t)0x1EAB, (wchar_t)0x1EAD, (wchar_t)0x1EAF, (wchar_t)0x1EB1,
187 (wchar_t)0x1EB3, (wchar_t)0x1EB5, (wchar_t)0x1EB7, (wchar_t)0x1EB9, (wchar_t)0x1EBB,
188 (wchar_t)0x1EBD, (wchar_t)0x1EBF, (wchar_t)0x1EC1, (wchar_t)0x1EC3, (wchar_t)0x1EC5,
189 (wchar_t)0x1EC7, (wchar_t)0x1EC9, (wchar_t)0x1ECB, (wchar_t)0x1ECD, (wchar_t)0x1ECF,
190 (wchar_t)0x1ED1, (wchar_t)0x1ED3, (wchar_t)0x1ED5, (wchar_t)0x1ED7, (wchar_t)0x1ED9,
191 (wchar_t)0x1EDB, (wchar_t)0x1EDD, (wchar_t)0x1EDF, (wchar_t)0x1EE1, (wchar_t)0x1EE3,
192 (wchar_t)0x1EE5, (wchar_t)0x1EE7, (wchar_t)0x1EE9, (wchar_t)0x1EEB, (wchar_t)0x1EED,
193 (wchar_t)0x1EEF, (wchar_t)0x1EF1, (wchar_t)0x1EF3, (wchar_t)0x1EF5, (wchar_t)0x1EF7,
194 (wchar_t)0x1EF9, (wchar_t)0x1F00, (wchar_t)0x1F01, (wchar_t)0x1F02, (wchar_t)0x1F03,
195 (wchar_t)0x1F04, (wchar_t)0x1F05, (wchar_t)0x1F06, (wchar_t)0x1F07, (wchar_t)0x1F10,
196 (wchar_t)0x1F11, (wchar_t)0x1F12, (wchar_t)0x1F13, (wchar_t)0x1F14, (wchar_t)0x1F15,
197 (wchar_t)0x1F20, (wchar_t)0x1F21, (wchar_t)0x1F22, (wchar_t)0x1F23, (wchar_t)0x1F24,
198 (wchar_t)0x1F25, (wchar_t)0x1F26, (wchar_t)0x1F27, (wchar_t)0x1F30, (wchar_t)0x1F31,
199 (wchar_t)0x1F32, (wchar_t)0x1F33, (wchar_t)0x1F34, (wchar_t)0x1F35, (wchar_t)0x1F36,
200 (wchar_t)0x1F37, (wchar_t)0x1F40, (wchar_t)0x1F41, (wchar_t)0x1F42, (wchar_t)0x1F43,
201 (wchar_t)0x1F44, (wchar_t)0x1F45, (wchar_t)0x1F51, (wchar_t)0x1F53, (wchar_t)0x1F55,
202 (wchar_t)0x1F57, (wchar_t)0x1F60, (wchar_t)0x1F61, (wchar_t)0x1F62, (wchar_t)0x1F63,
203 (wchar_t)0x1F64, (wchar_t)0x1F65, (wchar_t)0x1F66, (wchar_t)0x1F67, (wchar_t)0x1F80,
204 (wchar_t)0x1F81, (wchar_t)0x1F82, (wchar_t)0x1F83, (wchar_t)0x1F84, (wchar_t)0x1F85,
205 (wchar_t)0x1F86, (wchar_t)0x1F87, (wchar_t)0x1F90, (wchar_t)0x1F91, (wchar_t)0x1F92,
206 (wchar_t)0x1F93, (wchar_t)0x1F94, (wchar_t)0x1F95, (wchar_t)0x1F96, (wchar_t)0x1F97,
207 (wchar_t)0x1FA0, (wchar_t)0x1FA1, (wchar_t)0x1FA2, (wchar_t)0x1FA3, (wchar_t)0x1FA4,
208 (wchar_t)0x1FA5, (wchar_t)0x1FA6, (wchar_t)0x1FA7, (wchar_t)0x1FB0, (wchar_t)0x1FB1,
209 (wchar_t)0x1FD0, (wchar_t)0x1FD1, (wchar_t)0x1FE0, (wchar_t)0x1FE1, (wchar_t)0x24D0,
210 (wchar_t)0x24D1, (wchar_t)0x24D2, (wchar_t)0x24D3, (wchar_t)0x24D4, (wchar_t)0x24D5,
211 (wchar_t)0x24D6, (wchar_t)0x24D7, (wchar_t)0x24D8, (wchar_t)0x24D9, (wchar_t)0x24DA,
212 (wchar_t)0x24DB, (wchar_t)0x24DC, (wchar_t)0x24DD, (wchar_t)0x24DE, (wchar_t)0x24DF,
213 (wchar_t)0x24E0, (wchar_t)0x24E1, (wchar_t)0x24E2, (wchar_t)0x24E3, (wchar_t)0x24E4,
214 (wchar_t)0x24E5, (wchar_t)0x24E6, (wchar_t)0x24E7, (wchar_t)0x24E8, (wchar_t)0x24E9,
215 (wchar_t)0xFF41, (wchar_t)0xFF42, (wchar_t)0xFF43, (wchar_t)0xFF44, (wchar_t)0xFF45,
216 (wchar_t)0xFF46, (wchar_t)0xFF47, (wchar_t)0xFF48, (wchar_t)0xFF49, (wchar_t)0xFF4A,
217 (wchar_t)0xFF4B, (wchar_t)0xFF4C, (wchar_t)0xFF4D, (wchar_t)0xFF4E, (wchar_t)0xFF4F,
218 (wchar_t)0xFF50, (wchar_t)0xFF51, (wchar_t)0xFF52, (wchar_t)0xFF53, (wchar_t)0xFF54,
219 (wchar_t)0xFF55, (wchar_t)0xFF56, (wchar_t)0xFF57, (wchar_t)0xFF58, (wchar_t)0xFF59,
220 (wchar_t)0xFF5A};
221
222static const wchar_t unicode_uppers[] = {
223 (wchar_t)0x0041, (wchar_t)0x0042, (wchar_t)0x0043, (wchar_t)0x0044, (wchar_t)0x0045,
224 (wchar_t)0x0046, (wchar_t)0x0047, (wchar_t)0x0048, (wchar_t)0x0049, (wchar_t)0x004A,
225 (wchar_t)0x004B, (wchar_t)0x004C, (wchar_t)0x004D, (wchar_t)0x004E, (wchar_t)0x004F,
226 (wchar_t)0x0050, (wchar_t)0x0051, (wchar_t)0x0052, (wchar_t)0x0053, (wchar_t)0x0054,
227 (wchar_t)0x0055, (wchar_t)0x0056, (wchar_t)0x0057, (wchar_t)0x0058, (wchar_t)0x0059,
228 (wchar_t)0x005A, (wchar_t)0x00C0, (wchar_t)0x00C1, (wchar_t)0x00C2, (wchar_t)0x00C3,
229 (wchar_t)0x00C4, (wchar_t)0x00C5, (wchar_t)0x00C6, (wchar_t)0x00C7, (wchar_t)0x00C8,
230 (wchar_t)0x00C9, (wchar_t)0x00CA, (wchar_t)0x00CB, (wchar_t)0x00CC, (wchar_t)0x00CD,
231 (wchar_t)0x00CE, (wchar_t)0x00CF, (wchar_t)0x00D0, (wchar_t)0x00D1, (wchar_t)0x00D2,
232 (wchar_t)0x00D3, (wchar_t)0x00D4, (wchar_t)0x00D5, (wchar_t)0x00D6, (wchar_t)0x00D8,
233 (wchar_t)0x00D9, (wchar_t)0x00DA, (wchar_t)0x00DB, (wchar_t)0x00DC, (wchar_t)0x00DD,
234 (wchar_t)0x00DE, (wchar_t)0x0178, (wchar_t)0x0100, (wchar_t)0x0102, (wchar_t)0x0104,
235 (wchar_t)0x0106, (wchar_t)0x0108, (wchar_t)0x010A, (wchar_t)0x010C, (wchar_t)0x010E,
236 (wchar_t)0x0110, (wchar_t)0x0112, (wchar_t)0x0114, (wchar_t)0x0116, (wchar_t)0x0118,
237 (wchar_t)0x011A, (wchar_t)0x011C, (wchar_t)0x011E, (wchar_t)0x0120, (wchar_t)0x0122,
238 (wchar_t)0x0124, (wchar_t)0x0126, (wchar_t)0x0128, (wchar_t)0x012A, (wchar_t)0x012C,
239 (wchar_t)0x012E, (wchar_t)0x0049, (wchar_t)0x0132, (wchar_t)0x0134, (wchar_t)0x0136,
240 (wchar_t)0x0139, (wchar_t)0x013B, (wchar_t)0x013D, (wchar_t)0x013F, (wchar_t)0x0141,
241 (wchar_t)0x0143, (wchar_t)0x0145, (wchar_t)0x0147, (wchar_t)0x014A, (wchar_t)0x014C,
242 (wchar_t)0x014E, (wchar_t)0x0150, (wchar_t)0x0152, (wchar_t)0x0154, (wchar_t)0x0156,
243 (wchar_t)0x0158, (wchar_t)0x015A, (wchar_t)0x015C, (wchar_t)0x015E, (wchar_t)0x0160,
244 (wchar_t)0x0162, (wchar_t)0x0164, (wchar_t)0x0166, (wchar_t)0x0168, (wchar_t)0x016A,
245 (wchar_t)0x016C, (wchar_t)0x016E, (wchar_t)0x0170, (wchar_t)0x0172, (wchar_t)0x0174,
246 (wchar_t)0x0176, (wchar_t)0x0179, (wchar_t)0x017B, (wchar_t)0x017D, (wchar_t)0x0182,
247 (wchar_t)0x0184, (wchar_t)0x0187, (wchar_t)0x018B, (wchar_t)0x0191, (wchar_t)0x0198,
248 (wchar_t)0x01A0, (wchar_t)0x01A2, (wchar_t)0x01A4, (wchar_t)0x01A7, (wchar_t)0x01AC,
249 (wchar_t)0x01AF, (wchar_t)0x01B3, (wchar_t)0x01B5, (wchar_t)0x01B8, (wchar_t)0x01BC,
250 (wchar_t)0x01C4, (wchar_t)0x01C7, (wchar_t)0x01CA, (wchar_t)0x01CD, (wchar_t)0x01CF,
251 (wchar_t)0x01D1, (wchar_t)0x01D3, (wchar_t)0x01D5, (wchar_t)0x01D7, (wchar_t)0x01D9,
252 (wchar_t)0x01DB, (wchar_t)0x01DE, (wchar_t)0x01E0, (wchar_t)0x01E2, (wchar_t)0x01E4,
253 (wchar_t)0x01E6, (wchar_t)0x01E8, (wchar_t)0x01EA, (wchar_t)0x01EC, (wchar_t)0x01EE,
254 (wchar_t)0x01F1, (wchar_t)0x01F4, (wchar_t)0x01FA, (wchar_t)0x01FC, (wchar_t)0x01FE,
255 (wchar_t)0x0200, (wchar_t)0x0202, (wchar_t)0x0204, (wchar_t)0x0206, (wchar_t)0x0208,
256 (wchar_t)0x020A, (wchar_t)0x020C, (wchar_t)0x020E, (wchar_t)0x0210, (wchar_t)0x0212,
257 (wchar_t)0x0214, (wchar_t)0x0216, (wchar_t)0x0181, (wchar_t)0x0186, (wchar_t)0x018A,
258 (wchar_t)0x018E, (wchar_t)0x018F, (wchar_t)0x0190, (wchar_t)0x0193, (wchar_t)0x0194,
259 (wchar_t)0x0197, (wchar_t)0x0196, (wchar_t)0x019C, (wchar_t)0x019D, (wchar_t)0x019F,
260 (wchar_t)0x01A9, (wchar_t)0x01AE, (wchar_t)0x01B1, (wchar_t)0x01B2, (wchar_t)0x01B7,
261 (wchar_t)0x0386, (wchar_t)0x0388, (wchar_t)0x0389, (wchar_t)0x038A, (wchar_t)0x0391,
262 (wchar_t)0x0392, (wchar_t)0x0393, (wchar_t)0x0394, (wchar_t)0x0395, (wchar_t)0x0396,
263 (wchar_t)0x0397, (wchar_t)0x0398, (wchar_t)0x0399, (wchar_t)0x039A, (wchar_t)0x039B,
264 (wchar_t)0x039C, (wchar_t)0x039D, (wchar_t)0x039E, (wchar_t)0x039F, (wchar_t)0x03A0,
265 (wchar_t)0x03A1, (wchar_t)0x03A3, (wchar_t)0x03A4, (wchar_t)0x03A5, (wchar_t)0x03A6,
266 (wchar_t)0x03A7, (wchar_t)0x03A8, (wchar_t)0x03A9, (wchar_t)0x03AA, (wchar_t)0x03AB,
267 (wchar_t)0x038C, (wchar_t)0x038E, (wchar_t)0x038F, (wchar_t)0x03E2, (wchar_t)0x03E4,
268 (wchar_t)0x03E6, (wchar_t)0x03E8, (wchar_t)0x03EA, (wchar_t)0x03EC, (wchar_t)0x03EE,
269 (wchar_t)0x0410, (wchar_t)0x0411, (wchar_t)0x0412, (wchar_t)0x0413, (wchar_t)0x0414,
270 (wchar_t)0x0415, (wchar_t)0x0416, (wchar_t)0x0417, (wchar_t)0x0418, (wchar_t)0x0419,
271 (wchar_t)0x041A, (wchar_t)0x041B, (wchar_t)0x041C, (wchar_t)0x041D, (wchar_t)0x041E,
272 (wchar_t)0x041F, (wchar_t)0x0420, (wchar_t)0x0421, (wchar_t)0x0422, (wchar_t)0x0423,
273 (wchar_t)0x0424, (wchar_t)0x0425, (wchar_t)0x0426, (wchar_t)0x0427, (wchar_t)0x0428,
274 (wchar_t)0x0429, (wchar_t)0x042A, (wchar_t)0x042B, (wchar_t)0x042C, (wchar_t)0x042D,
275 (wchar_t)0x042E, (wchar_t)0x042F, (wchar_t)0x0401, (wchar_t)0x0402, (wchar_t)0x0403,
276 (wchar_t)0x0404, (wchar_t)0x0405, (wchar_t)0x0406, (wchar_t)0x0407, (wchar_t)0x0408,
277 (wchar_t)0x0409, (wchar_t)0x040A, (wchar_t)0x040B, (wchar_t)0x040C, (wchar_t)0x040E,
278 (wchar_t)0x040F, (wchar_t)0x0460, (wchar_t)0x0462, (wchar_t)0x0464, (wchar_t)0x0466,
279 (wchar_t)0x0468, (wchar_t)0x046A, (wchar_t)0x046C, (wchar_t)0x046E, (wchar_t)0x0470,
280 (wchar_t)0x0472, (wchar_t)0x0474, (wchar_t)0x0476, (wchar_t)0x0478, (wchar_t)0x047A,
281 (wchar_t)0x047C, (wchar_t)0x047E, (wchar_t)0x0480, (wchar_t)0x0490, (wchar_t)0x0492,
282 (wchar_t)0x0494, (wchar_t)0x0496, (wchar_t)0x0498, (wchar_t)0x049A, (wchar_t)0x049C,
283 (wchar_t)0x049E, (wchar_t)0x04A0, (wchar_t)0x04A2, (wchar_t)0x04A4, (wchar_t)0x04A6,
284 (wchar_t)0x04A8, (wchar_t)0x04AA, (wchar_t)0x04AC, (wchar_t)0x04AE, (wchar_t)0x04B0,
285 (wchar_t)0x04B2, (wchar_t)0x04B4, (wchar_t)0x04B6, (wchar_t)0x04B8, (wchar_t)0x04BA,
286 (wchar_t)0x04BC, (wchar_t)0x04BE, (wchar_t)0x04C1, (wchar_t)0x04C3, (wchar_t)0x04C7,
287 (wchar_t)0x04CB, (wchar_t)0x04D0, (wchar_t)0x04D2, (wchar_t)0x04D4, (wchar_t)0x04D6,
288 (wchar_t)0x04D8, (wchar_t)0x04DA, (wchar_t)0x04DC, (wchar_t)0x04DE, (wchar_t)0x04E0,
289 (wchar_t)0x04E2, (wchar_t)0x04E4, (wchar_t)0x04E6, (wchar_t)0x04E8, (wchar_t)0x04EA,
290 (wchar_t)0x04EE, (wchar_t)0x04F0, (wchar_t)0x04F2, (wchar_t)0x04F4, (wchar_t)0x04F8,
291 (wchar_t)0x0531, (wchar_t)0x0532, (wchar_t)0x0533, (wchar_t)0x0534, (wchar_t)0x0535,
292 (wchar_t)0x0536, (wchar_t)0x0537, (wchar_t)0x0538, (wchar_t)0x0539, (wchar_t)0x053A,
293 (wchar_t)0x053B, (wchar_t)0x053C, (wchar_t)0x053D, (wchar_t)0x053E, (wchar_t)0x053F,
294 (wchar_t)0x0540, (wchar_t)0x0541, (wchar_t)0x0542, (wchar_t)0x0543, (wchar_t)0x0544,
295 (wchar_t)0x0545, (wchar_t)0x0546, (wchar_t)0x0547, (wchar_t)0x0548, (wchar_t)0x0549,
296 (wchar_t)0x054A, (wchar_t)0x054B, (wchar_t)0x054C, (wchar_t)0x054D, (wchar_t)0x054E,
297 (wchar_t)0x054F, (wchar_t)0x0550, (wchar_t)0x0551, (wchar_t)0x0552, (wchar_t)0x0553,
298 (wchar_t)0x0554, (wchar_t)0x0555, (wchar_t)0x0556, (wchar_t)0x10A0, (wchar_t)0x10A1,
299 (wchar_t)0x10A2, (wchar_t)0x10A3, (wchar_t)0x10A4, (wchar_t)0x10A5, (wchar_t)0x10A6,
300 (wchar_t)0x10A7, (wchar_t)0x10A8, (wchar_t)0x10A9, (wchar_t)0x10AA, (wchar_t)0x10AB,
301 (wchar_t)0x10AC, (wchar_t)0x10AD, (wchar_t)0x10AE, (wchar_t)0x10AF, (wchar_t)0x10B0,
302 (wchar_t)0x10B1, (wchar_t)0x10B2, (wchar_t)0x10B3, (wchar_t)0x10B4, (wchar_t)0x10B5,
303 (wchar_t)0x10B6, (wchar_t)0x10B7, (wchar_t)0x10B8, (wchar_t)0x10B9, (wchar_t)0x10BA,
304 (wchar_t)0x10BB, (wchar_t)0x10BC, (wchar_t)0x10BD, (wchar_t)0x10BE, (wchar_t)0x10BF,
305 (wchar_t)0x10C0, (wchar_t)0x10C1, (wchar_t)0x10C2, (wchar_t)0x10C3, (wchar_t)0x10C4,
306 (wchar_t)0x10C5, (wchar_t)0x1E00, (wchar_t)0x1E02, (wchar_t)0x1E04, (wchar_t)0x1E06,
307 (wchar_t)0x1E08, (wchar_t)0x1E0A, (wchar_t)0x1E0C, (wchar_t)0x1E0E, (wchar_t)0x1E10,
308 (wchar_t)0x1E12, (wchar_t)0x1E14, (wchar_t)0x1E16, (wchar_t)0x1E18, (wchar_t)0x1E1A,
309 (wchar_t)0x1E1C, (wchar_t)0x1E1E, (wchar_t)0x1E20, (wchar_t)0x1E22, (wchar_t)0x1E24,
310 (wchar_t)0x1E26, (wchar_t)0x1E28, (wchar_t)0x1E2A, (wchar_t)0x1E2C, (wchar_t)0x1E2E,
311 (wchar_t)0x1E30, (wchar_t)0x1E32, (wchar_t)0x1E34, (wchar_t)0x1E36, (wchar_t)0x1E38,
312 (wchar_t)0x1E3A, (wchar_t)0x1E3C, (wchar_t)0x1E3E, (wchar_t)0x1E40, (wchar_t)0x1E42,
313 (wchar_t)0x1E44, (wchar_t)0x1E46, (wchar_t)0x1E48, (wchar_t)0x1E4A, (wchar_t)0x1E4C,
314 (wchar_t)0x1E4E, (wchar_t)0x1E50, (wchar_t)0x1E52, (wchar_t)0x1E54, (wchar_t)0x1E56,
315 (wchar_t)0x1E58, (wchar_t)0x1E5A, (wchar_t)0x1E5C, (wchar_t)0x1E5E, (wchar_t)0x1E60,
316 (wchar_t)0x1E62, (wchar_t)0x1E64, (wchar_t)0x1E66, (wchar_t)0x1E68, (wchar_t)0x1E6A,
317 (wchar_t)0x1E6C, (wchar_t)0x1E6E, (wchar_t)0x1E70, (wchar_t)0x1E72, (wchar_t)0x1E74,
318 (wchar_t)0x1E76, (wchar_t)0x1E78, (wchar_t)0x1E7A, (wchar_t)0x1E7C, (wchar_t)0x1E7E,
319 (wchar_t)0x1E80, (wchar_t)0x1E82, (wchar_t)0x1E84, (wchar_t)0x1E86, (wchar_t)0x1E88,
320 (wchar_t)0x1E8A, (wchar_t)0x1E8C, (wchar_t)0x1E8E, (wchar_t)0x1E90, (wchar_t)0x1E92,
321 (wchar_t)0x1E94, (wchar_t)0x1EA0, (wchar_t)0x1EA2, (wchar_t)0x1EA4, (wchar_t)0x1EA6,
322 (wchar_t)0x1EA8, (wchar_t)0x1EAA, (wchar_t)0x1EAC, (wchar_t)0x1EAE, (wchar_t)0x1EB0,
323 (wchar_t)0x1EB2, (wchar_t)0x1EB4, (wchar_t)0x1EB6, (wchar_t)0x1EB8, (wchar_t)0x1EBA,
324 (wchar_t)0x1EBC, (wchar_t)0x1EBE, (wchar_t)0x1EC0, (wchar_t)0x1EC2, (wchar_t)0x1EC4,
325 (wchar_t)0x1EC6, (wchar_t)0x1EC8, (wchar_t)0x1ECA, (wchar_t)0x1ECC, (wchar_t)0x1ECE,
326 (wchar_t)0x1ED0, (wchar_t)0x1ED2, (wchar_t)0x1ED4, (wchar_t)0x1ED6, (wchar_t)0x1ED8,
327 (wchar_t)0x1EDA, (wchar_t)0x1EDC, (wchar_t)0x1EDE, (wchar_t)0x1EE0, (wchar_t)0x1EE2,
328 (wchar_t)0x1EE4, (wchar_t)0x1EE6, (wchar_t)0x1EE8, (wchar_t)0x1EEA, (wchar_t)0x1EEC,
329 (wchar_t)0x1EEE, (wchar_t)0x1EF0, (wchar_t)0x1EF2, (wchar_t)0x1EF4, (wchar_t)0x1EF6,
330 (wchar_t)0x1EF8, (wchar_t)0x1F08, (wchar_t)0x1F09, (wchar_t)0x1F0A, (wchar_t)0x1F0B,
331 (wchar_t)0x1F0C, (wchar_t)0x1F0D, (wchar_t)0x1F0E, (wchar_t)0x1F0F, (wchar_t)0x1F18,
332 (wchar_t)0x1F19, (wchar_t)0x1F1A, (wchar_t)0x1F1B, (wchar_t)0x1F1C, (wchar_t)0x1F1D,
333 (wchar_t)0x1F28, (wchar_t)0x1F29, (wchar_t)0x1F2A, (wchar_t)0x1F2B, (wchar_t)0x1F2C,
334 (wchar_t)0x1F2D, (wchar_t)0x1F2E, (wchar_t)0x1F2F, (wchar_t)0x1F38, (wchar_t)0x1F39,
335 (wchar_t)0x1F3A, (wchar_t)0x1F3B, (wchar_t)0x1F3C, (wchar_t)0x1F3D, (wchar_t)0x1F3E,
336 (wchar_t)0x1F3F, (wchar_t)0x1F48, (wchar_t)0x1F49, (wchar_t)0x1F4A, (wchar_t)0x1F4B,
337 (wchar_t)0x1F4C, (wchar_t)0x1F4D, (wchar_t)0x1F59, (wchar_t)0x1F5B, (wchar_t)0x1F5D,
338 (wchar_t)0x1F5F, (wchar_t)0x1F68, (wchar_t)0x1F69, (wchar_t)0x1F6A, (wchar_t)0x1F6B,
339 (wchar_t)0x1F6C, (wchar_t)0x1F6D, (wchar_t)0x1F6E, (wchar_t)0x1F6F, (wchar_t)0x1F88,
340 (wchar_t)0x1F89, (wchar_t)0x1F8A, (wchar_t)0x1F8B, (wchar_t)0x1F8C, (wchar_t)0x1F8D,
341 (wchar_t)0x1F8E, (wchar_t)0x1F8F, (wchar_t)0x1F98, (wchar_t)0x1F99, (wchar_t)0x1F9A,
342 (wchar_t)0x1F9B, (wchar_t)0x1F9C, (wchar_t)0x1F9D, (wchar_t)0x1F9E, (wchar_t)0x1F9F,
343 (wchar_t)0x1FA8, (wchar_t)0x1FA9, (wchar_t)0x1FAA, (wchar_t)0x1FAB, (wchar_t)0x1FAC,
344 (wchar_t)0x1FAD, (wchar_t)0x1FAE, (wchar_t)0x1FAF, (wchar_t)0x1FB8, (wchar_t)0x1FB9,
345 (wchar_t)0x1FD8, (wchar_t)0x1FD9, (wchar_t)0x1FE8, (wchar_t)0x1FE9, (wchar_t)0x24B6,
346 (wchar_t)0x24B7, (wchar_t)0x24B8, (wchar_t)0x24B9, (wchar_t)0x24BA, (wchar_t)0x24BB,
347 (wchar_t)0x24BC, (wchar_t)0x24BD, (wchar_t)0x24BE, (wchar_t)0x24BF, (wchar_t)0x24C0,
348 (wchar_t)0x24C1, (wchar_t)0x24C2, (wchar_t)0x24C3, (wchar_t)0x24C4, (wchar_t)0x24C5,
349 (wchar_t)0x24C6, (wchar_t)0x24C7, (wchar_t)0x24C8, (wchar_t)0x24C9, (wchar_t)0x24CA,
350 (wchar_t)0x24CB, (wchar_t)0x24CC, (wchar_t)0x24CD, (wchar_t)0x24CE, (wchar_t)0x24CF,
351 (wchar_t)0xFF21, (wchar_t)0xFF22, (wchar_t)0xFF23, (wchar_t)0xFF24, (wchar_t)0xFF25,
352 (wchar_t)0xFF26, (wchar_t)0xFF27, (wchar_t)0xFF28, (wchar_t)0xFF29, (wchar_t)0xFF2A,
353 (wchar_t)0xFF2B, (wchar_t)0xFF2C, (wchar_t)0xFF2D, (wchar_t)0xFF2E, (wchar_t)0xFF2F,
354 (wchar_t)0xFF30, (wchar_t)0xFF31, (wchar_t)0xFF32, (wchar_t)0xFF33, (wchar_t)0xFF34,
355 (wchar_t)0xFF35, (wchar_t)0xFF36, (wchar_t)0xFF37, (wchar_t)0xFF38, (wchar_t)0xFF39,
356 (wchar_t)0xFF3A};
357
358namespace kodi
359{
360namespace tools
361{
362
363template<typename T, std::enable_if_t<!std::is_enum<T>::value, int> = 0>
364constexpr auto&& EnumToInt(T&& arg) noexcept
365{
366 return arg;
367}
368template<typename T, std::enable_if_t<std::is_enum<T>::value, int> = 0>
369constexpr auto EnumToInt(T&& arg) noexcept
370{
371 return static_cast<int>(arg);
372}
373
374//==============================================================================
375/// @defgroup cpp_kodi_tools_StringUtils_Defs Definitions, structures and enumerators
376/// @ingroup cpp_kodi_tools_StringUtils
377/// @brief **Parts used within string util functions**\n
378/// All to string functions associated data structures.
379///
380/// It is divided into individual modules that correspond to the respective
381/// types.
382///
383///
384///
385
386//==============================================================================
387/// @defgroup cpp_kodi_tools_StringUtils_Defs_TIME_FORMAT enum TIME_FORMAT
388/// @ingroup cpp_kodi_tools_StringUtils_Defs
389/// @brief TIME_FORMAT enum/bitmask used for formatting time strings.
390///
391/// Note the use of bitmasking, e.g. TIME_FORMAT_HH_MM_SS = TIME_FORMAT_HH | TIME_FORMAT_MM | TIME_FORMAT_SS
392/// @sa kodi::tools::StringUtils::SecondsToTimeString
393///
394/// @note For InfoLabels use the equivalent value listed (bold) on the
395/// description of each enum value.
396///
397/// <b>Example:</b> 3661 seconds => h=1, hh=01, m=1, mm=01, ss=01, hours=1, mins=61, secs=3661
398///
399///@{
400enum TIME_FORMAT
401{
402 /// Usually used as the fallback value if the format value is empty
403 TIME_FORMAT_GUESS = 0,
404
405 /// <b>ss</b> - seconds only
406 TIME_FORMAT_SS = 1,
407
408 /// <b>mm</b> - minutes only (2-digit)
409 TIME_FORMAT_MM = 2,
410
411 /// <b>mm:ss</b> - minutes and seconds
412 TIME_FORMAT_MM_SS = 3,
413
414 /// <b>hh</b> - hours only (2-digit)
415 TIME_FORMAT_HH = 4,
416
417 /// <b>hh:ss</b> - hours and seconds (this is not particularly useful)
418 TIME_FORMAT_HH_SS = 5,
419
420 /// <b>hh:mm</b> - hours and minutes
421 TIME_FORMAT_HH_MM = 6,
422
423 /// <b>hh:mm:ss</b> - hours, minutes and seconds
424 TIME_FORMAT_HH_MM_SS = 7,
425
426 /// <b>xx</b> - returns AM/PM for a 12-hour clock
427 TIME_FORMAT_XX = 8,
428
429 /// <b>hh:mm xx</b> - returns hours and minutes in a 12-hour clock format (AM/PM)
430 TIME_FORMAT_HH_MM_XX = 14,
431
432 /// <b>hh:mm:ss xx</b> - returns hours (2-digit), minutes and seconds in a 12-hour clock format (AM/PM)
433 TIME_FORMAT_HH_MM_SS_XX = 15,
434
435 /// <b>h</b> - hours only (1-digit)
436 TIME_FORMAT_H = 16,
437
438 /// <b>hh:mm:ss</b> - hours, minutes and seconds
439 TIME_FORMAT_H_MM_SS = 19,
440
441 /// <b>hh:mm:ss xx</b> - returns hours (1-digit), minutes and seconds in a 12-hour clock format (AM/PM)
442 TIME_FORMAT_H_MM_SS_XX = 27,
443
444 /// <b>secs</b> - total time in seconds
445 TIME_FORMAT_SECS = 32,
446
447 /// <b>mins</b> - total time in minutes
448 TIME_FORMAT_MINS = 64,
449
450 /// <b>hours</b> - total time in hours
451 TIME_FORMAT_HOURS = 128,
452
453 /// <b>m</b> - minutes only (1-digit)
454 TIME_FORMAT_M = 256
455};
456///@}
457//------------------------------------------------------------------------------
458
459//==============================================================================
460/// @defgroup cpp_kodi_tools_StringUtils class StringUtils
461/// @ingroup cpp_kodi_tools
462/// @brief **C++ class for processing strings**\n
463/// This class brings many different functions to edit, check or search texts.
464///
465/// Is intended to reduce any code work of C++ on addons and to have them faster
466/// to use.
467///
468/// All functions are static within the <b>`kodi::tools::StringUtils`</b> class.
469///
470///@{
471class StringUtils
472{
473public:
474 //============================================================================
475 /// @ingroup cpp_kodi_tools_StringUtils_Defs
476 /// @brief Defines a static empty <b>`std::string`</b>.
477 ///
478 static const std::string Empty;
479 //----------------------------------------------------------------------------
480
481 //----------------------------------------------------------------------------
482 /// @defgroup cpp_kodi_tools_StringUtils_FormatControl String format
483 /// @ingroup cpp_kodi_tools_StringUtils
484 /// @brief **Formatting functions**\n
485 /// Used to output the given values in newly formatted text using functions.
486 ///
487 /*!@{*/
488
489 //============================================================================
490 /// @brief Returns the C++ string pointed by given format. If format includes
491 /// format specifiers (subsequences beginning with %), the additional arguments
492 /// following format are formatted and inserted in the resulting string replacing
493 /// their respective specifiers.
494 ///
495 /// After the format parameter, the function expects at least as many additional
496 /// arguments as specified by format.
497 ///
498 /// @param[in] fmt The format of the text to process for output.
499 /// C string that contains the text to be written to the stream.
500 /// It can optionally contain embedded format specifiers that are
501 /// replaced by the values specified in subsequent additional
502 /// arguments and formatted as requested.
503 /// | specifier | Output | Example
504 /// |------------|----------------------------------------------------|------------
505 /// | d or i | Signed decimal integer | 392
506 /// | u | Unsigned decimal integer | 7235
507 /// | o | Unsigned octal | 610
508 /// | x | Unsigned hexadecimal integer | 7fa
509 /// | X | Unsigned hexadecimal integer (uppercase) | 7FA
510 /// | f | Decimal floating point, lowercase | 392.65
511 /// | F | Decimal floating point, uppercase | 392.65
512 /// | e | Scientific notation (mantissa/exponent), lowercase | 3.9265e+2
513 /// | E | Scientific notation (mantissa/exponent), uppercase | 3.9265E+2
514 /// | g | Use the shortest representation: %e or %f | 392.65
515 /// | G | Use the shortest representation: %E or %F | 392.65
516 /// | a | Hexadecimal floating point, lowercase | -0xc.90fep-2
517 /// | A | Hexadecimal floating point, uppercase | -0XC.90FEP-2
518 /// | c | Character | a
519 /// | s | String of characters | sample
520 /// | p | Pointer address | b8000000
521 /// | % | A % followed by another % character will write a single % to the stream. | %
522 /// The length sub-specifier modifies the length of the data type. This is a chart
523 /// showing the types used to interpret the corresponding arguments with and without
524 /// length specifier (if a different type is used, the proper type promotion or
525 /// conversion is performed, if allowed):
526 /// | length| d i | u o x X | f F e E g G a A | c | s | p | n |
527 /// |-------|---------------|-----------------------|-----------------|-------|---------|---------|-----------------|
528 /// | (none)| int | unsigned int | double | int | char* | void* | int* |
529 /// | hh | signed char | unsigned char | | | | | signed char* |
530 /// | h | short int | unsigned short int | | | | | short int* |
531 /// | l | long int | unsigned long int | | wint_t| wchar_t*| | long int* |
532 /// | ll | long long int | unsigned long long int| | | | | long long int* |
533 /// | j | intmax_t | uintmax_t | | | | | intmax_t* |
534 /// | z | size_t | size_t | | | | | size_t* |
535 /// | t | ptrdiff_t | ptrdiff_t | | | | | ptrdiff_t* |
536 /// | L | | | long double | | | | |
537 /// <b>Note:</b> that the c specifier takes an int (or wint_t) as argument, but performs the proper conversion to a char value
538 /// (or a wchar_t) before formatting it for output.
539 /// @param[in] ... <i>(additional arguments)</i>\n
540 /// Depending on the format string, the function may expect a
541 /// sequence of additional arguments, each containing a value
542 /// to be used to replace a format specifier in the format
543 /// string (or a pointer to a storage location, for n).\n
544 /// There should be at least as many of these arguments as the
545 /// number of values specified in the format specifiers.
546 /// Additional arguments are ignored by the function.
547 /// @return Formatted string
548 ///
549 ///
550 /// --------------------------------------------------------------------------
551 /// Example:
552 /// ~~~~~~~~~~~~~{.cpp}
553 /// #include <kodi/tools/StringUtils.h>
554 ///
555 /// std::string str = kodi::tools::StringUtils::Format("Hello %s %i", "World", 2020);
556 /// ~~~~~~~~~~~~~
557 ///
558 inline static std::string Format(const char* fmt, ...)
559 {
560 va_list args;
561 va_start(args, fmt);
562 std::string str = FormatV(fmt, args);
563 va_end(args);
564
565 return str;
566 }
567 //----------------------------------------------------------------------------
568
569 //============================================================================
570 /// @brief Returns the C++ wide string pointed by given format.
571 ///
572 /// @param[in] fmt The format of the text to process for output
573 /// (see @ref Format(const char* fmt, ...) for details).
574 /// @param[in] ... <i>(additional arguments)</i>\n
575 /// Depending on the format string, the function may expect a
576 /// sequence of additional arguments, each containing a value
577 /// to be used to replace a format specifier in the format
578 /// string (or a pointer to a storage location, for n).\n
579 /// There should be at least as many of these arguments as the
580 /// number of values specified in the format specifiers.
581 /// Additional arguments are ignored by the function.
582 /// @return Formatted string
583 ///
584 inline static std::wstring Format(const wchar_t* fmt, ...)
585 {
586 va_list args;
587 va_start(args, fmt);
588 std::wstring str = FormatV(fmt, args);
589 va_end(args);
590
591 return str;
592 }
593 //----------------------------------------------------------------------------
594
595 //============================================================================
596 /// @brief Returns the C++ string pointed by given format list.
597 ///
598 /// @param[in] fmt The format of the text to process for output
599 /// (see @ref Format(const char* fmt, ...) for details).
600 /// @param[in] args A value identifying a variable arguments list initialized
601 /// with `va_start`.
602 /// @return Formatted string
603 ///
604 inline static std::string FormatV(PRINTF_FORMAT_STRING const char* fmt, va_list args)
605 {
606 if (!fmt || !fmt[0])
607 return "";
608
609 int size = FORMAT_BLOCK_SIZE;
610 va_list argCopy;
611
612 while (true)
613 {
614 char* cstr = reinterpret_cast<char*>(malloc(sizeof(char) * size));
615 if (!cstr)
616 return "";
617
618 va_copy(argCopy, args);
619 int nActual = vsnprintf(cstr, size, fmt, argCopy);
620 va_end(argCopy);
621
622 if (nActual > -1 && nActual < size) // We got a valid result
623 {
624 std::string str(cstr, nActual);
625 free(cstr);
626 return str;
627 }
628 free(cstr);
629#ifndef TARGET_WINDOWS
630 if (nActual > -1) // Exactly what we will need (glibc 2.1)
631 size = nActual + 1;
632 else // Let's try to double the size (glibc 2.0)
633 size *= 2;
634#else // TARGET_WINDOWS
635 va_copy(argCopy, args);
636 size = _vscprintf(fmt, argCopy);
637 va_end(argCopy);
638 if (size < 0)
639 return "";
640 else
641 size++; // increment for null-termination
642#endif // TARGET_WINDOWS
643 }
644
645 return ""; // unreachable
646 }
647 //----------------------------------------------------------------------------
648
649 //============================================================================
650 /// @brief Returns the C++ wide string pointed by given format list.
651 ///
652 /// @param[in] fmt The format of the text to process for output
653 /// (see @ref Format(const char* fmt, ...) for details).
654 /// @param[in] args A value identifying a variable arguments list initialized
655 /// with `va_start`.
656 /// @return Formatted string
657 ///
658 inline static std::wstring FormatV(PRINTF_FORMAT_STRING const wchar_t* fmt, va_list args)
659 {
660 if (!fmt || !fmt[0])
661 return L"";
662
663 int size = FORMAT_BLOCK_SIZE;
664 va_list argCopy;
665
666 while (true)
667 {
668 wchar_t* cstr = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t) * size));
669 if (!cstr)
670 return L"";
671
672 va_copy(argCopy, args);
673 int nActual = vswprintf(cstr, size, fmt, argCopy);
674 va_end(argCopy);
675
676 if (nActual > -1 && nActual < size) // We got a valid result
677 {
678 std::wstring str(cstr, nActual);
679 free(cstr);
680 return str;
681 }
682 free(cstr);
683
684#ifndef TARGET_WINDOWS
685 if (nActual > -1) // Exactly what we will need (glibc 2.1)
686 size = nActual + 1;
687 else // Let's try to double the size (glibc 2.0)
688 size *= 2;
689#else // TARGET_WINDOWS
690 va_copy(argCopy, args);
691 size = _vscwprintf(fmt, argCopy);
692 va_end(argCopy);
693 if (size < 0)
694 return L"";
695 else
696 size++; // increment for null-termination
697#endif // TARGET_WINDOWS
698 }
699
700 return L"";
701 }
702 //----------------------------------------------------------------------------
703
704 //============================================================================
705 /// @brief Returns bytes in a human readable format using the smallest unit
706 /// that will fit `bytes` in at most three digits. The number of decimals are
707 /// adjusted with significance such that 'small' numbers will have more
708 /// decimals than larger ones.
709 ///
710 /// For example: 1024 bytes will be formatted as "1.00kB", 10240 bytes as
711 /// "10.0kB" and 102400 bytes as "100kB". See TestStringUtils for more
712 /// examples.
713 ///
714 /// Supported file sizes:
715 /// | Value | Short | Metric
716 /// |------------|-------|-----------
717 /// | 1 | B | byte
718 /// | 1024¹ | kB | kilobyte
719 /// | 1024² | MB | megabyte
720 /// | 1024³ | GB | gigabyte
721 /// | 1024 exp 4 | TB | terabyte
722 /// | 1024 exp 5 | PB | petabyte
723 /// | 1024 exp 6 | EB | exabyte
724 /// | 1024 exp 7 | ZB | zettabyte
725 /// | 1024 exp 8 | YB | yottabyte
726 ///
727 /// @param[in] bytes Bytes amount to return as human readable string
728 /// @return Size as string
729 ///
730 ///
731 /// --------------------------------------------------------------------------
732 /// Example:
733 /// ~~~~~~~~~~~~~{.cpp}
734 /// #include <kodi/tools/StringUtils.h>
735 ///
736 /// EXPECT_STREQ("0B", kodi::tools::StringUtils::FormatFileSize(0).c_str());
737 ///
738 /// EXPECT_STREQ("999B", kodi::tools::StringUtils::FormatFileSize(999).c_str());
739 /// EXPECT_STREQ("0.98kB", kodi::tools::StringUtils::FormatFileSize(1000).c_str());
740 ///
741 /// EXPECT_STREQ("1.00kB", kodi::tools::StringUtils::FormatFileSize(1024).c_str());
742 /// EXPECT_STREQ("9.99kB", kodi::tools::StringUtils::FormatFileSize(10229).c_str());
743 ///
744 /// EXPECT_STREQ("10.1kB", kodi::tools::StringUtils::FormatFileSize(10387).c_str());
745 /// EXPECT_STREQ("99.9kB", kodi::tools::StringUtils::FormatFileSize(102297).c_str());
746 ///
747 /// EXPECT_STREQ("100kB", kodi::tools::StringUtils::FormatFileSize(102400).c_str());
748 /// EXPECT_STREQ("999kB", kodi::tools::StringUtils::FormatFileSize(1023431).c_str());
749 ///
750 /// EXPECT_STREQ("0.98MB", kodi::tools::StringUtils::FormatFileSize(1023897).c_str());
751 /// EXPECT_STREQ("0.98MB", kodi::tools::StringUtils::FormatFileSize(1024000).c_str());
752 ///
753 /// EXPECT_STREQ("5.30EB", kodi::tools::StringUtils::FormatFileSize(6115888293969133568).c_str());
754 /// ~~~~~~~~~~~~~
755 ///
756 inline static std::string FormatFileSize(uint64_t bytes)
757 {
758 const std::array<std::string, 9> units{{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}};
759 if (bytes < 1000)
760 return Format("%" PRIu64 "B", bytes);
761
762 size_t i = 0;
763 double value = static_cast<double>(bytes);
764 while (i + 1 < units.size() && value >= 999.5)
765 {
766 ++i;
767 value /= 1024.0;
768 }
769 unsigned int decimals = value < 9.995 ? 2 : (value < 99.95 ? 1 : 0);
770 auto frmt = "%." + Format("%u", decimals) + "f%s";
771 return Format(frmt.c_str(), value, units[i].c_str());
772 }
773 //----------------------------------------------------------------------------
774
775 //============================================================================
776 /// @brief Convert the string of binary chars to the actual string.
777 ///
778 /// Convert the string representation of binary chars to the actual string.
779 /// For example <b>`\1\2\3`</b> is converted to a string with binary char
780 /// <b>`\1`</b>, <b>`\2`</b> and <b>`\3`</b>
781 ///
782 /// @param[in] in String to convert
783 /// @return Converted string
784 ///
785 inline static std::string BinaryStringToString(const std::string& in)
786 {
787 std::string out;
788 out.reserve(in.size() / 2);
789 for (const char *cur = in.c_str(), *end = cur + in.size(); cur != end; ++cur)
790 {
791 if (*cur == '\\')
792 {
793 ++cur;
794 if (cur == end)
795 {
796 break;
797 }
798 if (isdigit(*cur))
799 {
800 char* end;
801 unsigned long num = strtol(cur, &end, 10);
802 cur = end - 1;
803 out.push_back(static_cast<char>(num));
804 continue;
805 }
806 }
807 out.push_back(*cur);
808 }
809 return out;
810 }
811 //----------------------------------------------------------------------------
812
813 //============================================================================
814 /// @brief Convert each character in the string to its hexadecimal
815 /// representation and return the concatenated result
816 ///
817 /// Example: "abc\n" -> "6162630a"
818 ///
819 /// @param[in] in String to convert
820 /// @return Converted string
821 ///
822 ///
823 /// --------------------------------------------------------------------------
824 /// Example:
825 /// ~~~~~~~~~~~~~{.cpp}
826 /// #include <kodi/tools/StringUtils.h>
827 ///
828 /// EXPECT_STREQ("", kodi::tools::StringUtils::ToHexadecimal("").c_str());
829 /// EXPECT_STREQ("616263", kodi::tools::StringUtils::ToHexadecimal("abc").c_str());
830 /// std::string a{"a\0b\n", 4};
831 /// EXPECT_STREQ("6100620a", kodi::tools::StringUtils::ToHexadecimal(a).c_str());
832 /// std::string nul{"\0", 1};
833 /// EXPECT_STREQ("00", kodi::tools::StringUtils::ToHexadecimal(nul).c_str());
834 /// std::string ff{"\xFF", 1};
835 /// EXPECT_STREQ("ff", kodi::tools::StringUtils::ToHexadecimal(ff).c_str());
836 /// ~~~~~~~~~~~~~
837 ///
838 inline static std::string ToHexadecimal(const std::string& in)
839 {
840 std::ostringstream ss;
841 ss << std::hex;
842 for (unsigned char ch : in)
843 {
844 ss << std::setw(2) << std::setfill('0') << static_cast<unsigned long>(ch);
845 }
846 return ss.str();
847 }
848 //----------------------------------------------------------------------------
849
850 /*!@}*/
851
852 //----------------------------------------------------------------------------
853 /// @defgroup cpp_kodi_tools_StringUtils_EditControl String edit
854 /// @ingroup cpp_kodi_tools_StringUtils
855 /// @brief **Edits given texts**\n
856 /// This is used to revise the respective strings and to get them in the desired format.
857 ///
858 /*!@{*/
859
860 //============================================================================
861 /// @brief Convert a string to uppercase.
862 ///
863 /// @param[in,out] str String to convert
864 ///
865 ///
866 /// --------------------------------------------------------------------------
867 /// Example:
868 /// ~~~~~~~~~~~~~{.cpp}
869 /// #include <kodi/tools/StringUtils.h>
870 ///
871 /// std::string refstr = "TEST";
872 ///
873 /// std::string varstr = "TeSt";
874 /// kodi::tools::StringUtils::ToUpper(varstr);
875 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
876 /// ~~~~~~~~~~~~~
877 ///
878 inline static void ToUpper(std::string& str)
879 {
880 std::transform(str.begin(), str.end(), str.begin(), ::toupper);
881 }
882 //----------------------------------------------------------------------------
883
884 //============================================================================
885 /// @brief Convert a 16bit wide string to uppercase.
886 ///
887 /// @param[in,out] str String to convert
888 ///
889 inline static void ToUpper(std::wstring& str)
890 {
891 transform(str.begin(), str.end(), str.begin(), toupperUnicode);
892 }
893 //----------------------------------------------------------------------------
894
895 //============================================================================
896 /// @brief Convert a string to lowercase.
897 ///
898 /// @param[in,out] str String to convert
899 ///
900 ///
901 /// --------------------------------------------------------------------------
902 /// Example:
903 /// ~~~~~~~~~~~~~{.cpp}
904 /// #include <kodi/tools/StringUtils.h>
905 ///
906 /// std::string refstr = "test";
907 ///
908 /// std::string varstr = "TeSt";
909 /// kodi::tools::StringUtils::ToLower(varstr);
910 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
911 /// ~~~~~~~~~~~~~
912 ///
913 inline static void ToLower(std::string& str)
914 {
915 transform(str.begin(), str.end(), str.begin(), ::tolower);
916 }
917 //----------------------------------------------------------------------------
918
919 //============================================================================
920 /// @brief Convert a 16bit wide string to lowercase.
921 ///
922 /// @param[in,out] str String to convert
923 ///
924 inline static void ToLower(std::wstring& str)
925 {
926 transform(str.begin(), str.end(), str.begin(), tolowerUnicode);
927 }
928 //----------------------------------------------------------------------------
929
930 //============================================================================
931 /// @brief Combine all numerical digits and give it as integer value.
932 ///
933 /// @param[in,out] str String to check for digits
934 /// @return All numerical digits fit together as integer value
935 ///
936 inline static int ReturnDigits(const std::string& str)
937 {
938 std::stringstream ss;
939 for (const auto& character : str)
940 {
941 if (isdigit(character))
942 ss << character;
943 }
944 return atoi(ss.str().c_str());
945 }
946 //----------------------------------------------------------------------------
947
948 //============================================================================
949 /// @brief Returns a string from start with givent count.
950 ///
951 /// @param[in] str String to use
952 /// @param[in] count Amount of characters to go from left
953 /// @return The left part string in amount of given count or complete if it
954 /// was higher.
955 ///
956 ///
957 /// --------------------------------------------------------------------------
958 /// Example:
959 /// ~~~~~~~~~~~~~{.cpp}
960 /// #include <kodi/tools/StringUtils.h>
961 ///
962 /// std::string refstr, varstr;
963 /// std::string origstr = "test";
964 ///
965 /// refstr = "";
966 /// varstr = kodi::tools::StringUtils::Left(origstr, 0);
967 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
968 ///
969 /// refstr = "te";
970 /// varstr = kodi::tools::StringUtils::Left(origstr, 2);
971 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
972 ///
973 /// refstr = "test";
974 /// varstr = kodi::tools::StringUtils::Left(origstr, 10);
975 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
976 /// ~~~~~~~~~~~~~
977 ///
978 inline static std::string Left(const std::string& str, size_t count)
979 {
980 count = std::max((size_t)0, std::min(count, str.size()));
981 return str.substr(0, count);
982 }
983 //----------------------------------------------------------------------------
984
985 //============================================================================
986 /// @brief Get substring from mid of given string.
987 ///
988 /// @param[in] str String to get substring from
989 /// @param[in] first Position from where to start
990 /// @param[in] count [opt] length of position to get after start, default is
991 /// complete to end
992 /// @return The substring taken from middle of input string
993 ///
994 ///
995 /// --------------------------------------------------------------------------
996 /// Example:
997 /// ~~~~~~~~~~~~~{.cpp}
998 /// #include <kodi/tools/StringUtils.h>
999 ///
1000 /// std::string refstr, varstr;
1001 /// std::string origstr = "test";
1002 ///
1003 /// refstr = "";
1004 /// varstr = kodi::tools::StringUtils::Mid(origstr, 0, 0);
1005 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1006 ///
1007 /// refstr = "te";
1008 /// varstr = kodi::tools::StringUtils::Mid(origstr, 0, 2);
1009 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1010 ///
1011 /// refstr = "test";
1012 /// varstr = kodi::tools::StringUtils::Mid(origstr, 0, 10);
1013 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1014 ///
1015 /// refstr = "st";
1016 /// varstr = kodi::tools::StringUtils::Mid(origstr, 2);
1017 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1018 ///
1019 /// refstr = "st";
1020 /// varstr = kodi::tools::StringUtils::Mid(origstr, 2, 2);
1021 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1022 ///
1023 /// refstr = "es";
1024 /// varstr = kodi::tools::StringUtils::Mid(origstr, 1, 2);
1025 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1026 /// ~~~~~~~~~~~~~
1027 ///
1028 inline static std::string Mid(const std::string& str,
1029 size_t first,
1030 size_t count = std::string::npos)
1031 {
1032 if (first + count > str.size())
1033 count = str.size() - first;
1034
1035 if (first > str.size())
1036 return std::string();
1037
1038 assert(first + count <= str.size());
1039
1040 return str.substr(first, count);
1041 }
1042 //----------------------------------------------------------------------------
1043
1044 //============================================================================
1045 /// @brief Returns a string from end with givent count.
1046 ///
1047 /// @param[in] str String to use
1048 /// @param[in] count Amount of characters to go from right
1049 /// @return The right part string in amount of given count or complete if it
1050 /// was higher.
1051 ///
1052 ///
1053 /// --------------------------------------------------------------------------
1054 /// Example:
1055 /// ~~~~~~~~~~~~~{.cpp}
1056 /// #include <kodi/tools/StringUtils.h>
1057 ///
1058 /// std::string refstr, varstr;
1059 /// std::string origstr = "test";
1060 ///
1061 /// refstr = "";
1062 /// varstr = kodi::tools::StringUtils::Right(origstr, 0);
1063 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1064 ///
1065 /// refstr = "st";
1066 /// varstr = kodi::tools::StringUtils::Right(origstr, 2);
1067 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1068 ///
1069 /// refstr = "test";
1070 /// varstr = kodi::tools::StringUtils::Right(origstr, 10);
1071 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1072 /// ~~~~~~~~~~~~~
1073 ///
1074 inline static std::string Right(const std::string& str, size_t count)
1075 {
1076 count = std::max((size_t)0, std::min(count, str.size()));
1077 return str.substr(str.size() - count);
1078 }
1079 //----------------------------------------------------------------------------
1080
1081 //============================================================================
1082 /// @brief Trim a string with remove of not wanted spaces at begin and end
1083 /// of string.
1084 ///
1085 /// @param[in,out] str String to trim, becomes also changed and given on
1086 /// return
1087 /// @return The changed string
1088 ///
1089 ///
1090 /// --------------------------------------------------------------------------
1091 /// Example:
1092 /// ~~~~~~~~~~~~~{.cpp}
1093 /// #include <kodi/tools/StringUtils.h>
1094 ///
1095 /// std::string refstr = "test test";
1096 ///
1097 /// std::string varstr = " test test ";
1098 /// kodi::tools::StringUtils::Trim(varstr);
1099 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1100 /// ~~~~~~~~~~~~~
1101 ///
1102 inline static std::string& Trim(std::string& str)
1103 {
1104 TrimLeft(str);
1105 return TrimRight(str);
1106 }
1107 //----------------------------------------------------------------------------
1108
1109 //============================================================================
1110 /// @brief Trim a string with remove of not wanted characters at begin and end
1111 /// of string.
1112 ///
1113 /// @param[in,out] str String to trim, becomes also changed and given on
1114 /// return
1115 /// @param[in] chars Characters to use for trim
1116 /// @return The changed string
1117 ///
1118 inline static std::string& Trim(std::string& str, const char* const chars)
1119 {
1120 TrimLeft(str, chars);
1121 return TrimRight(str, chars);
1122 }
1123 //----------------------------------------------------------------------------
1124
1125 //============================================================================
1126 /// @brief Trim a string with remove of not wanted spaces at begin of string.
1127 ///
1128 /// @param[in,out] str String to trim, becomes also changed and given on
1129 /// return
1130 /// @return The changed string
1131 ///
1132 ///
1133 /// --------------------------------------------------------------------------
1134 /// Example:
1135 /// ~~~~~~~~~~~~~{.cpp}
1136 /// #include <kodi/tools/StringUtils.h>
1137 ///
1138 /// std::string refstr = "test test ";
1139 ///
1140 /// std::string varstr = " test test ";
1141 /// kodi::tools::StringUtils::TrimLeft(varstr);
1142 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1143 /// ~~~~~~~~~~~~~
1144 ///
1145 inline static std::string& TrimLeft(std::string& str)
1146 {
1147 str.erase(str.begin(),
1148 std::find_if(str.begin(), str.end(), [](char s) { return IsSpace(s) == 0; }));
1149 return str;
1150 }
1151 //----------------------------------------------------------------------------
1152
1153 //============================================================================
1154 /// @brief Trim a string with remove of not wanted characters at begin of
1155 /// string.
1156 ///
1157 /// @param[in,out] str String to trim, becomes also changed and given on
1158 /// return
1159 /// @param[in] chars Characters to use for trim
1160 /// @return The changed string
1161 ///
1162 inline static std::string& TrimLeft(std::string& str, const char* const chars)
1163 {
1164 size_t nidx = str.find_first_not_of(chars);
1165 str.erase(0, nidx);
1166 return str;
1167 }
1168 //----------------------------------------------------------------------------
1169
1170 //============================================================================
1171 /// @brief Trim a string with remove of not wanted spaces at end of string.
1172 ///
1173 /// @param[in,out] str String to trim, becomes also changed and given on
1174 /// return
1175 /// @return The changed string
1176 ///
1177 ///
1178 /// --------------------------------------------------------------------------
1179 /// Example:
1180 /// ~~~~~~~~~~~~~{.cpp}
1181 /// #include <kodi/tools/StringUtils.h>
1182 ///
1183 /// std::string refstr = " test test";
1184 ///
1185 /// std::string varstr = " test test ";
1186 /// kodi::tools::StringUtils::TrimRight(varstr);
1187 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1188 /// ~~~~~~~~~~~~~
1189 ///
1190 inline static std::string& TrimRight(std::string& str)
1191 {
1192 str.erase(std::find_if(str.rbegin(), str.rend(), [](char s) { return IsSpace(s) == 0; }).base(),
1193 str.end());
1194 return str;
1195 }
1196 //----------------------------------------------------------------------------
1197
1198 //============================================================================
1199 /// @brief Trim a string with remove of not wanted characters at end of
1200 /// string.
1201 ///
1202 /// @param[in,out] str String to trim, becomes also changed and given on
1203 /// return
1204 /// @param[in] chars Characters to use for trim
1205 /// @return The changed string
1206 ///
1207 inline static std::string& TrimRight(std::string& str, const char* const chars)
1208 {
1209 size_t nidx = str.find_last_not_of(chars);
1210 str.erase(str.npos == nidx ? 0 : ++nidx);
1211 return str;
1212 }
1213 //----------------------------------------------------------------------------
1214
1215 //============================================================================
1216 /// @brief Cleanup string by remove of duplicates of spaces and tabs.
1217 ///
1218 /// @param[in,out] str String to remove duplicates, becomes also changed and
1219 /// given further on return
1220 /// @return The changed string
1221 ///
1222 inline static std::string& RemoveDuplicatedSpacesAndTabs(std::string& str)
1223 {
1224 std::string::iterator it = str.begin();
1225 bool onSpace = false;
1226 while (it != str.end())
1227 {
1228 if (*it == '\t')
1229 *it = ' ';
1230
1231 if (*it == ' ')
1232 {
1233 if (onSpace)
1234 {
1235 it = str.erase(it);
1236 continue;
1237 }
1238 else
1239 onSpace = true;
1240 }
1241 else
1242 onSpace = false;
1243
1244 ++it;
1245 }
1246 return str;
1247 }
1248 //----------------------------------------------------------------------------
1249
1250 //============================================================================
1251 /// @brief Replace a character with another inside text string.
1252 ///
1253 /// @param[in] str String to replace within
1254 /// @param[in] oldChar Character to search for replacement
1255 /// @param[in] newChar New character to use for replacement
1256 /// @return Amount of replaced characters
1257 ///
1258 ///
1259 /// --------------------------------------------------------------------------
1260 /// Example:
1261 /// ~~~~~~~~~~~~~{.cpp}
1262 /// #include <kodi/tools/StringUtils.h>
1263 ///
1264 /// std::string refstr = "text text";
1265 ///
1266 /// std::string varstr = "test test";
1267 /// EXPECT_EQ(kodi::tools::StringUtils::Replace(varstr, 's', 'x'), 2);
1268 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1269 ///
1270 /// EXPECT_EQ(kodi::tools::StringUtils::Replace(varstr, 's', 'x'), 0);
1271 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1272 /// ~~~~~~~~~~~~~
1273 ///
1274 inline static int Replace(std::string& str, char oldChar, char newChar)
1275 {
1276 int replacedChars = 0;
1277 for (std::string::iterator it = str.begin(); it != str.end(); ++it)
1278 {
1279 if (*it == oldChar)
1280 {
1281 *it = newChar;
1282 replacedChars++;
1283 }
1284 }
1285
1286 return replacedChars;
1287 }
1288 //----------------------------------------------------------------------------
1289
1290 //============================================================================
1291 /// @brief Replace a complete text with another inside text string.
1292 ///
1293 /// @param[in] str String to replace within
1294 /// @param[in] oldStr String to search for replacement
1295 /// @param[in] newStr New string to use for replacement
1296 /// @return Amount of replaced text fields
1297 ///
1298 ///
1299 /// --------------------------------------------------------------------------
1300 /// Example:
1301 /// ~~~~~~~~~~~~~{.cpp}
1302 /// #include <kodi/tools/StringUtils.h>
1303 ///
1304 /// std::string refstr = "text text";
1305 ///
1306 /// std::string varstr = "test test";
1307 /// EXPECT_EQ(kodi::tools::StringUtils::Replace(varstr, "s", "x"), 2);
1308 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1309 ///
1310 /// EXPECT_EQ(kodi::tools::StringUtils::Replace(varstr, "s", "x"), 0);
1311 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1312 /// ~~~~~~~~~~~~~
1313 ///
1314 inline static int Replace(std::string& str, const std::string& oldStr, const std::string& newStr)
1315 {
1316 if (oldStr.empty())
1317 return 0;
1318
1319 int replacedChars = 0;
1320 size_t index = 0;
1321
1322 while (index < str.size() && (index = str.find(oldStr, index)) != std::string::npos)
1323 {
1324 str.replace(index, oldStr.size(), newStr);
1325 index += newStr.size();
1326 replacedChars++;
1327 }
1328
1329 return replacedChars;
1330 }
1331 //----------------------------------------------------------------------------
1332
1333 //============================================================================
1334 /// @brief Replace a complete text with another inside 16bit wide text string.
1335 ///
1336 /// @param[in] str String to replace within
1337 /// @param[in] oldStr String to search for replacement
1338 /// @param[in] newStr New string to use for replacement
1339 /// @return Amount of replaced text fields
1340 ///
1341 inline static int Replace(std::wstring& str,
1342 const std::wstring& oldStr,
1343 const std::wstring& newStr)
1344 {
1345 if (oldStr.empty())
1346 return 0;
1347
1348 int replacedChars = 0;
1349 size_t index = 0;
1350
1351 while (index < str.size() && (index = str.find(oldStr, index)) != std::string::npos)
1352 {
1353 str.replace(index, oldStr.size(), newStr);
1354 index += newStr.size();
1355 replacedChars++;
1356 }
1357
1358 return replacedChars;
1359 }
1360 //----------------------------------------------------------------------------
1361
1362 //============================================================================
1363 /// @brief Transform characters to create a safe URL.
1364 ///
1365 /// @param[in] str The string to transform
1366 /// @return The transformed string, with unsafe characters replaced by "_"
1367 ///
1368 /// Safe URLs are composed of the unreserved characters defined in
1369 /// RFC 3986 section 2.3:
1370 ///
1371 /// ALPHA / DIGIT / "-" / "." / "_" / "~"
1372 ///
1373 /// Characters outside of this set will be replaced by "_".
1374 ///
1375 inline static std::string MakeSafeUrl(const std::string& str)
1376 {
1377 std::string safeUrl;
1378
1379 safeUrl.reserve(str.size());
1380
1381 std::transform(str.begin(), str.end(), std::back_inserter(safeUrl), [](char c) {
1382 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '-' ||
1383 c == '.' || c == '_' || c == '~')
1384 {
1385 return c;
1386 }
1387 return '_';
1388 });
1389
1390 return safeUrl;
1391 }
1392 //----------------------------------------------------------------------------
1393
1394 //============================================================================
1395 /// @brief Transform characters to create a safe, printable string.
1396 ///
1397 /// @param[in] str The string to transform
1398 /// @return The transformed string, with unsafe characters replaced by " "
1399 ///
1400 /// Unsafe characters are defined as the non-printable ASCII characters
1401 /// (character code 0-31).
1402 ///
1403 inline static std::string MakeSafeString(const std::string& str)
1404 {
1405 std::string safeString;
1406
1407 safeString.reserve(str.size());
1408
1409 std::transform(str.begin(), str.end(), std::back_inserter(safeString), [](char c) {
1410 if (c < 0x20)
1411 return ' ';
1412
1413 return c;
1414 });
1415
1416 return safeString;
1417 }
1418 //----------------------------------------------------------------------------
1419
1420 //============================================================================
1421 /// @brief Removes a MAC address from a given string.
1422 ///
1423 /// @param[in] str The string containing a MAC address
1424 /// @return The string without the MAC address (for chaining)
1425 ///
1426 inline static std::string RemoveMACAddress(const std::string& str)
1427 {
1428 std::regex re(R"mac([\(\[]?([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})[\)\]]?)mac");
1429 return std::regex_replace(str, re, "", std::regex_constants::format_default);
1430 }
1431 //----------------------------------------------------------------------------
1432
1433 //============================================================================
1434 /// @brief Remove carriage return and line feeds on string ends.
1435 ///
1436 /// @param[in,out] str String where CR and LF becomes removed on end
1437 ///
1438 ///
1439 /// --------------------------------------------------------------------------
1440 /// Example:
1441 /// ~~~~~~~~~~~~~{.cpp}
1442 /// #include <kodi/tools/StringUtils.h>
1443 ///
1444 /// std::string refstr, varstr;
1445 ///
1446 /// refstr = "test\r\nstring\nblah blah";
1447 /// varstr = "test\r\nstring\nblah blah\n";
1448 /// kodi::tools::StringUtils::RemoveCRLF(varstr);
1449 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
1450 /// ~~~~~~~~~~~~~
1451 ///
1452 inline static void RemoveCRLF(std::string& strLine) { StringUtils::TrimRight(strLine, "\n\r"); }
1453 //----------------------------------------------------------------------------
1454
1455 //============================================================================
1456 /// @brief Convert a word to a digit numerical string
1457 ///
1458 /// @param[in] str String to convert
1459 ///
1460 ///
1461 /// --------------------------------------------------------------------------
1462 /// Example:
1463 /// ~~~~~~~~~~~~~{.cpp}
1464 /// std::string ref, var;
1465 ///
1466 /// ref = "8378 787464";
1467 /// var = "test string";
1468 /// kodi::tools::StringUtils::WordToDigits(var);
1469 /// EXPECT_STREQ(ref.c_str(), var.c_str());
1470 /// ~~~~~~~~~~~~~
1471 ///
1472 inline static void WordToDigits(std::string& word)
1473 {
1474 static const char word_to_letter[] = "22233344455566677778889999";
1475 StringUtils::ToLower(word);
1476 for (unsigned int i = 0; i < word.size(); ++i)
1477 { // NB: This assumes ascii, which probably needs extending at some point.
1478 char letter = word[i];
1479 if ((letter >= 'a' && letter <= 'z')) // assume contiguous letter range
1480 {
1481 word[i] = word_to_letter[letter - 'a'];
1482 }
1483 else if (letter < '0' || letter > '9') // We want to keep 0-9!
1484 {
1485 word[i] = ' '; // replace everything else with a space
1486 }
1487 }
1488 }
1489 //----------------------------------------------------------------------------
1490
1491 //============================================================================
1492 /// @brief Escapes the given string to be able to be used as a parameter.
1493 ///
1494 /// Escapes backslashes and double-quotes with an additional backslash and
1495 /// adds double-quotes around the whole string.
1496 ///
1497 /// @param[in] param String to escape/paramify
1498 /// @return Escaped/Paramified string
1499 ///
1500 ///
1501 /// --------------------------------------------------------------------------
1502 /// Example:
1503 /// ~~~~~~~~~~~~~{.cpp}
1504 /// const char *input = "some, very \\ odd \"string\"";
1505 /// const char *ref = "\"some, very \\\\ odd \\\"string\\\"\"";
1506 ///
1507 /// std::string result = kodi::tools::StringUtils::Paramify(input);
1508 /// EXPECT_STREQ(ref, result.c_str());
1509 /// ~~~~~~~~~~~~~
1510 ///
1511 inline static std::string Paramify(const std::string& param)
1512 {
1513 std::string result = param;
1514 // escape backspaces
1515 StringUtils::Replace(result, "\\", "\\\\");
1516 // escape double quotes
1517 StringUtils::Replace(result, "\"", "\\\"");
1518
1519 // add double quotes around the whole string
1520 return "\"" + result + "\"";
1521 }
1522 //----------------------------------------------------------------------------
1523
1524 /*!@}*/
1525
1526 //----------------------------------------------------------------------------
1527 /// @defgroup cpp_kodi_tools_StringUtils_CompareControl String compare
1528 /// @ingroup cpp_kodi_tools_StringUtils
1529 /// @brief **Check strings for the desired state**\n
1530 /// With this, texts can be checked to see that they correspond to a required
1531 /// format.
1532 ///
1533 /*!@{*/
1534
1535 //============================================================================
1536 /// @brief Compare two strings with ignore of lower-/uppercase.
1537 ///
1538 /// @param[in] str1 C++ string to compare
1539 /// @param[in] str2 C++ string to compare
1540 /// @return True if the strings are equal, false otherwise
1541 ///
1542 ///
1543 /// --------------------------------------------------------------------------
1544 /// Example:
1545 /// ~~~~~~~~~~~~~{.cpp}
1546 /// #include <kodi/tools/StringUtils.h>
1547 ///
1548 /// std::string refstr = "TeSt";
1549 ///
1550 /// EXPECT_TRUE(kodi::tools::StringUtils::EqualsNoCase(refstr, "TeSt"));
1551 /// EXPECT_TRUE(kodi::tools::StringUtils::EqualsNoCase(refstr, "tEsT"));
1552 /// ~~~~~~~~~~~~~
1553 ///
1554 inline static bool EqualsNoCase(const std::string& str1, const std::string& str2)
1555 {
1556 // before we do the char-by-char comparison, first compare sizes of both strings.
1557 // This led to a 33% improvement in benchmarking on average. (size() just returns a member of std::string)
1558 if (str1.size() != str2.size())
1559 return false;
1560 return EqualsNoCase(str1.c_str(), str2.c_str());
1561 }
1562 //----------------------------------------------------------------------------
1563
1564 //============================================================================
1565 /// @brief Compare two strings with ignore of lower-/uppercase.
1566 ///
1567 /// @param[in] str1 C++ string to compare
1568 /// @param[in] s2 C string to compare
1569 /// @return True if the strings are equal, false otherwise
1570 ///
1571 inline static bool EqualsNoCase(const std::string& str1, const char* s2)
1572 {
1573 return EqualsNoCase(str1.c_str(), s2);
1574 }
1575 //----------------------------------------------------------------------------
1576
1577 //============================================================================
1578 /// @brief Compare two strings with ignore of lower-/uppercase.
1579 ///
1580 /// @param[in] s1 C string to compare
1581 /// @param[in] s2 C string to compare
1582 /// @return True if the strings are equal, false otherwise
1583 ///
1584 inline static bool EqualsNoCase(const char* s1, const char* s2)
1585 {
1586 char c2; // we need only one char outside the loop
1587 do
1588 {
1589 const char c1 = *s1++; // const local variable should help compiler to optimize
1590 c2 = *s2++;
1591 // This includes the possibility that one of the characters is the null-terminator,
1592 // which implies a string mismatch.
1593 if (c1 != c2 && ::tolower(c1) != ::tolower(c2))
1594 return false;
1595 } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both.
1596 return true;
1597 }
1598 //----------------------------------------------------------------------------
1599
1600 //============================================================================
1601 /// @brief Compare two strings with ignore of lower-/uppercase with given
1602 /// size.
1603 ///
1604 /// Equal to @ref EqualsNoCase only that size can defined and on return the
1605 /// difference between compared character becomes given.
1606 ///
1607 /// @param[in] str1 C++ string to compare
1608 /// @param[in] str2 C++ string to compare
1609 /// @param[in] n [opt] Length to check, 0 as default to make complete
1610 /// @return 0 if equal, otherwise difference of failed character in string to
1611 /// other ("a" - "b" = -1)
1612 ///
1613 inline static int CompareNoCase(const std::string& str1, const std::string& str2, size_t n = 0)
1614 {
1615 return CompareNoCase(str1.c_str(), str2.c_str(), n);
1616 }
1617 //----------------------------------------------------------------------------
1618
1619 //============================================================================
1620 /// @brief Compare two strings with ignore of lower-/uppercase with given
1621 /// size.
1622 ///
1623 /// Equal to @ref EqualsNoCase only that size can defined and on return the
1624 /// difference between compared character becomes given.
1625 ///
1626 /// @param[in] s1 C string to compare
1627 /// @param[in] s2 C string to compare
1628 /// @param[in] n [opt] Length to check, 0 as default to make complete
1629 /// @return 0 if equal, otherwise difference of failed character in string to
1630 /// other ("a" - "b" = -1)
1631 ///
1632 inline static int CompareNoCase(const char* s1, const char* s2, size_t n = 0)
1633 {
1634 char c2; // we need only one char outside the loop
1635 size_t index = 0;
1636 do
1637 {
1638 const char c1 = *s1++; // const local variable should help compiler to optimize
1639 c2 = *s2++;
1640 index++;
1641 // This includes the possibility that one of the characters is the null-terminator,
1642 // which implies a string mismatch.
1643 if (c1 != c2 && ::tolower(c1) != ::tolower(c2))
1644 return ::tolower(c1) - ::tolower(c2);
1645 } while (c2 != '\0' &&
1646 index != n); // At this point, we know c1 == c2, so there's no need to test them both.
1647 return 0;
1648 }
1649 //----------------------------------------------------------------------------
1650
1651 //============================================================================
1652 /// @brief Checks a string for the begin of another string.
1653 ///
1654 /// @param[in] str1 C++ string to be checked
1655 /// @param[in] str2 C++ string with which text defined in str1 is checked at
1656 /// the beginning
1657 /// @return True if string started with asked text, false otherwise
1658 ///
1659 ///
1660 /// --------------------------------------------------------------------------
1661 /// Example:
1662 /// ~~~~~~~~~~~~~{.cpp}
1663 /// #include <kodi/tools/StringUtils.h>
1664 ///
1665 /// bool ret;
1666 /// std::string refstr = "test";
1667 ///
1668 /// ret = kodi::tools::StringUtils::StartsWith(refstr, "te");
1669 /// fprintf(stderr, "Excpect true for here and is '%s'\n", ret ? "true" : "false");
1670 ///
1671 /// ret = kodi::tools::StringUtils::StartsWith(refstr, "abc");
1672 /// fprintf(stderr, "Excpect false for here and is '%s'\n", ret ? "true" : "false");
1673 /// ~~~~~~~~~~~~~
1674 ///
1675 inline static bool StartsWith(const std::string& str1, const std::string& str2)
1676 {
1677 return str1.compare(0, str2.size(), str2) == 0;
1678 }
1679 //----------------------------------------------------------------------------
1680
1681 //============================================================================
1682 /// @brief Checks a string for the begin of another string.
1683 ///
1684 /// @param[in] str1 C++ string to be checked
1685 /// @param[in] s2 C string with which text defined in str1 is checked at
1686 /// the beginning
1687 /// @return True if string started with asked text, false otherwise
1688 ///
1689 inline static bool StartsWith(const std::string& str1, const char* s2)
1690 {
1691 return StartsWith(str1.c_str(), s2);
1692 }
1693 //----------------------------------------------------------------------------
1694
1695 //============================================================================
1696 /// @brief Checks a string for the begin of another string.
1697 ///
1698 /// @param[in] s1 C string to be checked
1699 /// @param[in] s2 C string with which text defined in str1 is checked at
1700 /// the beginning
1701 /// @return True if string started with asked text, false otherwise
1702 ///
1703 inline static bool StartsWith(const char* s1, const char* s2)
1704 {
1705 while (*s2 != '\0')
1706 {
1707 if (*s1 != *s2)
1708 return false;
1709 s1++;
1710 s2++;
1711 }
1712 return true;
1713 }
1714 //----------------------------------------------------------------------------
1715
1716 //============================================================================
1717 /// @brief Checks a string for the begin of another string by ignore of
1718 /// upper-/lowercase.
1719 ///
1720 /// @param[in] str1 C++ string to be checked
1721 /// @param[in] str2 C++ string with which text defined in str1 is checked at
1722 /// the beginning
1723 /// @return True if string started with asked text, false otherwise
1724 ///
1725 ///
1726 /// --------------------------------------------------------------------------
1727 /// Example:
1728 /// ~~~~~~~~~~~~~{.cpp}
1729 /// #include <kodi/tools/StringUtils.h>
1730 ///
1731 /// bool ret;
1732 /// std::string refstr = "test";
1733 ///
1734 /// ret = kodi::tools::StringUtils::StartsWithNoCase(refstr, "te");
1735 /// fprintf(stderr, "Excpect true for here and is '%s'\n", ret ? "true" : "false");
1736 ///
1737 /// ret = kodi::tools::StringUtils::StartsWithNoCase(refstr, "TEs");
1738 /// fprintf(stderr, "Excpect true for here and is '%s'\n", ret ? "true" : "false");
1739 ///
1740 /// ret = kodi::tools::StringUtils::StartsWithNoCase(refstr, "abc");
1741 /// fprintf(stderr, "Excpect false for here and is '%s'\n", ret ? "true" : "false");
1742 /// ~~~~~~~~~~~~~
1743 ///
1744 inline static bool StartsWithNoCase(const std::string& str1, const std::string& str2)
1745 {
1746 return StartsWithNoCase(str1.c_str(), str2.c_str());
1747 }
1748 //----------------------------------------------------------------------------
1749
1750 //============================================================================
1751 /// @brief Checks a string for the begin of another string by ignore of
1752 /// upper-/lowercase.
1753 ///
1754 /// @param[in] str1 C++ string to be checked
1755 /// @param[in] s2 C string with which text defined in str1 is checked at
1756 /// the beginning
1757 /// @return True if string started with asked text, false otherwise
1758 ///
1759 inline static bool StartsWithNoCase(const std::string& str1, const char* s2)
1760 {
1761 return StartsWithNoCase(str1.c_str(), s2);
1762 }
1763 //----------------------------------------------------------------------------
1764
1765 //============================================================================
1766 /// @brief Checks a string for the begin of another string by ignore of
1767 /// upper-/lowercase.
1768 ///
1769 /// @param[in] s1 C string to be checked
1770 /// @param[in] s2 C string with which text defined in str1 is checked at
1771 /// the beginning
1772 /// @return True if string started with asked text, false otherwise
1773 ///
1774 inline static bool StartsWithNoCase(const char* s1, const char* s2)
1775 {
1776 while (*s2 != '\0')
1777 {
1778 if (::tolower(*s1) != ::tolower(*s2))
1779 return false;
1780 s1++;
1781 s2++;
1782 }
1783 return true;
1784 }
1785 //----------------------------------------------------------------------------
1786
1787 //============================================================================
1788 /// @brief Checks a string for the ending of another string.
1789 ///
1790 /// @param[in] str1 C++ string to be checked
1791 /// @param[in] str2 C++ string with which text defined in str1 is checked at
1792 /// the ending
1793 /// @return True if string ended with asked text, false otherwise
1794 ///
1795 ///
1796 /// --------------------------------------------------------------------------
1797 /// Example:
1798 /// ~~~~~~~~~~~~~{.cpp}
1799 /// #include <kodi/tools/StringUtils.h>
1800 ///
1801 /// bool ret;
1802 /// std::string refstr = "test";
1803 ///
1804 /// ret = kodi::tools::StringUtils::EndsWith(refstr, "st");
1805 /// fprintf(stderr, "Excpect true for here and is '%s'\n", ret ? "true" : "false");
1806 ///
1807 /// ret = kodi::tools::StringUtils::EndsWith(refstr, "abc");
1808 /// fprintf(stderr, "Excpect false for here and is '%s'\n", ret ? "true" : "false");
1809 /// ~~~~~~~~~~~~~
1810 ///
1811 inline static bool EndsWith(const std::string& str1, const std::string& str2)
1812 {
1813 if (str1.size() < str2.size())
1814 return false;
1815 return str1.compare(str1.size() - str2.size(), str2.size(), str2) == 0;
1816 }
1817 //----------------------------------------------------------------------------
1818
1819 //============================================================================
1820 /// @brief Checks a string for the ending of another string.
1821 ///
1822 /// @param[in] str1 C++ string to be checked
1823 /// @param[in] s2 C string with which text defined in str1 is checked at
1824 /// the ending
1825 /// @return True if string ended with asked text, false otherwise
1826 ///
1827 inline static bool EndsWith(const std::string& str1, const char* s2)
1828 {
1829 size_t len2 = strlen(s2);
1830 if (str1.size() < len2)
1831 return false;
1832 return str1.compare(str1.size() - len2, len2, s2) == 0;
1833 }
1834 //----------------------------------------------------------------------------
1835
1836 //============================================================================
1837 /// @brief Checks a string for the ending of another string by ignore of
1838 /// upper-/lowercase.
1839 ///
1840 /// @param[in] str1 C++ string to be checked
1841 /// @param[in] str2 C++ string with which text defined in str1 is checked at
1842 /// the ending
1843 /// @return True if string ended with asked text, false otherwise
1844 ///
1845 ///
1846 /// --------------------------------------------------------------------------
1847 /// Example:
1848 /// ~~~~~~~~~~~~~{.cpp}
1849 /// #include <kodi/tools/StringUtils.h>
1850 ///
1851 /// bool ret;
1852 /// std::string refstr = "test";
1853 ///
1854 /// ret = kodi::tools::StringUtils::EndsWithNoCase(refstr, "ST");
1855 /// fprintf(stderr, "Excpect true for here and is '%s'\n", ret ? "true" : "false");
1856 ///
1857 /// ret = kodi::tools::StringUtils::EndsWithNoCase(refstr, "ABC");
1858 /// fprintf(stderr, "Excpect false for here and is '%s'\n", ret ? "true" : "false");
1859 /// ~~~~~~~~~~~~~
1860 ///
1861 inline static bool EndsWithNoCase(const std::string& str1, const std::string& str2)
1862 {
1863 if (str1.size() < str2.size())
1864 return false;
1865 const char* s1 = str1.c_str() + str1.size() - str2.size();
1866 const char* s2 = str2.c_str();
1867 while (*s2 != '\0')
1868 {
1869 if (::tolower(*s1) != ::tolower(*s2))
1870 return false;
1871 s1++;
1872 s2++;
1873 }
1874 return true;
1875 }
1876 //----------------------------------------------------------------------------
1877
1878 //============================================================================
1879 /// @brief Checks a string for the ending of another string by ignore of
1880 /// upper-/lowercase.
1881 ///
1882 /// @param[in] str1 C++ string to be checked
1883 /// @param[in] s2 C string with which text defined in str1 is checked at
1884 /// the ending
1885 /// @return True if string ended with asked text, false otherwise
1886 ///
1887 inline static bool EndsWithNoCase(const std::string& str1, const char* s2)
1888 {
1889 size_t len2 = strlen(s2);
1890 if (str1.size() < len2)
1891 return false;
1892 const char* s1 = str1.c_str() + str1.size() - len2;
1893 while (*s2 != '\0')
1894 {
1895 if (::tolower(*s1) != ::tolower(*s2))
1896 return false;
1897 s1++;
1898 s2++;
1899 }
1900 return true;
1901 }
1902 //----------------------------------------------------------------------------
1903
1904 //============================================================================
1905 /// @brief Compare two strings by his calculated alpha numeric values.
1906 ///
1907 /// @param[in] left Left string to compare with right
1908 /// @param[in] right Right string to compare with left
1909 /// @return Return about compare
1910 /// - 0 if left and right the same
1911 /// - -1 if right is longer
1912 /// - 1 if left is longer
1913 /// - < 0 if less equal
1914 /// - > 0 if more equal
1915 ///
1916 ///
1917 /// --------------------------------------------------------------------------
1918 /// Example:
1919 /// ~~~~~~~~~~~~~{.cpp}
1920 /// #include <kodi/tools/StringUtils.h>
1921 ///
1922 /// int64_t ref, var;
1923 ///
1924 /// ref = 0;
1925 /// var = kodi::tools::StringUtils::AlphaNumericCompare(L"123abc", L"abc123");
1926 /// EXPECT_LT(var, ref);
1927 /// ~~~~~~~~~~~~~
1928 ///
1929 inline static int64_t AlphaNumericCompare(const wchar_t* left, const wchar_t* right)
1930 {
1931 const wchar_t* l = left;
1932 const wchar_t* r = right;
1933 const wchar_t *ld, *rd;
1934 wchar_t lc, rc;
1935 int64_t lnum, rnum;
1936 const std::collate<wchar_t>& coll = std::use_facet<std::collate<wchar_t>>(std::locale());
1937 int cmp_res = 0;
1938 while (*l != 0 && *r != 0)
1939 {
1940 // check if we have a numerical value
1941 if (*l >= L'0' && *l <= L'9' && *r >= L'0' && *r <= L'9')
1942 {
1943 ld = l;
1944 lnum = 0;
1945 while (*ld >= L'0' && *ld <= L'9' && ld < l + 15)
1946 { // compare only up to 15 digits
1947 lnum *= 10;
1948 lnum += *ld++ - '0';
1949 }
1950 rd = r;
1951 rnum = 0;
1952 while (*rd >= L'0' && *rd <= L'9' && rd < r + 15)
1953 { // compare only up to 15 digits
1954 rnum *= 10;
1955 rnum += *rd++ - L'0';
1956 }
1957 // do we have numbers?
1958 if (lnum != rnum)
1959 { // yes - and they're different!
1960 return lnum - rnum;
1961 }
1962 l = ld;
1963 r = rd;
1964 continue;
1965 }
1966 // do case less comparison
1967 lc = *l;
1968 if (lc >= L'A' && lc <= L'Z')
1969 lc += L'a' - L'A';
1970 rc = *r;
1971 if (rc >= L'A' && rc <= L'Z')
1972 rc += L'a' - L'A';
1973
1974 // ok, do a normal comparison, taking current locale into account. Add special case stuff (eg '(' characters)) in here later
1975 if ((cmp_res = coll.compare(&lc, &lc + 1, &rc, &rc + 1)) != 0)
1976 {
1977 return cmp_res;
1978 }
1979 l++;
1980 r++;
1981 }
1982 if (*r)
1983 { // r is longer
1984 return -1;
1985 }
1986 else if (*l)
1987 { // l is longer
1988 return 1;
1989 }
1990 return 0; // files are the same
1991 }
1992 //----------------------------------------------------------------------------
1993
1994 //============================================================================
1995 /// @brief UTF8 version of strlen
1996 ///
1997 /// Skips any non-starting bytes in the count, thus returning the number of
1998 /// utf8 characters.
1999 ///
2000 /// @param[in] s c-string to find the length of.
2001 /// @return The number of utf8 characters in the string.
2002 ///
2003 inline static size_t Utf8StringLength(const char* s)
2004 {
2005 size_t length = 0;
2006 while (*s)
2007 {
2008 if ((*s++ & 0xC0) != 0x80)
2009 length++;
2010 }
2011 return length;
2012 }
2013 //----------------------------------------------------------------------------
2014
2015 //============================================================================
2016 /// @brief Check given character is a space.
2017 ///
2018 /// Hack to check only first byte of UTF-8 character
2019 /// without this hack "TrimX" functions failed on Win32 and OS X with UTF-8 strings
2020 ///
2021 /// @param[in] c Character to check
2022 /// @return true if space, false otherwise
2023 ///
2024 inline static int IsSpace(char c) { return (c & 0x80) == 0 && ::isspace(c); }
2025 //----------------------------------------------------------------------------
2026
2027 //============================================================================
2028 /// @brief Checks given pointer in string is a UTF8 letter.
2029 ///
2030 /// @param[in] str Given character values to check, must be minimum array of 2
2031 /// @return return -1 if not, else return the utf8 char length.
2032 ///
2033 inline static int IsUTF8Letter(const unsigned char* str)
2034 {
2035 // reference:
2036 // unicode -> utf8 table: http://www.utf8-chartable.de/
2037 // latin characters in unicode: http://en.wikipedia.org/wiki/Latin_characters_in_Unicode
2038 unsigned char ch = str[0];
2039 if (!ch)
2040 return -1;
2041 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
2042 return 1;
2043 if (!(ch & 0x80))
2044 return -1;
2045 unsigned char ch2 = str[1];
2046 if (!ch2)
2047 return -1;
2048 // check latin 1 letter table: http://en.wikipedia.org/wiki/C1_Controls_and_Latin-1_Supplement
2049 if (ch == 0xC3 && ch2 >= 0x80 && ch2 <= 0xBF && ch2 != 0x97 && ch2 != 0xB7)
2050 return 2;
2051 // check latin extended A table: http://en.wikipedia.org/wiki/Latin_Extended-A
2052 if (ch >= 0xC4 && ch <= 0xC7 && ch2 >= 0x80 && ch2 <= 0xBF)
2053 return 2;
2054 // check latin extended B table: http://en.wikipedia.org/wiki/Latin_Extended-B
2055 // and International Phonetic Alphabet: http://en.wikipedia.org/wiki/IPA_Extensions_(Unicode_block)
2056 if (((ch == 0xC8 || ch == 0xC9) && ch2 >= 0x80 && ch2 <= 0xBF) ||
2057 (ch == 0xCA && ch2 >= 0x80 && ch2 <= 0xAF))
2058 return 2;
2059 return -1;
2060 }
2061 //----------------------------------------------------------------------------
2062
2063 //============================================================================
2064 /// @brief Check whether a string is a natural number.
2065 ///
2066 /// Matches `[ \t]*[0-9]+[ \t]*`
2067 ///
2068 /// @param[in] str The string to check
2069 /// @return true if the string is a natural number, false otherwise.
2070 ///
2071 ///
2072 /// --------------------------------------------------------------------------
2073 /// Example:
2074 /// ~~~~~~~~~~~~~{.cpp}
2075 /// #include <kodi/tools/StringUtils.h>
2076 ///
2077 /// EXPECT_TRUE(kodi::tools::StringUtils::IsNaturalNumber("10"));
2078 /// EXPECT_TRUE(kodi::tools::StringUtils::IsNaturalNumber(" 10"));
2079 /// EXPECT_TRUE(kodi::tools::StringUtils::IsNaturalNumber("0"));
2080 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber(" 1 0"));
2081 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber("1.0"));
2082 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber("1.1"));
2083 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber("0x1"));
2084 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber("blah"));
2085 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber("120 h"));
2086 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber(" "));
2087 /// EXPECT_FALSE(kodi::tools::StringUtils::IsNaturalNumber(""));
2088 /// ~~~~~~~~~~~~~
2089 ///
2090 inline static bool IsNaturalNumber(const std::string& str)
2091 {
2092 size_t i = 0, n = 0;
2093 // allow whitespace,digits,whitespace
2094 while (i < str.size() && isspace((unsigned char)str[i]))
2095 i++;
2096 while (i < str.size() && isdigit((unsigned char)str[i]))
2097 {
2098 i++;
2099 n++;
2100 }
2101 while (i < str.size() && isspace((unsigned char)str[i]))
2102 i++;
2103 return i == str.size() && n > 0;
2104 }
2105 //----------------------------------------------------------------------------
2106
2107 //============================================================================
2108 /// @brief Check whether a string is an integer.
2109 ///
2110 /// Matches `[ \t]*[\-]*[0-9]+[ \t]*`
2111 ///
2112 /// @param str The string to check
2113 /// @return true if the string is an integer, false otherwise.
2114 ///
2115 ///
2116 /// --------------------------------------------------------------------------
2117 /// Example:
2118 /// ~~~~~~~~~~~~~{.cpp}
2119 /// #include <kodi/tools/StringUtils.h>
2120 ///
2121 /// EXPECT_TRUE(kodi::tools::StringUtils::IsInteger("10"));
2122 /// EXPECT_TRUE(kodi::tools::StringUtils::IsInteger(" -10"));
2123 /// EXPECT_TRUE(kodi::tools::StringUtils::IsInteger("0"));
2124 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger(" 1 0"));
2125 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger("1.0"));
2126 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger("1.1"));
2127 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger("0x1"));
2128 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger("blah"));
2129 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger("120 h"));
2130 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger(" "));
2131 /// EXPECT_FALSE(kodi::tools::StringUtils::IsInteger(""));
2132 /// ~~~~~~~~~~~~~
2133 ///
2134 inline static bool IsInteger(const std::string& str)
2135 {
2136 size_t i = 0, n = 0;
2137 // allow whitespace,-,digits,whitespace
2138 while (i < str.size() && isspace((unsigned char)str[i]))
2139 i++;
2140 if (i < str.size() && str[i] == '-')
2141 i++;
2142 while (i < str.size() && isdigit((unsigned char)str[i]))
2143 {
2144 i++;
2145 n++;
2146 }
2147 while (i < str.size() && isspace((unsigned char)str[i]))
2148 i++;
2149 return i == str.size() && n > 0;
2150 }
2151 //----------------------------------------------------------------------------
2152
2153 //============================================================================
2154 /// @brief Checks a character is ascii number.
2155 ///
2156 /// @param[in] chr Single character to test
2157 /// @return true if yes, false otherwise
2158 ///
2159 inline static bool IsAasciiDigit(char chr) // locale independent
2160 {
2161 return chr >= '0' && chr <= '9';
2162 }
2163 //----------------------------------------------------------------------------
2164
2165 //============================================================================
2166 /// @brief Checks a character is ascii hexadecimal number.
2167 ///
2168 /// @param[in] chr Single character to test
2169 /// @return true if yes, false otherwise
2170 ///
2171 inline static bool IsAsciiXDigit(char chr) // locale independent
2172 {
2173 return (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F');
2174 }
2175 //----------------------------------------------------------------------------
2176
2177 //============================================================================
2178 /// @brief Translate a character where defined as a numerical value (0-9)
2179 /// string to right integer.
2180 ///
2181 /// @param[in] chr Single character to translate
2182 /// @return
2183 ///
2184 inline static int AsciiDigitValue(char chr) // locale independent
2185 {
2186 if (!IsAasciiDigit(chr))
2187 return -1;
2188
2189 return chr - '0';
2190 }
2191 //----------------------------------------------------------------------------
2192
2193 //============================================================================
2194 /// @brief Translate a character where defined as a hexadecimal value string
2195 /// to right integer.
2196 ///
2197 /// @param[in] chr Single character to translate
2198 /// @return Corresponding integer value, e.g. character is "A" becomes
2199 /// returned as a integer with 10.
2200 ///
2201 inline static int AsciiXDigitValue(char chr) // locale independent
2202 {
2203 int v = AsciiDigitValue(chr);
2204 if (v >= 0)
2205 return v;
2206 if (chr >= 'a' && chr <= 'f')
2207 return chr - 'a' + 10;
2208 if (chr >= 'A' && chr <= 'F')
2209 return chr - 'A' + 10;
2210
2211 return -1;
2212 }
2213 //----------------------------------------------------------------------------
2214
2215 //============================================================================
2216 /// @brief Checks a character is ascii alphabetic lowercase.
2217 ///
2218 /// @param[in] chr Single character to test
2219 /// @return True if ascii uppercase letter, false otherwise
2220 ///
2221 inline static bool IsAsciiUppercaseLetter(char chr) // locale independent
2222 {
2223 return (chr >= 'A' && chr <= 'Z');
2224 }
2225 //----------------------------------------------------------------------------
2226
2227 //============================================================================
2228 /// @brief Checks a character is ascii alphabetic lowercase.
2229 ///
2230 /// @param[in] chr Single character to test
2231 /// @return True if ascii lowercase letter, false otherwise
2232 ///
2233 inline static bool IsAsciiLowercaseLetter(char chr) // locale independent
2234 {
2235 return (chr >= 'a' && chr <= 'z');
2236 }
2237 //----------------------------------------------------------------------------
2238
2239 //============================================================================
2240 /// @brief Checks a character is within ascii alphabetic and numerical fields.
2241 ///
2242 /// @param[in] chr Single character to test
2243 /// @return true if alphabetic / numerical ascii value
2244 ///
2245 inline static bool IsAsciiAlphaNum(char chr) // locale independent
2246 {
2247 return IsAsciiUppercaseLetter(chr) || IsAsciiLowercaseLetter(chr) || IsAasciiDigit(chr);
2248 }
2249 //----------------------------------------------------------------------------
2250
2251 //============================================================================
2252 /// @brief Check a string for another text.
2253 ///
2254 /// @param[in] str String to seach for keywords
2255 /// @param[in] keywords List of keywords to search in text
2256 /// @return true if string contains word in list
2257 ///
2258 inline static bool ContainsKeyword(const std::string& str,
2259 const std::vector<std::string>& keywords)
2260 {
2261 for (const auto& it : keywords)
2262 {
2263 if (str.find(it) != str.npos)
2264 return true;
2265 }
2266 return false;
2267 }
2268 //----------------------------------------------------------------------------
2269
2270 /*!@}*/
2271
2272 //----------------------------------------------------------------------------
2273 /// @defgroup cpp_kodi_tools_StringUtils_SearchControl String search
2274 /// @ingroup cpp_kodi_tools_StringUtils
2275 /// @brief **To search a string**\n
2276 /// Various functions are defined in here which allow you to search through a
2277 /// text in different ways.
2278 ///
2279 /*!@{*/
2280
2281 //============================================================================
2282 /// @brief Search for a single word within a text.
2283 ///
2284 /// @param[in] str String to search within
2285 /// @param[in] wordLowerCase Word as lowercase to search
2286 /// @return Position in string where word is found, -1 (std::string::npos) if
2287 /// not found
2288 ///
2289 ///
2290 /// --------------------------------------------------------------------------
2291 /// Example:
2292 /// ~~~~~~~~~~~~~{.cpp}
2293 /// #include <kodi/tools/StringUtils.h>
2294 ///
2295 /// size_t ref, var;
2296 ///
2297 /// // The name "string" is alone within text and becomes found on position 5
2298 /// ref = 5;
2299 /// var = kodi::tools::StringUtils::FindWords("test string", "string");
2300 /// EXPECT_EQ(ref, var);
2301 ///
2302 /// // The 12 is included inside another word and then not found as it should alone (-1 return)
2303 /// ref = -1;
2304 /// var = kodi::tools::StringUtils::FindWords("apple2012", "12");
2305 /// EXPECT_EQ(ref, var);
2306 /// ~~~~~~~~~~~~~
2307 ///
2308 inline static size_t FindWords(const char* str, const char* wordLowerCase)
2309 {
2310 // NOTE: This assumes word is lowercase!
2311 const unsigned char* s = (const unsigned char*)str;
2312 do
2313 {
2314 // start with a compare
2315 const unsigned char* c = s;
2316 const unsigned char* w = (const unsigned char*)wordLowerCase;
2317 bool same = true;
2318 while (same && *c && *w)
2319 {
2320 unsigned char lc = *c++;
2321 if (lc >= 'A' && lc <= 'Z')
2322 lc += 'a' - 'A';
2323
2324 if (lc != *w++) // different
2325 same = false;
2326 }
2327 if (same && *w == 0) // only the same if word has been exhausted
2328 return (const char*)s - str;
2329
2330 // otherwise, skip current word (composed by latin letters) or number
2331 int l;
2332 if (*s >= '0' && *s <= '9')
2333 {
2334 ++s;
2335 while (*s >= '0' && *s <= '9')
2336 ++s;
2337 }
2338 else if ((l = IsUTF8Letter(s)) > 0)
2339 {
2340 s += l;
2341 while ((l = IsUTF8Letter(s)) > 0)
2342 s += l;
2343 }
2344 else
2345 ++s;
2346 while (*s && *s == ' ')
2347 s++;
2348
2349 // and repeat until we're done
2350 } while (*s);
2351
2352 return std::string::npos;
2353 }
2354 //----------------------------------------------------------------------------
2355
2356 //============================================================================
2357 /// @brief Search a string for a given bracket and give its end position.
2358 ///
2359 /// @param[in] str String to search within
2360 /// @param[in] opener Begin character to start search
2361 /// @param[in] closer End character to end search
2362 /// @param[in] startPos [opt] Position to start search in string, 0 as default
2363 /// to start from begin
2364 /// @return End position where found, -1 if failed
2365 ///
2366 ///
2367 /// --------------------------------------------------------------------------
2368 /// Example:
2369 /// ~~~~~~~~~~~~~{.cpp}
2370 /// #include <kodi/tools/StringUtils.h>
2371 ///
2372 /// int ref, var;
2373 ///
2374 /// ref = 11;
2375 /// var = kodi::tools::StringUtils::FindEndBracket("atest testbb test", 'a', 'b');
2376 /// EXPECT_EQ(ref, var);
2377 /// ~~~~~~~~~~~~~
2378 ///
2379 inline static size_t FindEndBracket(const std::string& str,
2380 char opener,
2381 char closer,
2382 size_t startPos = 0)
2383 {
2384 size_t blocks = 1;
2385 for (size_t i = startPos; i < str.size(); i++)
2386 {
2387 if (str[i] == opener)
2388 blocks++;
2389 else if (str[i] == closer)
2390 {
2391 blocks--;
2392 if (!blocks)
2393 return i;
2394 }
2395 }
2396
2397 return std::string::npos;
2398 }
2399 //----------------------------------------------------------------------------
2400
2401 //============================================================================
2402 /// @brief Search a text and return the number of parts found as a number.
2403 ///
2404 /// @param[in] strInput Input string to search for
2405 /// @param[in] strFind String to search in input
2406 /// @return Amount how much the string is found
2407 ///
2408 ///
2409 /// --------------------------------------------------------------------------
2410 /// Example:
2411 /// ~~~~~~~~~~~~~{.cpp}
2412 /// #include <kodi/tools/StringUtils.h>
2413 ///
2414 /// EXPECT_EQ(3, kodi::tools::StringUtils::FindNumber("aabcaadeaa", "aa"));
2415 /// EXPECT_EQ(1, kodi::tools::StringUtils::FindNumber("aabcaadeaa", "b"));
2416 /// ~~~~~~~~~~~~~
2417 ///
2418 inline static int FindNumber(const std::string& strInput, const std::string& strFind)
2419 {
2420 size_t pos = strInput.find(strFind, 0);
2421 int numfound = 0;
2422 while (pos != std::string::npos)
2423 {
2424 numfound++;
2425 pos = strInput.find(strFind, pos + 1);
2426 }
2427 return numfound;
2428 }
2429 //----------------------------------------------------------------------------
2430
2431 /*!@}*/
2432
2433 //----------------------------------------------------------------------------
2434 /// @defgroup cpp_kodi_tools_StringUtils_ListControl String list
2435 /// @ingroup cpp_kodi_tools_StringUtils
2436 /// @brief **Creating lists using a string**\n
2437 /// With this, either simple vectors or lists defined by templates can be given
2438 /// for the respective divided text.
2439 ///
2440 /*!@{*/
2441
2442 //============================================================================
2443 /// @brief Concatenates the elements of a specified array or the members of a
2444 /// collection and uses the specified separator between each element or
2445 /// member.
2446 ///
2447 /// @param[in] strings An array of objects whose string representations are
2448 /// concatenated.
2449 /// @param[in] delimiter Delimiter to be used to join the input string
2450 /// @return A string consisting of the elements of values, separated by the
2451 /// separator character.
2452 ///
2453 ///
2454 /// --------------------------------------------------------------------------
2455 /// Example:
2456 /// ~~~~~~~~~~~~~{.cpp}
2457 /// #include <kodi/tools/StringUtils.h>
2458 ///
2459 /// std::string refstr, varstr;
2460 /// std::vector<std::string> strarray;
2461 ///
2462 /// strarray.emplace_back("a");
2463 /// strarray.emplace_back("b");
2464 /// strarray.emplace_back("c");
2465 /// strarray.emplace_back("de");
2466 /// strarray.emplace_back(",");
2467 /// strarray.emplace_back("fg");
2468 /// strarray.emplace_back(",");
2469 /// refstr = "a,b,c,de,,,fg,,";
2470 /// varstr = kodi::tools::StringUtils::Join(strarray, ",");
2471 /// EXPECT_STREQ(refstr.c_str(), varstr.c_str());
2472 /// ~~~~~~~~~~~~~
2473 ///
2474 template<typename CONTAINER>
2475 inline static std::string Join(const CONTAINER& strings, const std::string& delimiter)
2476 {
2477 std::string result;
2478 for (const auto& str : strings)
2479 result += str + delimiter;
2480
2481 if (!result.empty())
2482 result.erase(result.size() - delimiter.size());
2483 return result;
2484 }
2485 //----------------------------------------------------------------------------
2486
2487 //============================================================================
2488 /// @brief Splits the given input string using the given delimiter into
2489 /// separate strings.
2490 ///
2491 /// If the given input string is empty the result will be an empty array (not
2492 /// an array containing an empty string).
2493 ///
2494 /// @param[in] input Input string to be split
2495 /// @param[in] delimiter Delimiter to be used to split the input string
2496 /// @param[in] iMaxStrings [opt] Maximum number of resulting split strings
2497 /// @return List of splitted strings
2498 ///
2499 ///
2500 /// --------------------------------------------------------------------------
2501 /// Example:
2502 /// ~~~~~~~~~~~~~{.cpp}
2503 /// #include <kodi/tools/StringUtils.h>
2504 ///
2505 /// std::vector<std::string> varresults;
2506 ///
2507 /// varresults = kodi::tools::StringUtils::Split("g,h,ij,k,lm,,n", ",");
2508 /// EXPECT_STREQ("g", varresults.at(0).c_str());
2509 /// EXPECT_STREQ("h", varresults.at(1).c_str());
2510 /// EXPECT_STREQ("ij", varresults.at(2).c_str());
2511 /// EXPECT_STREQ("k", varresults.at(3).c_str());
2512 /// EXPECT_STREQ("lm", varresults.at(4).c_str());
2513 /// EXPECT_STREQ("", varresults.at(5).c_str());
2514 /// EXPECT_STREQ("n", varresults.at(6).c_str());
2515 /// ~~~~~~~~~~~~~
2516 ///
2517 inline static std::vector<std::string> Split(const std::string& input,
2518 const std::string& delimiter,
2519 unsigned int iMaxStrings = 0)
2520 {
2521 std::vector<std::string> result;
2522 SplitTo(std::back_inserter(result), input, delimiter, iMaxStrings);
2523 return result;
2524 }
2525 //----------------------------------------------------------------------------
2526
2527 //============================================================================
2528 /// @brief Splits the given input string using the given delimiter into
2529 /// separate strings.
2530 ///
2531 /// If the given input string is empty the result will be an empty array (not
2532 /// an array containing an empty string).
2533 ///
2534 /// @param[in] input Input string to be split
2535 /// @param[in] delimiter Delimiter to be used to split the input string
2536 /// @param[in] iMaxStrings [opt] Maximum number of resulting split strings
2537 /// @return List of splitted strings
2538 ///
2539 inline static std::vector<std::string> Split(const std::string& input,
2540 const char delimiter,
2541 int iMaxStrings = 0)
2542 {
2543 std::vector<std::string> result;
2544 SplitTo(std::back_inserter(result), input, delimiter, iMaxStrings);
2545 return result;
2546 }
2547 //----------------------------------------------------------------------------
2548
2549 //============================================================================
2550 /// @brief Splits the given input string using the given delimiter into
2551 /// separate strings.
2552 ///
2553 /// If the given input string is empty the result will be an empty array (not
2554 /// an array containing an empty string).
2555 ///
2556 /// @param[in] input Input string to be split
2557 /// @param[in] delimiters Delimiter strings to be used to split the input
2558 /// strings
2559 /// @return List of splitted strings
2560 ///
2561 inline static std::vector<std::string> Split(const std::string& input,
2562 const std::vector<std::string>& delimiters)
2563 {
2564 std::vector<std::string> result;
2565 SplitTo(std::back_inserter(result), input, delimiters);
2566 return result;
2567 }
2568 //----------------------------------------------------------------------------
2569
2570 //============================================================================
2571 /// @brief Splits the given input string using the given delimiter into
2572 /// separate strings.
2573 ///
2574 /// If the given input string is empty nothing will be put into the target
2575 /// iterator.
2576 ///
2577 /// @param[in] d_first The beginning of the destination range
2578 /// @param[in] input Input string to be split
2579 /// @param[in] delimiter Delimiter to be used to split the input string
2580 /// @param[in] iMaxStrings [opt] Maximum number of resulting split strings
2581 /// @return Output iterator to the element in the destination range, one past
2582 /// the last element that was put there
2583 ///
2584 template<typename OutputIt>
2585 inline static OutputIt SplitTo(OutputIt d_first,
2586 const std::string& input,
2587 const std::string& delimiter,
2588 unsigned int iMaxStrings = 0)
2589 {
2590 OutputIt dest = d_first;
2591
2592 if (input.empty())
2593 return dest;
2594 if (delimiter.empty())
2595 {
2596 *d_first++ = input;
2597 return dest;
2598 }
2599
2600 const size_t delimLen = delimiter.length();
2601 size_t nextDelim;
2602 size_t textPos = 0;
2603 do
2604 {
2605 if (--iMaxStrings == 0)
2606 {
2607 *dest++ = input.substr(textPos);
2608 break;
2609 }
2610 nextDelim = input.find(delimiter, textPos);
2611 *dest++ = input.substr(textPos, nextDelim - textPos);
2612 textPos = nextDelim + delimLen;
2613 } while (nextDelim != std::string::npos);
2614
2615 return dest;
2616 }
2617 //----------------------------------------------------------------------------
2618
2619 //============================================================================
2620 /// @brief Splits the given input string using the given delimiter into
2621 /// separate strings.
2622 ///
2623 /// If the given input string is empty nothing will be put into the target
2624 /// iterator.
2625 ///
2626 /// @param[in] d_first The beginning of the destination range
2627 /// @param[in] input Input string to be split
2628 /// @param[in] delimiter Delimiter to be used to split the input string
2629 /// @param[in] iMaxStrings [opt] Maximum number of resulting split strings
2630 /// @return Output iterator to the element in the destination range, one past
2631 /// the last element that was put there
2632 ///
2633 template<typename OutputIt>
2634 inline static OutputIt SplitTo(OutputIt d_first,
2635 const std::string& input,
2636 const char delimiter,
2637 int iMaxStrings = 0)
2638 {
2639 return SplitTo(d_first, input, std::string(1, delimiter), iMaxStrings);
2640 }
2641 //----------------------------------------------------------------------------
2642
2643 //============================================================================
2644 /// @brief Splits the given input string using the given delimiter into
2645 /// separate strings.
2646 ///
2647 /// If the given input string is empty nothing will be put into the target
2648 /// iterator.
2649 ///
2650 /// @param[in] d_first The beginning of the destination range
2651 /// @param[in] input Input string to be split
2652 /// @param[in] delimiters Delimiter strings to be used to split the input
2653 /// strings
2654 /// @return Output iterator to the element in the destination range, one past
2655 /// the last element that was put there
2656 ///
2657 template<typename OutputIt>
2658 inline static OutputIt SplitTo(OutputIt d_first,
2659 const std::string& input,
2660 const std::vector<std::string>& delimiters)
2661 {
2662 OutputIt dest = d_first;
2663 if (input.empty())
2664 return dest;
2665
2666 if (delimiters.empty())
2667 {
2668 *dest++ = input;
2669 return dest;
2670 }
2671 std::string str = input;
2672 for (size_t di = 1; di < delimiters.size(); di++)
2673 StringUtils::Replace(str, delimiters[di], delimiters[0]);
2674 return SplitTo(dest, str, delimiters[0]);
2675 }
2676 //----------------------------------------------------------------------------
2677
2678 //============================================================================
2679 /// @brief Splits the given input strings using the given delimiters into
2680 /// further separate strings.
2681 ///
2682 /// If the given input string vector is empty the result will be an empty
2683 /// array (not an array containing an empty string).
2684 ///
2685 /// Delimiter strings are applied in order, so once the (optional) maximum
2686 /// number of items is produced no other delimiters are applied. This produces
2687 /// different results to applying all delimiters at once e.g. "a/b#c/d"
2688 /// becomes "a", "b#c", "d" rather than "a", "b", "c/d"
2689 ///
2690 /// @param[in] input Input vector of strings each to be split
2691 /// @param[in] delimiters Delimiter strings to be used to split the input
2692 /// strings
2693 /// @param[in] iMaxStrings [opt] Maximum number of resulting split strings
2694 /// @return List of splitted strings
2695 ///
2696 inline static std::vector<std::string> SplitMulti(const std::vector<std::string>& input,
2697 const std::vector<std::string>& delimiters,
2698 unsigned int iMaxStrings = 0)
2699 {
2700 if (input.empty())
2701 return std::vector<std::string>();
2702
2703 std::vector<std::string> results(input);
2704
2705 if (delimiters.empty() || (iMaxStrings > 0 && iMaxStrings <= input.size()))
2706 return results;
2707
2708 std::vector<std::string> strings1;
2709 if (iMaxStrings == 0)
2710 {
2711 for (size_t di = 0; di < delimiters.size(); di++)
2712 {
2713 for (size_t i = 0; i < results.size(); i++)
2714 {
2715 std::vector<std::string> substrings = StringUtils::Split(results[i], delimiters[di]);
2716 for (size_t j = 0; j < substrings.size(); j++)
2717 strings1.push_back(substrings[j]);
2718 }
2719 results = strings1;
2720 strings1.clear();
2721 }
2722 return results;
2723 }
2724
2725 // Control the number of strings input is split into, keeping the original strings.
2726 // Note iMaxStrings > input.size()
2727 size_t iNew = iMaxStrings - results.size();
2728 for (size_t di = 0; di < delimiters.size(); di++)
2729 {
2730 for (size_t i = 0; i < results.size(); i++)
2731 {
2732 if (iNew > 0)
2733 {
2734 std::vector<std::string> substrings =
2735 StringUtils::Split(results[i], delimiters[di], static_cast<int>(iNew + 1));
2736 iNew = iNew - substrings.size() + 1;
2737 for (size_t j = 0; j < substrings.size(); j++)
2738 strings1.push_back(substrings[j]);
2739 }
2740 else
2741 strings1.push_back(results[i]);
2742 }
2743 results = strings1;
2744 iNew = iMaxStrings - results.size();
2745 strings1.clear();
2746 if ((iNew <= 0))
2747 break; //Stop trying any more delimiters
2748 }
2749 return results;
2750 }
2751 //----------------------------------------------------------------------------
2752
2753 //============================================================================
2754 /// @brief Split a string by the specified delimiters.
2755 ///
2756 /// Splits a string using one or more delimiting characters, ignoring empty
2757 /// tokens.
2758 ///
2759 /// Differs from Split() in two ways:
2760 /// 1. The delimiters are treated as individual characters, rather than a single delimiting string.
2761 /// 2. Empty tokens are ignored.
2762 ///
2763 ///
2764 /// @param[in] input String to split
2765 /// @param[in] delimiters Delimiters
2766 /// @return A vector of tokens
2767 ///
2768 inline static std::vector<std::string> Tokenize(const std::string& input,
2769 const std::string& delimiters)
2770 {
2771 std::vector<std::string> tokens;
2772 Tokenize(input, tokens, delimiters);
2773 return tokens;
2774 }
2775 //----------------------------------------------------------------------------
2776
2777 //============================================================================
2778 /// @brief Tokenizing a string denotes splitting a string with respect to a
2779 /// delimiter.
2780 ///
2781 /// @param[in] input String to split
2782 /// @param[out] tokens A vector of tokens
2783 /// @param[in] delimiters Delimiters
2784 ///
2785 inline static void Tokenize(const std::string& input,
2786 std::vector<std::string>& tokens,
2787 const std::string& delimiters)
2788 {
2789 tokens.clear();
2790 // Skip delimiters at beginning.
2791 std::string::size_type dataPos = input.find_first_not_of(delimiters);
2792 while (dataPos != std::string::npos)
2793 {
2794 // Find next delimiter
2795 const std::string::size_type nextDelimPos = input.find_first_of(delimiters, dataPos);
2796 // Found a token, add it to the vector.
2797 tokens.push_back(input.substr(dataPos, nextDelimPos - dataPos));
2798 // Skip delimiters. Note the "not_of"
2799 dataPos = input.find_first_not_of(delimiters, nextDelimPos);
2800 }
2801 }
2802 //----------------------------------------------------------------------------
2803
2804 //============================================================================
2805 /// @brief Tokenizing a string denotes splitting a string with respect to a
2806 /// delimiter.
2807 ///
2808 /// @param[in] input String to split
2809 /// @param[in] delimiter Delimiters
2810 /// @return A vector of tokens
2811 ///
2812 inline static std::vector<std::string> Tokenize(const std::string& input, const char delimiter)
2813 {
2814 std::vector<std::string> tokens;
2815 Tokenize(input, tokens, delimiter);
2816 return tokens;
2817 }
2818 //----------------------------------------------------------------------------
2819
2820 //============================================================================
2821 /// @brief Tokenizing a string denotes splitting a string with respect to a
2822 /// delimiter.
2823 ///
2824 /// @param[in] input String to split
2825 /// @param[out] tokens List of
2826 /// @param[in] delimiter Delimiters
2827 ///
2828 inline static void Tokenize(const std::string& input,
2829 std::vector<std::string>& tokens,
2830 const char delimiter)
2831 {
2832 tokens.clear();
2833 // Skip delimiters at beginning.
2834 std::string::size_type dataPos = input.find_first_not_of(delimiter);
2835 while (dataPos != std::string::npos)
2836 {
2837 // Find next delimiter
2838 const std::string::size_type nextDelimPos = input.find(delimiter, dataPos);
2839 // Found a token, add it to the vector.
2840 tokens.push_back(input.substr(dataPos, nextDelimPos - dataPos));
2841 // Skip delimiters. Note the "not_of"
2842 dataPos = input.find_first_not_of(delimiter, nextDelimPos);
2843 }
2844 }
2845 //----------------------------------------------------------------------------
2846
2847 /*!@}*/
2848
2849 //----------------------------------------------------------------------------
2850 /// @defgroup cpp_kodi_tools_StringUtils_TimeControl Time value processing
2851 /// @ingroup cpp_kodi_tools_StringUtils
2852 /// @brief **String time formats**\n
2853 /// This is used to process the respective time formats in text fields.
2854 /*!@{*/
2855
2856 //============================================================================
2857 /// @brief Converts a time string to the respective integer value.
2858 ///
2859 /// @param[in] timeString String with time.\n
2860 /// Following types are possible:
2861 /// - "MM min" (integer number with "min" on end)
2862 /// - "HH:MM:SS"
2863 /// @return Time in seconds
2864 ///
2865 ///
2866 /// --------------------------------------------------------------------------
2867 /// Example:
2868 /// ~~~~~~~~~~~~~{.cpp}
2869 /// #include <kodi/tools/StringUtils.h>
2870 ///
2871 /// EXPECT_EQ(77455, kodi::tools::StringUtils::TimeStringToSeconds("21:30:55"));
2872 /// EXPECT_EQ(7*60, kodi::tools::StringUtils::TimeStringToSeconds("7 min"));
2873 /// EXPECT_EQ(7*60, kodi::tools::StringUtils::TimeStringToSeconds("7 min\t"));
2874 /// EXPECT_EQ(154*60, kodi::tools::StringUtils::TimeStringToSeconds(" 154 min"));
2875 /// EXPECT_EQ(1*60+1, kodi::tools::StringUtils::TimeStringToSeconds("1:01"));
2876 /// EXPECT_EQ(4*60+3, kodi::tools::StringUtils::TimeStringToSeconds("4:03"));
2877 /// EXPECT_EQ(2*3600+4*60+3, kodi::tools::StringUtils::TimeStringToSeconds("2:04:03"));
2878 /// EXPECT_EQ(2*3600+4*60+3, kodi::tools::StringUtils::TimeStringToSeconds(" 2:4:3"));
2879 /// EXPECT_EQ(2*3600+4*60+3, kodi::tools::StringUtils::TimeStringToSeconds(" \t\t 02:04:03 \n "));
2880 /// EXPECT_EQ(1*3600+5*60+2, kodi::tools::StringUtils::TimeStringToSeconds("01:05:02:04:03 \n "));
2881 /// EXPECT_EQ(0, kodi::tools::StringUtils::TimeStringToSeconds("blah"));
2882 /// EXPECT_EQ(0, kodi::tools::StringUtils::TimeStringToSeconds("ля-ля"));
2883 /// ~~~~~~~~~~~~~
2884 ///
2885 inline static long TimeStringToSeconds(const std::string& timeString)
2886 {
2887 std::string strCopy(timeString);
2888 StringUtils::Trim(strCopy);
2889 if (StringUtils::EndsWithNoCase(strCopy, " min"))
2890 {
2891 // this is imdb format of "XXX min"
2892 return 60 * atoi(strCopy.c_str());
2893 }
2894 else
2895 {
2896 std::vector<std::string> secs = StringUtils::Split(strCopy, ':');
2897 int timeInSecs = 0;
2898 for (unsigned int i = 0; i < 3 && i < secs.size(); i++)
2899 {
2900 timeInSecs *= 60;
2901 timeInSecs += atoi(secs[i].c_str());
2902 }
2903 return timeInSecs;
2904 }
2905 }
2906 //----------------------------------------------------------------------------
2907
2908 //============================================================================
2909 /// @brief Convert a time in seconds to a string based on the given time
2910 /// format.
2911 ///
2912 /// @param[in] seconds time in seconds
2913 /// @param[in] format [opt] The format we want the time in
2914 /// @return The formatted time
2915 ///
2916 /// @sa TIME_FORMAT
2917 ///
2918 ///
2919 /// --------------------------------------------------------------------------
2920 /// Example:
2921 /// ~~~~~~~~~~~~~{.cpp}
2922 /// #include <kodi/tools/StringUtils.h>
2923 ///
2924 /// std::string ref, var;
2925 ///
2926 /// ref = "21:30:55";
2927 /// var = kodi::tools::StringUtils::SecondsToTimeString(77455);
2928 /// EXPECT_STREQ(ref.c_str(), var.c_str());
2929 /// ~~~~~~~~~~~~~
2930 ///
2931 inline static std::string SecondsToTimeString(long seconds,
2932 TIME_FORMAT format = TIME_FORMAT_GUESS)
2933 {
2934 bool isNegative = seconds < 0;
2935 seconds = std::abs(seconds);
2936
2937 std::string strHMS;
2938 if (format == TIME_FORMAT_SECS)
2939 strHMS = StringUtils::Format("%i", seconds);
2940 else if (format == TIME_FORMAT_MINS)
2941 strHMS = StringUtils::Format("%i", lrintf(static_cast<float>(seconds) / 60.0f));
2942 else if (format == TIME_FORMAT_HOURS)
2943 strHMS = StringUtils::Format("%i", lrintf(static_cast<float>(seconds) / 3600.0f));
2944 else if (format & TIME_FORMAT_M)
2945 strHMS += StringUtils::Format("%i", seconds % 3600 / 60);
2946 else
2947 {
2948 int hh = seconds / 3600;
2949 seconds = seconds % 3600;
2950 int mm = seconds / 60;
2951 int ss = seconds % 60;
2952
2953 if (format == TIME_FORMAT_GUESS)
2954 format = (hh >= 1) ? TIME_FORMAT_HH_MM_SS : TIME_FORMAT_MM_SS;
2955 if (format & TIME_FORMAT_HH)
2956 strHMS += StringUtils::Format("%2.2i", hh);
2957 else if (format & TIME_FORMAT_H)
2958 strHMS += StringUtils::Format("%i", hh);
2959 if (format & TIME_FORMAT_MM)
2960 strHMS += StringUtils::Format(strHMS.empty() ? "%2.2i" : ":%2.2i", mm);
2961 if (format & TIME_FORMAT_SS)
2962 strHMS += StringUtils::Format(strHMS.empty() ? "%2.2i" : ":%2.2i", ss);
2963 }
2964
2965 if (isNegative)
2966 strHMS = "-" + strHMS;
2967
2968 return strHMS;
2969 }
2970 //----------------------------------------------------------------------------
2971
2972 //============================================================================
2973 /// @brief Converts a string in the format YYYYMMDD to the corresponding
2974 /// integer value.
2975 ///
2976 /// @param[in] dateString The date in the associated format, possible values
2977 /// are:
2978 /// - DD (for days only)
2979 /// - MM-DD (for days with month)
2980 /// - YYYY-MM-DD (for years, then month and last days)
2981 /// @return Corresponding integer, e.g. "2020-12-24" return as integer value
2982 /// 20201224
2983 ///
2984 ///
2985 /// --------------------------------------------------------------------------
2986 /// Example:
2987 /// ~~~~~~~~~~~~~{.cpp}
2988 /// #include <kodi/tools/StringUtils.h>
2989 ///
2990 /// int ref, var;
2991 ///
2992 /// ref = 20120706;
2993 /// var = kodi::tools::StringUtils::DateStringToYYYYMMDD("2012-07-06");
2994 /// EXPECT_EQ(ref, var);
2995 /// ~~~~~~~~~~~~~
2996 ///
2997 inline static int DateStringToYYYYMMDD(const std::string& dateString)
2998 {
2999 std::vector<std::string> days = StringUtils::Split(dateString, '-');
3000 if (days.size() == 1)
3001 return atoi(days[0].c_str());
3002 else if (days.size() == 2)
3003 return atoi(days[0].c_str()) * 100 + atoi(days[1].c_str());
3004 else if (days.size() == 3)
3005 return atoi(days[0].c_str()) * 10000 + atoi(days[1].c_str()) * 100 + atoi(days[2].c_str());
3006 else
3007 return -1;
3008 }
3009 //----------------------------------------------------------------------------
3010
3011 /*!@}*/
3012
3013private:
3014 inline static int compareWchar(const void* a, const void* b)
3015 {
3016 if (*static_cast<const wchar_t*>(a) < *static_cast<const wchar_t*>(b))
3017 return -1;
3018 else if (*static_cast<const wchar_t*>(a) > *static_cast<const wchar_t*>(b))
3019 return 1;
3020 return 0;
3021 }
3022
3023 inline static wchar_t tolowerUnicode(const wchar_t& c)
3024 {
3025 wchar_t* p =
3026 static_cast<wchar_t*>(bsearch(&c, unicode_uppers, sizeof(unicode_uppers) / sizeof(wchar_t),
3027 sizeof(wchar_t), compareWchar));
3028 if (p)
3029 return *(unicode_lowers + (p - unicode_uppers));
3030
3031 return c;
3032 }
3033
3034 inline static wchar_t toupperUnicode(const wchar_t& c)
3035 {
3036 wchar_t* p =
3037 static_cast<wchar_t*>(bsearch(&c, unicode_lowers, sizeof(unicode_lowers) / sizeof(wchar_t),
3038 sizeof(wchar_t), compareWchar));
3039 if (p)
3040 return *(unicode_uppers + (p - unicode_lowers));
3041
3042 return c;
3043 }
3044
3045 static uint32_t UTF8ToUnicode(const unsigned char* z, int nKey, unsigned char& bytes)
3046 {
3047 // Lookup table used decode the first byte of a multi-byte UTF8 character
3048 // clang-format off
3049 static const unsigned char utf8Trans1[] = {
3050 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
3051 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
3052 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
3053 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
3054 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
3055 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
3056 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
3057 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
3058 };
3059 // clang-format on
3060
3061 uint32_t c;
3062 bytes = 0;
3063 c = z[0];
3064 if (c >= 0xc0)
3065 {
3066 c = utf8Trans1[c - 0xc0];
3067 int index = 1;
3068 while (index < nKey && (z[index] & 0xc0) == 0x80)
3069 {
3070 c = (c << 6) + (0x3f & z[index]);
3071 index++;
3072 }
3073 if (c < 0x80 || (c & 0xFFFFF800) == 0xD800 || (c & 0xFFFFFFFE) == 0xFFFE)
3074 c = 0xFFFD;
3075 bytes = static_cast<unsigned char>(index - 1);
3076 }
3077 return c;
3078 }
3079};
3080///@}
3081//------------------------------------------------------------------------------
3082
3083} /* namespace tools */
3084} /* namespace kodi */
3085
3086#endif /* __cplusplus */