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