Add int tests, fix bugs, add docs

Added tests for string to/from integers and added documentation.
While adding tests caught few bugs and shortcomings (now everything
works as expected)
This commit is contained in:
2025-03-26 09:06:14 +03:00
parent b7fc93a490
commit 4b2e3da567
3 changed files with 466 additions and 18 deletions

View File

@@ -5,85 +5,332 @@
#include "Common.h"
/**
* Frees dynamically allocated \a string.
*
* \param string String pointer
*/
void BH_StringFree(char *string);
/**
* Creates a copy of the input \a string.
*
* \param string String pointer
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringCopy(const char *string);
/**
* Formats a double \a value into a string using the provided \a format and
* \a precision.
*
* Formats supported:
* - Scientific or fixed format ('g' or 'G')
* - Scientific format ('e' or 'E')
* - Fixed format ('f' or 'F')
*
* If precision is negative, string will contain shortest representation of the
* double that can round-trip (i.e. converted back without information loss).
*
* This function follows IEEE 754 round to even to break ties during rounding.
*
* \param value Value
* \param format Format
* \param precision Precision
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromDouble(double value,
char format,
int precision);
/**
* Reads \a string containing double value in fixed or scientific format,
* optionally reports \a size amount of characters consumed and returns
* the result value.
*
* \param string String
* \param size Optional size
*
* \return On success, returns parsed double value.
* \return On failure, returns zero.
*/
double BH_StringToDouble(const char *string,
size_t *size);
/**
* Formats signed 8bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt8s(int8_t value,
int base);
char *BH_StringFromInt32s(int32_t value,
int base);
char *BH_StringFromInt64s(int64_t value,
int base);
/**
* Formats signed 16bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt16s(int16_t value,
int base);
/**
* Formats signed 32bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt32s(int32_t value,
int base);
/**
* Formats signed 64bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt64s(int64_t value,
int base);
/**
* Formats unsigned 8bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt8u(uint8_t value,
int base);
/**
* Formats unsigned 16bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt16u(uint16_t value,
int base);
/**
* Formats unsigned 32bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt32u(uint32_t value,
int base);
/**
* Formats unsigned 64bit \a value into string with the specified \a base.
*
* \param value Value
* \param base Base
*
* \return On success, returns new string pointer.
* \return On failure, returns NULL pointer.
*/
char *BH_StringFromInt64u(uint64_t value,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
int8_t BH_StringToInt8s(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
int16_t BH_StringToInt16s(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
int32_t BH_StringToInt32s(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
int64_t BH_StringToInt64s(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
uint8_t BH_StringToInt8u(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
uint16_t BH_StringToInt16u(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
uint32_t BH_StringToInt32u(const char *string,
size_t *size,
int base);
/**
* Reads \a string containing value in specified \a base, optionally reports
* \a size amount of characters consumed and returns the result value.
*
* If base is 0, function will automaticly detect input base by the prefix:
* - Base 8 if prefix is 0
* - Base 16 if prefix is 0x
* - Base 10 in other cases
*
* \param string String
* \param size Optional size
* \param base Base
*
* \return On success, returns parsed value.
* \return On failure, returns zero.
*/
uint64_t BH_StringToInt64u(const char *string,
size_t *size,
int base);

View File

@@ -47,36 +47,36 @@ static void guessBase(const char **string,
size_t *size,
int *base)
{
if (*base != 0)
if (*base != 0 && *base != 2 && *base != 8 && *base != 16)
return;
*base = 10;
if (**string != '0')
{
if (*base == 0)
*base = 10;
return;
}
(*string)++;
if (size)
(*size)++;
switch (**string)
if ((**string == 'x' || **string == 'X') && (*base == 0 || *base == 16))
{
case 'x': case 'X':
*base = 16;
(*string)++;
if (size)
(*size)++;
break;
case 'b': case 'B':
}
else if ((**string == 'b' || **string == 'B') && (*base == 0 || *base == 2))
{
*base = 2;
(*string)++;
if (size)
(*size)++;
break;
default:
*base = 8;
}
else if ((*base == 0 || *base == 8))
*base = 8;
}
@@ -158,7 +158,7 @@ char *BH_StringFromInt64u(uint64_t value,
#undef TEMPLATE_IMPL
#define TEMPLATE_IMPL(type) \
type result = 0; int sign, flag = 0; char sym; *size = 0; \
type result = 0; int sign, flag = 0; char sym; if (size) *size = 0; \
if (base != 0 && (base < 2 || base > 36)) { return 0; } \
skipSpace(&string, size); handleSign(&string, size, &sign); \
guessBase(&string, size, &base); while(*string) { sym = *(string++); \

201
test/src/TestInt.c Normal file
View File

@@ -0,0 +1,201 @@
#include <BH/String.h>
#include <BH/Unit.h>
BH_UNIT_TEST(Int8)
{
char *str;
size_t size;
BH_VERIFY(str = BH_StringFromInt8s(-13, 16));
BH_VERIFY(BH_StringToInt8s(str, &size, 16) == -13);
BH_VERIFY(size == 2);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8s(-13, 10));
BH_VERIFY(BH_StringToInt8s(str, &size, 10) == -13);
BH_VERIFY(size == 3);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8s(-13, 8));
BH_VERIFY(BH_StringToInt8s(str, &size, 8) == -13);
BH_VERIFY(size == 3);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8s(-13, 2));
BH_VERIFY(BH_StringToInt8s(str, &size, 2) == -13);
BH_VERIFY(size == 5);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8u(200, 16));
BH_VERIFY(BH_StringToInt8u(str, &size, 16) == 200);
BH_VERIFY(size == 2);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8u(200, 10));
BH_VERIFY(BH_StringToInt8u(str, &size, 10) == 200);
BH_VERIFY(size == 3);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8u(200, 8));
BH_VERIFY(BH_StringToInt8u(str, &size, 8) == 200);
BH_VERIFY(size == 3);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt8u(200, 2));
BH_VERIFY(BH_StringToInt8u(str, &size, 2) == 200);
BH_VERIFY(size == 8);
BH_StringFree(str);
return 0;
}
BH_UNIT_TEST(Int16)
{
char *str;
size_t size;
BH_VERIFY(str = BH_StringFromInt16s(-1234, 16));
BH_VERIFY(BH_StringToInt16s(str, &size, 16) == -1234);
BH_VERIFY(size == 4);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16s(-1234, 10));
BH_VERIFY(BH_StringToInt16s(str, &size, 10) == -1234);
BH_VERIFY(size == 5);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16s(-1234, 8));
BH_VERIFY(BH_StringToInt16s(str, &size, 8) == -1234);
BH_VERIFY(size == 5);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16s(-1234, 2));
BH_VERIFY(BH_StringToInt16s(str, &size, 2) == -1234);
BH_VERIFY(size == 12);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16u(43210, 16));
BH_VERIFY(BH_StringToInt16u(str, &size, 16) == 43210);
BH_VERIFY(size == 4);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16u(43210, 10));
BH_VERIFY(BH_StringToInt16u(str, &size, 10) == 43210);
BH_VERIFY(size == 5);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16u(43210, 8));
BH_VERIFY(BH_StringToInt16u(str, &size, 8) == 43210);
BH_VERIFY(size == 6);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt16u(43210, 2));
BH_VERIFY(BH_StringToInt16u(str, &size, 2) == 43210);
BH_VERIFY(size == 16);
BH_StringFree(str);
return 0;
}
BH_UNIT_TEST(Int32)
{
char *str;
size_t size;
BH_VERIFY(str = BH_StringFromInt32s(-1234567890l, 16));
BH_VERIFY(BH_StringToInt32s(str, &size, 16) == -1234567890l);
BH_VERIFY(size == 9);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32s(-1234567890l, 10));
BH_VERIFY(BH_StringToInt32s(str, &size, 10) == -1234567890l);
BH_VERIFY(size == 11);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32s(-1234567890l, 8));
BH_VERIFY(BH_StringToInt32s(str, &size, 8) == -1234567890l);
BH_VERIFY(size == 12);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32s(-1234567890l, 2));
BH_VERIFY(BH_StringToInt32s(str, &size, 2) == -1234567890l);
BH_VERIFY(size == 32);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32u(3456789012ul, 16));
BH_VERIFY(BH_StringToInt32u(str, &size, 16) == 3456789012ul);
BH_VERIFY(size == 8);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32u(3456789012ul, 10));
BH_VERIFY(BH_StringToInt32u(str, &size, 10) == 3456789012ul);
BH_VERIFY(size == 10);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32u(3456789012ul, 8));
BH_VERIFY(BH_StringToInt32u(str, &size, 8) == 3456789012ul);
BH_VERIFY(size == 11);
BH_StringFree(str);
BH_VERIFY(str = BH_StringFromInt32u(3456789012ul, 2));
BH_VERIFY(BH_StringToInt32u(str, &size, 2) == 3456789012ul);
BH_VERIFY(size == 32);
BH_StringFree(str);
return 0;
}
BH_UNIT_TEST(Parsing)
{
size_t size;
BH_VERIFY(BH_StringToInt32s(" 0x1234", &size, 16) == 0x1234);
BH_VERIFY(size == 9);
BH_VERIFY(BH_StringToInt32s(" 12345", &size, 10) == 12345);
BH_VERIFY(size == 8);
BH_VERIFY(BH_StringToInt32s(" 0123", &size, 8) == 0123);
BH_VERIFY(size == 7);
BH_VERIFY(BH_StringToInt32s(" 0b1111Hello", &size, 2) == 15);
BH_VERIFY(size == 9);
BH_VERIFY(BH_StringToInt32s(" 0x1234", &size, 0) == 0x1234);
BH_VERIFY(size == 9);
BH_VERIFY(BH_StringToInt32s(" 0123", &size, 0) == 0123);
BH_VERIFY(size == 7);
BH_VERIFY(BH_StringToInt32s(" 123", &size, 0) == 123);
BH_VERIFY(size == 6);
BH_VERIFY(BH_StringToInt32s(" 0b1111", &size, 0) == 15);
BH_VERIFY(size == 9);
BH_VERIFY(BH_StringToInt32s(" 12345Hello", &size, 10) == 12345);
BH_VERIFY(size == 8);
BH_VERIFY(BH_StringToInt32s(" Hello", &size, 10) == 0);
BH_VERIFY(size == 0);
return 0;
}
int main(int argc, char **argv)
{
BH_UNUSED(argc);
BH_UNUSED(argv);
BH_UNIT_ADD(Int8);
BH_UNIT_ADD(Int16);
BH_UNIT_ADD(Int32);
BH_UNIT_ADD(Parsing);
return BH_UnitRun();
}