1 /*
2 * Copyright (C) 2010 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include "StringHelper.h"
29
30
31 // This is basically strtoull(), but it needs to be explicitly implemented
32 // since some systems lack it. (Also, compiling with GNU C++ in ANSI mode
33 // does not work with strtoull.)
ParseNumber(const char * str,bool & error)34 uint64_t StringHelper::ParseNumber(const char* str, bool& error)
35 {
36 bool baseSet = false;
37 int base = 10;
38 uint64_t result = 0;
39 bool negative = false;
40
41 error = false;
42
43 if (str == NULL)
44 return 0;
45
46 while (*str == ' ')
47 ++str;
48
49 if (*str == '-') {
50 negative = true;
51 ++str;
52 }
53
54 while ((*str == 'x' || *str == 'X') || (*str >= '0' && *str <= '9')
55 || (*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F')) {
56 char c = *str;
57
58 if (c == 'x' || c == 'X') {
59 // Multiple base selections are not allowed.
60 if (baseSet)
61 break;
62
63 // Only 0 prefix before base selection is allowed,
64 // no other values.
65 if (result != 0)
66 break;
67
68 base = 16;
69 baseSet = true;
70 } else {
71 if (base == 10 && (c < '0' || c > '9'))
72 break;
73
74 int n = c - '0';
75 if (c >= 'a' && c <= 'f')
76 n = *str - 'a' + 10;
77 if (c >= 'A' && c <= 'F')
78 n = *str - 'A' + 10;
79
80 if (base == 16 && (n < 0 || n > 15))
81 break;
82
83 result = result * base + n;
84 }
85
86 ++str;
87 }
88
89 if (*str)
90 error = true;
91
92 if (negative)
93 return -result;
94 else
95 return result;
96 }
97
98
SplitStringIntoVector(const string & str,const char splitter)99 vector<string> StringHelper::SplitStringIntoVector(const string &str, const char splitter)
100 {
101 // This is slow and hackish, but works.
102 vector<string> strings;
103 string word;
104 bool lastWasSplitter = false;
105
106 for (size_t i=0, n=str.length(); i<n; i++) {
107 char ch = str[i];
108 if (ch == splitter) {
109 strings.push_back(word);
110 word = "";
111 lastWasSplitter = true;
112 } else {
113 word += ch;
114 lastWasSplitter = false;
115 }
116 }
117
118 if (word != "" || lastWasSplitter)
119 strings.push_back(word);
120
121 return strings;
122 }
123
124
125 /*****************************************************************************/
126
127
128 #ifdef WITHUNITTESTS
129
Test_StringHelper_ParseNumber_Simple()130 static void Test_StringHelper_ParseNumber_Simple()
131 {
132 string s = "42";
133 bool error = true;
134
135 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
136
137 UnitTest::Assert("Should have succeeded with no error", error == false);
138 UnitTest::Assert("Unexpected resulting value", value, 42);
139 }
140
Test_StringHelper_ParseNumber_SimpleError()141 static void Test_StringHelper_ParseNumber_SimpleError()
142 {
143 string s = "Q42";
144 bool error = false;
145
146 StringHelper::ParseNumber(s.c_str(), error);
147
148 UnitTest::Assert("Should have resulted in error", error == true);
149 }
150
Test_StringHelper_ParseNumber_Negative()151 static void Test_StringHelper_ParseNumber_Negative()
152 {
153 string s = "-42";
154 bool error = true;
155
156 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
157
158 UnitTest::Assert("Should have succeeded with no error", error == false);
159 UnitTest::Assert("Unexpected resulting value", value, (uint64_t) -42);
160 }
161
Test_StringHelper_ParseNumber_LeadingSpaces()162 static void Test_StringHelper_ParseNumber_LeadingSpaces()
163 {
164 string s = " 42";
165 bool error = true;
166
167 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
168
169 UnitTest::Assert("Should have succeeded with no error", error == false);
170 UnitTest::Assert("Unexpected resulting value", value, 42);
171 }
172
Test_StringHelper_ParseNumber_LeadingSpacesAndNegative()173 static void Test_StringHelper_ParseNumber_LeadingSpacesAndNegative()
174 {
175 string s = " -42";
176 bool error = true;
177
178 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
179
180 UnitTest::Assert("Should have succeeded with no error", error == false);
181 UnitTest::Assert("Unexpected resulting value", value, (uint64_t) -42);
182 }
183
Test_StringHelper_ParseNumber_LeadingSpacesAndErrorNegative()184 static void Test_StringHelper_ParseNumber_LeadingSpacesAndErrorNegative()
185 {
186 string s = " - 42";
187 bool error = false;
188
189 StringHelper::ParseNumber(s.c_str(), error);
190
191 UnitTest::Assert("Should have resulted in error", error == true);
192 }
193
Test_StringHelper_ParseNumber_SimpleHexLowerCase()194 static void Test_StringHelper_ParseNumber_SimpleHexLowerCase()
195 {
196 string s = "0x42a";
197 bool error = true;
198
199 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
200
201 UnitTest::Assert("Should have succeeded with no error", error == false);
202 UnitTest::Assert("Unexpected resulting value", value, 0x42a);
203 }
204
Test_StringHelper_ParseNumber_SimpleHexUpperCase()205 static void Test_StringHelper_ParseNumber_SimpleHexUpperCase()
206 {
207 string s = "0X42A";
208 bool error = true;
209
210 uint64_t value = StringHelper::ParseNumber(s.c_str(), error);
211
212 UnitTest::Assert("Should have succeeded with no error", error == false);
213 UnitTest::Assert("Unexpected resulting value", value, 0x42a);
214 }
215
Test_StringHelper_ParseNumber_HexErrorDoubleX()216 static void Test_StringHelper_ParseNumber_HexErrorDoubleX()
217 {
218 string s = "0xx42";
219 bool error = false;
220
221 StringHelper::ParseNumber(s.c_str(), error);
222
223 UnitTest::Assert("Should have resulted in error", error == true);
224 }
225
Test_StringHelper_ParseNumber_HexErrorNonZeroPrefix()226 static void Test_StringHelper_ParseNumber_HexErrorNonZeroPrefix()
227 {
228 string s = "04x42";
229 bool error = false;
230
231 StringHelper::ParseNumber(s.c_str(), error);
232
233 UnitTest::Assert("Should have resulted in error", error == true);
234 }
235
Test_StringHelper_ParseNumber_NumberFollowedByErrorValidHexChar()236 static void Test_StringHelper_ParseNumber_NumberFollowedByErrorValidHexChar()
237 {
238 string s = "42A"; // Note: A is a valid hex char
239 bool error = false;
240
241 StringHelper::ParseNumber(s.c_str(), error);
242
243 UnitTest::Assert("Should have resulted in error", error == true);
244 }
245
Test_StringHelper_ParseNumber_NumberFollowedByError()246 static void Test_StringHelper_ParseNumber_NumberFollowedByError()
247 {
248 string s = "42Q";
249 bool error = false;
250
251 StringHelper::ParseNumber(s.c_str(), error);
252
253 UnitTest::Assert("Should have resulted in error", error == true);
254 }
255
Test_StringHelper_SplitStringIntoVector_Simple()256 static void Test_StringHelper_SplitStringIntoVector_Simple()
257 {
258 vector<string> v = StringHelper::SplitStringIntoVector("A:B:C", ':');
259
260 UnitTest::Assert("Wrong number of strings?", v.size(), 3);
261 UnitTest::Assert("Wrong string contents?", v[0], "A");
262 UnitTest::Assert("Wrong string contents?", v[1], "B");
263 UnitTest::Assert("Wrong string contents?", v[2], "C");
264 }
265
Test_StringHelper_SplitStringIntoVector_EmptyInput()266 static void Test_StringHelper_SplitStringIntoVector_EmptyInput()
267 {
268 vector<string> v = StringHelper::SplitStringIntoVector("", ':');
269
270 UnitTest::Assert("Wrong number of strings?", v.size(), 0);
271 }
272
Test_StringHelper_SplitStringIntoVector_Simple2()273 static void Test_StringHelper_SplitStringIntoVector_Simple2()
274 {
275 vector<string> v = StringHelper::SplitStringIntoVector("A:B:C", 'B');
276
277 UnitTest::Assert("Wrong number of strings?", v.size(), 2);
278 UnitTest::Assert("Wrong string contents?", v[0], "A:");
279 UnitTest::Assert("Wrong string contents?", v[1], ":C");
280 }
281
Test_StringHelper_SplitStringIntoVector_WithZeroLengthParts()282 static void Test_StringHelper_SplitStringIntoVector_WithZeroLengthParts()
283 {
284 vector<string> v = StringHelper::SplitStringIntoVector("A::B:::C", ':');
285
286 UnitTest::Assert("Wrong number of strings?", v.size(), 6);
287 UnitTest::Assert("Wrong string contents?", v[0], "A");
288 UnitTest::Assert("Wrong string contents?", v[1], "");
289 UnitTest::Assert("Wrong string contents?", v[2], "B");
290 UnitTest::Assert("Wrong string contents?", v[3], "");
291 UnitTest::Assert("Wrong string contents?", v[4], "");
292 UnitTest::Assert("Wrong string contents?", v[5], "C");
293 }
294
Test_StringHelper_SplitStringIntoVector_WithTrailingZeroLengthParts()295 static void Test_StringHelper_SplitStringIntoVector_WithTrailingZeroLengthParts()
296 {
297 vector<string> v = StringHelper::SplitStringIntoVector("A::", ':');
298
299 UnitTest::Assert("Wrong number of strings?", v.size(), 3);
300 UnitTest::Assert("Wrong string contents?", v[0], "A");
301 UnitTest::Assert("Wrong string contents?", v[1], "");
302 UnitTest::Assert("Wrong string contents?", v[2], "");
303 }
304
Test_StringHelper_SplitStringIntoVector_WithHeadingZeroLengthParts()305 static void Test_StringHelper_SplitStringIntoVector_WithHeadingZeroLengthParts()
306 {
307 vector<string> v = StringHelper::SplitStringIntoVector("A::", 'A');
308
309 UnitTest::Assert("Wrong number of strings?", v.size(), 2);
310 UnitTest::Assert("Wrong string contents?", v[0], "");
311 UnitTest::Assert("Wrong string contents?", v[1], "::");
312 }
313
UNITTESTS(StringHelper)314 UNITTESTS(StringHelper)
315 {
316 UNITTEST(Test_StringHelper_ParseNumber_Simple);
317 UNITTEST(Test_StringHelper_ParseNumber_SimpleError);
318 UNITTEST(Test_StringHelper_ParseNumber_Negative);
319 UNITTEST(Test_StringHelper_ParseNumber_LeadingSpaces);
320 UNITTEST(Test_StringHelper_ParseNumber_LeadingSpacesAndNegative);
321 UNITTEST(Test_StringHelper_ParseNumber_LeadingSpacesAndErrorNegative);
322 UNITTEST(Test_StringHelper_ParseNumber_SimpleHexLowerCase);
323 UNITTEST(Test_StringHelper_ParseNumber_SimpleHexUpperCase);
324 UNITTEST(Test_StringHelper_ParseNumber_HexErrorDoubleX);
325 UNITTEST(Test_StringHelper_ParseNumber_HexErrorNonZeroPrefix);
326 UNITTEST(Test_StringHelper_ParseNumber_NumberFollowedByErrorValidHexChar);
327 UNITTEST(Test_StringHelper_ParseNumber_NumberFollowedByError);
328
329 UNITTEST(Test_StringHelper_SplitStringIntoVector_Simple);
330 UNITTEST(Test_StringHelper_SplitStringIntoVector_EmptyInput);
331 UNITTEST(Test_StringHelper_SplitStringIntoVector_Simple2);
332 UNITTEST(Test_StringHelper_SplitStringIntoVector_WithZeroLengthParts);
333 UNITTEST(Test_StringHelper_SplitStringIntoVector_WithTrailingZeroLengthParts);
334 UNITTEST(Test_StringHelper_SplitStringIntoVector_WithHeadingZeroLengthParts);
335 }
336
337 #endif
338
339