| /* ----> 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 pszNum 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; |
| } |
| |