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