1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2 *
3 * Copyright (c) 2014-2015 Datalight, Inc.
4 * All Rights Reserved Worldwide.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; use version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /* Businesses and individuals that for commercial or other reasons cannot
21 * comply with the terms of the GPLv2 license may obtain a commercial license
22 * before incorporating Reliance Edge into proprietary software for
23 * distribution in any form. Visit http://www.datalight.com/reliance-edge for
24 * more information.
25 */
26
27 /** @file
28 * @brief Implements utilities that convert strings to numbers.
29 */
30 #include <redfs.h>
31 #include <redtestutils.h>
32
33
34 #define ISHEXDIGITU( c ) ( ( ( c ) >= 'A' ) && ( ( c ) <= 'F' ) )
35 #define ISHEXDIGITL( c ) ( ( ( c ) >= 'a' ) && ( ( c ) <= 'f' ) )
36 #define ISHEXDIGIT( c ) ( ISHEXDIGITL( c ) || ISHEXDIGITU( c ) )
37
38
39 /** @brief Converts an ASCII number into an int32_t.
40 *
41 * Converts all decimal digit numbers up to the end of the string or to the
42 * first non-numerical character.
43 *
44 * @note This function does *not* ignore leading white space.
45 *
46 * @param pszNum Pointer to a constant array of characters.
47 *
48 * @return The integer represented in the string.
49 */
RedAtoI(const char * pszNum)50 int32_t RedAtoI( const char * pszNum )
51 {
52 int32_t lValue = 0;
53 int32_t lNegative = 1;
54 uint32_t ulIdx = 0U;
55
56 if( pszNum[ ulIdx ] == '+' )
57 {
58 ulIdx++;
59 }
60 else if( pszNum[ ulIdx ] == '-' )
61 {
62 ulIdx++;
63 lNegative = -1;
64 }
65 else
66 {
67 /* No sign, implicitly positive.
68 */
69 }
70
71 while( ISDIGIT( pszNum[ ulIdx ] ) )
72 {
73 lValue *= 10;
74 lValue += pszNum[ ulIdx ] - '0';
75 ulIdx++;
76 }
77
78 lValue *= lNegative;
79
80 return lValue;
81 }
82
83
84 /** @brief Convert a hexadecimal ASCII number into a uint32_t value.
85 *
86 * The function processes all hex digits up to a NUL-terminator, or to the
87 * first non-hex character. Only hexadecimal digits are processed, so leading
88 * white space, or a leading "0x" prefix are not allowed.
89 *
90 * If pszNum points to an empty string (points to a NUL), this function will
91 * return NULL, and the value at *pulNum will not be modified.
92 *
93 * @note This function does not check for overflow. If there are more
94 * significant digits than can be represented in a uint32_t variable, the
95 * output is unspecified.
96 *
97 * @param pszNum A pointer to a constant array of hex characters.
98 * @param pulNum A pointer to the location in which to store the uint32_t
99 * result. Upon return, this value will be modified ONLY if
100 * the function succeeds and the returned pointer is valid (not
101 * NULL).
102 *
103 * @return A pointer to the byte following the converted number or NULL to
104 * indicate failure.
105 */
RedHtoUL(const char * pszNum,uint32_t * pulNum)106 const char * RedHtoUL( const char * pszNum,
107 uint32_t * pulNum )
108 {
109 uint64_t ullValue;
110 const char * pszReturn;
111
112 pszReturn = RedHtoULL( pszNum, &ullValue );
113
114 if( pszReturn != NULL )
115 {
116 if( ullValue < UINT32_MAX )
117 {
118 *pulNum = ( uint32_t ) ullValue;
119 }
120 else
121 {
122 pszReturn = NULL;
123 }
124 }
125
126 return pszReturn;
127 }
128
129
130 /** @brief Convert a hexadecimal ASCII number into a D_UINT64 value.
131 *
132 * The function processes all hex digits up to a NUL-terminator, or to the
133 * first non-hex character. Only hexadecimal digits are processed, so leading
134 * white space, or a leading "0x" prefix are not allowed.
135 *
136 * If pszNum points to an empty string (points to a NUL), this function will
137 * return NULL, and the value at *pulNum will not be modified.
138 *
139 * @note This function does not check for overflow. If there are more
140 * significant digits than can be represented in a uint64_t variable, the
141 * output is unspecified.
142 *
143 * @param pszNum A pointer to a constant array of hex characters.
144 * @param pullNum A pointer to the location in which to store the uint64_t
145 * result. Upon return, this value will be modified ONLY if
146 * the function succeeds and the returned pointer is valid (not
147 * NULL).
148 *
149 * @return A pointer to the byte following the converted number, or NULL to
150 * indicate failure.
151 */
RedHtoULL(const char * pszNum,uint64_t * pullNum)152 const char * RedHtoULL( const char * pszNum,
153 uint64_t * pullNum )
154 {
155 uint64_t ullValue = 0U;
156 const char * pszReturn = NULL;
157 uint32_t ulIdx = 0U;
158
159 REDASSERT( pszNum != NULL );
160 REDASSERT( pullNum != NULL );
161
162 while( pszNum[ ulIdx ] != '\0' )
163 {
164 char cDigit = pszNum[ ulIdx ];
165
166 if( ISDIGIT( cDigit ) )
167 {
168 cDigit -= '0';
169 }
170 else if( ISHEXDIGITU( cDigit ) )
171 {
172 cDigit -= ( 'A' - 10 );
173 }
174 else if( ISHEXDIGITL( cDigit ) )
175 {
176 cDigit -= ( 'a' - 10 );
177 }
178 else
179 {
180 break;
181 }
182
183 REDASSERT( ( ullValue & UINT64_SUFFIX( 0xF000000000000000 ) ) == 0U );
184
185 ullValue <<= 4U;
186 ullValue += cDigit;
187
188 ulIdx++;
189 pszReturn = &pszNum[ ulIdx ];
190 }
191
192 /* Modify the number returned only if we found one or more valid hex
193 * digits.
194 */
195 if( pszReturn != NULL )
196 {
197 *pullNum = ullValue;
198 }
199
200 return pszReturn;
201 }
202
203
204 /** @brief Convert the ASCII number to a uint32_t value.
205 *
206 * The number may be hex or decimal. Hex numbers must be prefixed by '0x', and
207 * they may be upper or lower case. The conversion process will stop with the
208 * first non hex or decimal digit.
209 *
210 * If the number is negative (the first character is a '-' sign), the value
211 * will be range checked and returned as the equivalent unsigned value.
212 *
213 * @note This function will NOT fail for numbers which exceed the size of a
214 * uint32_t value.
215 *
216 * @param pszNum A pointer to the ASCII number to convert
217 * @param pulNum A pointer to the uint32_t location to store the result.
218 * This value will be modified on return only if the function
219 * succeeds and the returned pointer is valid (not NULL).
220 *
221 * @return A pointer to the byte following the converted number, or NULL to
222 * indicate failure.
223 */
RedNtoUL(const char * pszNum,uint32_t * pulNum)224 const char * RedNtoUL( const char * pszNum,
225 uint32_t * pulNum )
226 {
227 bool fNegative = false;
228 uint32_t ulIdx = 0U;
229 const char * pszReturn;
230
231 REDASSERT( pszNum != NULL );
232 REDASSERT( pulNum != NULL );
233
234 if( pszNum[ ulIdx ] == '-' )
235 {
236 fNegative = true;
237 ulIdx++;
238 }
239
240 /* Hex numbers must be prefixed with '0x'.
241 */
242 if( ( pszNum[ ulIdx ] == '0' ) && ( ( pszNum[ ulIdx + 1U ] == 'x' ) || ( pszNum[ ulIdx + 1U ] == 'X' ) ) )
243 {
244 ulIdx += 2U;
245
246 if( ISDIGIT( pszNum[ ulIdx ] ) || ISHEXDIGIT( pszNum[ ulIdx ] ) )
247 {
248 pszReturn = RedHtoUL( &pszNum[ ulIdx ], pulNum );
249 }
250 else
251 {
252 pszReturn = NULL;
253 }
254 }
255 else if( ISDIGIT( pszNum[ ulIdx ] ) )
256 {
257 uint32_t ulTemp;
258
259 ulTemp = RedAtoI( &pszNum[ ulIdx ] );
260
261 while( ISDIGIT( pszNum[ ulIdx ] ) )
262 {
263 ulIdx++;
264 }
265
266 if( fNegative )
267 {
268 /* Fail if the number is out of range.
269 */
270 if( ulTemp > INT32_MAX )
271 {
272 pszReturn = NULL;
273 }
274 else
275 {
276 *pulNum = -( ( int32_t ) ulTemp );
277 pszReturn = &pszNum[ ulIdx ];
278 }
279 }
280 else
281 {
282 *pulNum = ulTemp;
283 pszReturn = &pszNum[ ulIdx ];
284 }
285 }
286 else
287 {
288 /* Return an error if there is not at least one hex or decimal digit.
289 */
290 pszReturn = NULL;
291 }
292
293 return pszReturn;
294 }
295
296
297 /** @brief Convert the ASCII number pointed to by pszNum to a uint64_t value.
298 *
299 * The number may be hex or decimal. Hex numbers must be prefixed by '0x', and
300 * they may be upper or lower case. The conversion process will stop with the
301 * first non hex or decimal digit.
302 *
303 * If the number is negative (the first character is a '-' sign), the value
304 * will be range checked and returned as the equivalent unsigned value.
305 *
306 * @param pszNum A pointer to the ASCII number to convert.
307 * @param pullNum A pointer to the uint64_t location to store the result.
308 * This value will be modified on return only if the function
309 * succeeds and the returned pointer is valid (not NULL).
310 *
311 * @return A pointer to the byte following the converted number, or NULL to
312 * indicate failure.
313 */
RedNtoULL(const char * pszNum,uint64_t * pullNum)314 const char * RedNtoULL( const char * pszNum,
315 uint64_t * pullNum )
316 {
317 bool fNegative = false;
318 uint32_t ulIdx = 0U;
319 const char * pszReturn;
320
321 REDASSERT( pszNum != NULL );
322 REDASSERT( pullNum != NULL );
323
324 if( pszNum[ ulIdx ] == '-' )
325 {
326 fNegative = true;
327 ulIdx++;
328 }
329
330 /* Hex numbers must be prefixed with '0x'.
331 */
332 if( ( pszNum[ ulIdx ] == '0' ) && ( ( pszNum[ ulIdx + 1U ] == 'x' ) || ( pszNum[ ulIdx + 1U ] == 'X' ) ) )
333 {
334 ulIdx += 2U;
335
336 if( ISDIGIT( pszNum[ ulIdx ] ) || ISHEXDIGIT( pszNum[ ulIdx ] ) )
337 {
338 pszReturn = RedHtoULL( &pszNum[ ulIdx ], pullNum );
339 }
340 else
341 {
342 pszReturn = NULL;
343 }
344 }
345 else if( ISDIGIT( pszNum[ ulIdx ] ) )
346 {
347 uint64_t ullTemp = 0U;
348
349 while( ISDIGIT( pszNum[ ulIdx ] ) )
350 {
351 ullTemp *= 10U;
352 ullTemp += pszNum[ ulIdx ] - '0';
353 ulIdx++;
354 }
355
356 if( fNegative )
357 {
358 /* Fail if the number is out of range.
359 */
360 if( ullTemp > INT64_MAX )
361 {
362 pszReturn = NULL;
363 }
364 else
365 {
366 *pullNum = ( uint64_t ) ( -( ( int64_t ) ullTemp ) );
367 pszReturn = &pszNum[ ulIdx ];
368 }
369 }
370 else
371 {
372 *pullNum = ullTemp;
373 pszReturn = &pszNum[ ulIdx ];
374 }
375 }
376 else
377 {
378 /* Return an error if there is not at least one hex or decimal digit.
379 */
380 pszReturn = NULL;
381 }
382
383 return pszReturn;
384 }
385
386
387 /** @brief Convert an ASCII hex or decimal number, which may may have a "B",
388 * "KB", or "MB" suffix (case insensitive), to a binary value.
389 *
390 * Hex numbers must be prefixed with "0x".
391 *
392 * @note If there is no postfix, KB is assumed!
393 *
394 * May fail due to bad formatting or overflow.
395 *
396 * @param pszNum A pointer to the ASCII number to convert.
397 * @param pulResult A pointer to a uint32_t in which to place the result.
398 *
399 * @return A pointer to the byte following the string, or NULL to indicate an
400 * error. In the event of an error, *pulResult will not be modified.
401 */
RedSizeToUL(const char * pszNum,uint32_t * pulResult)402 const char * RedSizeToUL( const char * pszNum,
403 uint32_t * pulResult )
404 {
405 uint32_t ulResult;
406 const char * pszSuffix;
407 const char * pszReturn;
408 uint32_t ulIdx = 0U;
409
410 REDASSERT( pszNum != NULL );
411 REDASSERT( pulResult != NULL );
412
413 /* Do the basic hex/decimal conversion
414 */
415 pszSuffix = RedNtoUL( pszNum, &ulResult );
416
417 if( pszSuffix != NULL )
418 {
419 if( ( pszSuffix[ ulIdx ] == 'B' ) || ( pszSuffix[ ulIdx ] == 'b' ) )
420 {
421 ulIdx++;
422 pszReturn = &pszSuffix[ ulIdx ];
423 }
424 else if( ( ( pszSuffix[ ulIdx ] == 'M' ) || ( pszSuffix[ ulIdx ] == 'm' ) ) &&
425 ( ( pszSuffix[ ulIdx + 1U ] == 'B' ) || ( pszSuffix[ ulIdx + 1U ] == 'b' ) ) )
426 {
427 ulIdx += 2U;
428
429 if( ulResult > ( UINT32_MAX / ( 1024U * 1024U ) ) )
430 {
431 pszReturn = NULL;
432 }
433 else
434 {
435 ulResult *= 1024U * 1024U;
436 pszReturn = &pszSuffix[ ulIdx ];
437 }
438 }
439 else
440 {
441 /* The number is either postfixed with "KB" or something
442 * else (we don't care), but we must increment the pointer
443 * if it is something recognize.
444 */
445 if( ( ( pszSuffix[ ulIdx ] == 'K' ) || ( pszSuffix[ ulIdx ] == 'k' ) ) &&
446 ( ( pszSuffix[ ulIdx + 1U ] == 'B' ) || ( pszSuffix[ ulIdx + 1U ] == 'b' ) ) )
447 {
448 ulIdx += 2U;
449 }
450
451 /* "B" or "MB" were not specified, so it must be "KB"
452 */
453 if( ulResult > ( UINT32_MAX / 1024U ) )
454 {
455 pszReturn = NULL;
456 }
457 else
458 {
459 ulResult *= 1024UL;
460 pszReturn = &pszSuffix[ ulIdx ];
461 }
462 }
463
464 if( pszReturn != NULL )
465 {
466 *pulResult = ulResult;
467 }
468 }
469 else
470 {
471 pszReturn = NULL;
472 }
473
474 return pszReturn;
475 }
476