/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <---- | |
Copyright (c) 2014-2015 Datalight, Inc. | |
All Rights Reserved Worldwide. | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; use version 2 of the License. | |
This program is distributed in the hope that it will be useful, | |
but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty | |
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License along | |
with this program; if not, write to the Free Software Foundation, Inc., | |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
*/ | |
/* Businesses and individuals that for commercial or other reasons cannot | |
comply with the terms of the GPLv2 license may obtain a commercial license | |
before incorporating Reliance Edge into proprietary software for | |
distribution in any form. Visit http://www.datalight.com/reliance-edge for | |
more information. | |
*/ | |
/** @file | |
@brief Implements utilities that convert strings to numbers. | |
*/ | |
#include <redfs.h> | |
#include <redtestutils.h> | |
#define ISHEXDIGITU(c) (((c) >= 'A') && ((c) <= 'F')) | |
#define ISHEXDIGITL(c) (((c) >= 'a') && ((c) <= 'f')) | |
#define ISHEXDIGIT(c) (ISHEXDIGITL(c) || ISHEXDIGITU(c)) | |
/** @brief Converts an ASCII number into an int32_t. | |
Converts all decimal digit numbers up to the end of the string or to the | |
first non-numerical character. | |
@note This function does *not* ignore leading white space. | |
@param pszNum Pointer to a constant array of characters. | |
@return The integer represented in the string. | |
*/ | |
int32_t RedAtoI( | |
const char *pszNum) | |
{ | |
int32_t lValue = 0; | |
int32_t lNegative = 1; | |
uint32_t ulIdx = 0U; | |
if(pszNum[ulIdx] == '+') | |
{ | |
ulIdx++; | |
} | |
else if(pszNum[ulIdx] == '-') | |
{ | |
ulIdx++; | |
lNegative = -1; | |
} | |
else | |
{ | |
/* No sign, implicitly positive. | |
*/ | |
} | |
while(ISDIGIT(pszNum[ulIdx])) | |
{ | |
lValue *= 10; | |
lValue += pszNum[ulIdx] - '0'; | |
ulIdx++; | |
} | |
lValue *= lNegative; | |
return lValue; | |
} | |
/** @brief Convert a hexadecimal ASCII number into a uint32_t value. | |
The function processes all hex digits up to a NUL-terminator, or to the | |
first non-hex character. Only hexadecimal digits are processed, so leading | |
white space, or a leading "0x" prefix are not allowed. | |
If pachNum points to an empty string (points to a NUL), this function will | |
return NULL, and the value at *pulNum will not be modified. | |
@note This function does not check for overflow. If there are more | |
significant digits than can be represented in a uint32_t variable, the | |
output is unspecified. | |
@param pachNum A pointer to a constant array of hex characters. | |
@param pulNum A pointer to the location in which to store the uint32_t | |
result. Upon return, this value will be modified ONLY if | |
the function succeeds and the returned pointer is valid (not | |
NULL). | |
@return A pointer to the byte following the converted number or NULL to | |
indicate failure. | |
*/ | |
const char *RedHtoUL( | |
const char *pszNum, | |
uint32_t *pulNum) | |
{ | |
uint64_t ullValue; | |
const char *pszReturn; | |
pszReturn = RedHtoULL(pszNum, &ullValue); | |
if(pszReturn != NULL) | |
{ | |
if(ullValue < UINT32_MAX) | |
{ | |
*pulNum = (uint32_t)ullValue; | |
} | |
else | |
{ | |
pszReturn = NULL; | |
} | |
} | |
return pszReturn; | |
} | |
/** @brief Convert a hexadecimal ASCII number into a D_UINT64 value. | |
The function processes all hex digits up to a NUL-terminator, or to the | |
first non-hex character. Only hexadecimal digits are processed, so leading | |
white space, or a leading "0x" prefix are not allowed. | |
If pachNum points to an empty string (points to a NUL), this function will | |
return NULL, and the value at *pulNum will not be modified. | |
@note This function does not check for overflow. If there are more | |
significant digits than can be represented in a uint64_t variable, the | |
output is unspecified. | |
@param pszNum A pointer to a constant array of hex characters. | |
@param pullNum A pointer to the location in which to store the uint64_t | |
result. Upon return, this value will be modified ONLY if | |
the function succeeds and the returned pointer is valid (not | |
NULL). | |
@return A pointer to the byte following the converted number, or NULL to | |
indicate failure. | |
*/ | |
const char *RedHtoULL( | |
const char *pszNum, | |
uint64_t *pullNum) | |
{ | |
uint64_t ullValue = 0U; | |
const char *pszReturn = NULL; | |
uint32_t ulIdx = 0U; | |
REDASSERT(pszNum != NULL); | |
REDASSERT(pullNum != NULL); | |
while(pszNum[ulIdx] != '\0') | |
{ | |
char cDigit = pszNum[ulIdx]; | |
if(ISDIGIT(cDigit)) | |
{ | |
cDigit -= '0'; | |
} | |
else if(ISHEXDIGITU(cDigit)) | |
{ | |
cDigit -= ('A' - 10); | |
} | |
else if(ISHEXDIGITL(cDigit)) | |
{ | |
cDigit -= ('a' - 10); | |
} | |
else | |
{ | |
break; | |
} | |
REDASSERT((ullValue & UINT64_SUFFIX(0xF000000000000000)) == 0U); | |
ullValue <<= 4U; | |
ullValue += cDigit; | |
ulIdx++; | |
pszReturn = &pszNum[ulIdx]; | |
} | |
/* Modify the number returned only if we found one or more valid hex | |
digits. | |
*/ | |
if(pszReturn != NULL) | |
{ | |
*pullNum = ullValue; | |
} | |
return pszReturn; | |
} | |
/** @brief Convert the ASCII number to a uint32_t value. | |
The number may be hex or decimal. Hex numbers must be prefixed by '0x', and | |
they may be upper or lower case. The conversion process will stop with the | |
first non hex or decimal digit. | |
If the number is negative (the first character is a '-' sign), the value | |
will be range checked and returned as the equivalent unsigned value. | |
@note This function will NOT fail for numbers which exceed the size of a | |
uint32_t value. | |
@param pszNum A pointer to the ASCII number to convert | |
@param pulNum A pointer to the uint32_t location to store the result. | |
This value will be modified on return only if the function | |
succeeds and the returned pointer is valid (not NULL). | |
@return A pointer to the byte following the converted number, or NULL to | |
indicate failure. | |
*/ | |
const char *RedNtoUL( | |
const char *pszNum, | |
uint32_t *pulNum) | |
{ | |
bool fNegative = false; | |
uint32_t ulIdx = 0U; | |
const char *pszReturn; | |
REDASSERT(pszNum != NULL); | |
REDASSERT(pulNum != NULL); | |
if(pszNum[ulIdx] == '-') | |
{ | |
fNegative = true; | |
ulIdx++; | |
} | |
/* Hex numbers must be prefixed with '0x'. | |
*/ | |
if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X'))) | |
{ | |
ulIdx += 2U; | |
if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx])) | |
{ | |
pszReturn = RedHtoUL(&pszNum[ulIdx], pulNum); | |
} | |
else | |
{ | |
pszReturn = NULL; | |
} | |
} | |
else if(ISDIGIT(pszNum[ulIdx])) | |
{ | |
uint32_t ulTemp; | |
ulTemp = RedAtoI(&pszNum[ulIdx]); | |
while(ISDIGIT(pszNum[ulIdx])) | |
{ | |
ulIdx++; | |
} | |
if(fNegative) | |
{ | |
/* Fail if the number is out of range. | |
*/ | |
if(ulTemp > INT32_MAX) | |
{ | |
pszReturn = NULL; | |
} | |
else | |
{ | |
*pulNum = -((int32_t)ulTemp); | |
pszReturn = &pszNum[ulIdx]; | |
} | |
} | |
else | |
{ | |
*pulNum = ulTemp; | |
pszReturn = &pszNum[ulIdx]; | |
} | |
} | |
else | |
{ | |
/* Return an error if there is not at least one hex or decimal digit. | |
*/ | |
pszReturn = NULL; | |
} | |
return pszReturn; | |
} | |
/** @brief Convert the ASCII number pointed to by pachNum to a uint64_t value. | |
The number may be hex or decimal. Hex numbers must be prefixed by '0x', and | |
they may be upper or lower case. The conversion process will stop with the | |
first non hex or decimal digit. | |
If the number is negative (the first character is a '-' sign), the value | |
will be range checked and returned as the equivalent unsigned value. | |
@param pszNum A pointer to the ASCII number to convert. | |
@param pullNum A pointer to the uint64_t location to store the result. | |
This value will be modified on return only if the function | |
succeeds and the returned pointer is valid (not NULL). | |
@return A pointer to the byte following the converted number, or NULL to | |
indicate failure. | |
*/ | |
const char *RedNtoULL( | |
const char *pszNum, | |
uint64_t *pullNum) | |
{ | |
bool fNegative = false; | |
uint32_t ulIdx = 0U; | |
const char *pszReturn; | |
REDASSERT(pszNum != NULL); | |
REDASSERT(pullNum != NULL); | |
if(pszNum[ulIdx] == '-') | |
{ | |
fNegative = true; | |
ulIdx++; | |
} | |
/* Hex numbers must be prefixed with '0x'. | |
*/ | |
if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X'))) | |
{ | |
ulIdx += 2U; | |
if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx])) | |
{ | |
pszReturn = RedHtoULL(&pszNum[ulIdx], pullNum); | |
} | |
else | |
{ | |
pszReturn = NULL; | |
} | |
} | |
else if(ISDIGIT(pszNum[ulIdx])) | |
{ | |
uint64_t ullTemp = 0U; | |
while(ISDIGIT(pszNum[ulIdx])) | |
{ | |
ullTemp *= 10U; | |
ullTemp += pszNum[ulIdx] - '0'; | |
ulIdx++; | |
} | |
if(fNegative) | |
{ | |
/* Fail if the number is out of range. | |
*/ | |
if(ullTemp > INT64_MAX) | |
{ | |
pszReturn = NULL; | |
} | |
else | |
{ | |
*pullNum = (uint64_t)(-((int64_t)ullTemp)); | |
pszReturn = &pszNum[ulIdx]; | |
} | |
} | |
else | |
{ | |
*pullNum = ullTemp; | |
pszReturn = &pszNum[ulIdx]; | |
} | |
} | |
else | |
{ | |
/* Return an error if there is not at least one hex or decimal digit. | |
*/ | |
pszReturn = NULL; | |
} | |
return pszReturn; | |
} | |
/** @brief Convert an ASCII hex or decimal number, which may may have a "B", | |
"KB", or "MB" suffix (case insensitive), to a binary value. | |
Hex numbers must be prefixed with "0x". | |
@note If there is no postfix, KB is assumed! | |
May fail due to bad formatting or overflow. | |
@param pszNum A pointer to the ASCII number to convert. | |
@param pulResult A pointer to a uint32_t in which to place the result. | |
@return A pointer to the byte following the string, or NULL to indicate an | |
error. In the event of an error, *pulResult will not be modified. | |
*/ | |
const char *RedSizeToUL( | |
const char *pszNum, | |
uint32_t *pulResult) | |
{ | |
uint32_t ulResult; | |
const char *pszSuffix; | |
const char *pszReturn; | |
uint32_t ulIdx = 0U; | |
REDASSERT(pszNum != NULL); | |
REDASSERT(pulResult != NULL); | |
/* Do the basic hex/decimal conversion | |
*/ | |
pszSuffix = RedNtoUL(pszNum, &ulResult); | |
if(pszSuffix != NULL) | |
{ | |
if((pszSuffix[ulIdx] == 'B') || (pszSuffix[ulIdx] == 'b')) | |
{ | |
ulIdx++; | |
pszReturn = &pszSuffix[ulIdx]; | |
} | |
else if( ((pszSuffix[ulIdx] == 'M') || (pszSuffix[ulIdx] == 'm')) | |
&& ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b'))) | |
{ | |
ulIdx += 2U; | |
if(ulResult > (UINT32_MAX / (1024U * 1024U))) | |
{ | |
pszReturn = NULL; | |
} | |
else | |
{ | |
ulResult *= 1024U * 1024U; | |
pszReturn = &pszSuffix[ulIdx]; | |
} | |
} | |
else | |
{ | |
/* The number is either postfixed with "KB" or something | |
else (we don't care), but we must increment the pointer | |
if it is something recognize. | |
*/ | |
if( ((pszSuffix[ulIdx] == 'K') || (pszSuffix[ulIdx] == 'k')) | |
&& ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b'))) | |
{ | |
ulIdx += 2U; | |
} | |
/* "B" or "MB" were not specified, so it must be "KB" | |
*/ | |
if(ulResult > (UINT32_MAX / 1024U)) | |
{ | |
pszReturn = NULL; | |
} | |
else | |
{ | |
ulResult *= 1024UL; | |
pszReturn = &pszSuffix[ulIdx]; | |
} | |
} | |
if(pszReturn != NULL) | |
{ | |
*pulResult = ulResult; | |
} | |
} | |
else | |
{ | |
pszReturn = NULL; | |
} | |
return pszReturn; | |
} | |