diff --git a/inc/private/dmod_prf.h b/inc/private/dmod_prf.h index c3fd935..6008d13 100644 --- a/inc/private/dmod_prf.h +++ b/inc/private/dmod_prf.h @@ -62,13 +62,18 @@ extern "C" { * - %u: unsigned integer * - %x: hexadecimal (lowercase) * - %X: hexadecimal (uppercase) + * - %o: octal * - %p: pointer * - %%: literal % * * Supported format modifiers: * - Width: Minimum field width (e.g., %30s for 30 characters) * - Left-align: '-' flag for left-justification (e.g., %-30s) - * - Length modifier 'll': long long (64-bit) for d, i, u, x, X (e.g., %lld, %llu, %llx, %llX) + * - Length modifier 'hh': char (8-bit) for d, i, u, x, X, o (e.g., %hhd, %hhu) + * - Length modifier 'h': short (16-bit) for d, i, u, x, X, o (e.g., %hd, %hu) + * - Length modifier 'l': long for d, i, u, x, X, o (e.g., %ld, %lu, %lx) + * - Length modifier 'll': long long (64-bit) for d, i, u, x, X, o (e.g., %lld, %llu, %llx, %llX) + * - Length modifier 'z': size_t for d, i, u, x, X, o (e.g., %zu, %zd, %zx) */ extern int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list Args ); diff --git a/src/system/Makefile b/src/system/Makefile index d510850..6dfa57b 100644 --- a/src/system/Makefile +++ b/src/system/Makefile @@ -39,7 +39,8 @@ DMOD_SOURCES=private/dmod_vars.c\ if/dmod_if_str.c\ if/dmod_if_input.c\ if/dmod_if_proc.c\ - Dmod.cpp + Dmod.cpp\ + if/dmod_prf.c DMOD_INC_DIRS = $(DMOD_DIR)/src/system\ $(DMOD_DIR)/inc\ $(DMOD_BUILD_DIR)\ diff --git a/src/system/if/dmod_prf.c b/src/system/if/dmod_prf.c index 6929264..3533953 100644 --- a/src/system/if/dmod_prf.c +++ b/src/system/if/dmod_prf.c @@ -36,6 +36,7 @@ #include #include #include +#include //============================================================================== // HELPER FUNCTIONS @@ -308,6 +309,44 @@ static void Dmod_Print_Hex64( char** Buffer, size_t* Pos, size_t Size, uint64_t } } +static void Dmod_Print_Octal( char** Buffer, size_t* Pos, size_t Size, uint32_t Value, int* Count ) +{ + char Temp[12]; // Enough for 11 octal digits (32-bit) + int i = 0; + + // Convert to octal string (reversed) + do + { + Temp[i++] = '0' + (Value & 0x7); + Value >>= 3; + } while( Value > 0 ); + + // Print in correct order + while( i > 0 ) + { + Dmod_Print_Char( Buffer, Pos, Size, Temp[--i], Count ); + } +} + +static void Dmod_Print_Octal64( char** Buffer, size_t* Pos, size_t Size, uint64_t Value, int* Count ) +{ + char Temp[23]; // Enough for 22 octal digits (64-bit) + int i = 0; + + // Convert to octal string (reversed) + do + { + Temp[i++] = '0' + (Value & 0x7); + Value >>= 3; + } while( Value > 0 ); + + // Print in correct order + while( i > 0 ) + { + Dmod_Print_Char( Buffer, Pos, Size, Temp[--i], Count ); + } +} + //============================================================================== // PUBLIC FUNCTIONS //============================================================================== @@ -356,11 +395,47 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list } // Parse length modifier - bool IsLongLong = false; - if( *Format == 'l' && *(Format + 1) == 'l' ) + typedef enum { + LEN_NONE, // default (int/unsigned int) + LEN_HH, // char + LEN_H, // short + LEN_L, // long + LEN_LL, // long long + LEN_Z // size_t + } LengthModifier; + + LengthModifier LenMod = LEN_NONE; + + if( *Format == 'h' ) { - IsLongLong = true; - Format += 2; + if( *(Format + 1) == 'h' ) + { + LenMod = LEN_HH; + Format += 2; + } + else + { + LenMod = LEN_H; + Format++; + } + } + else if( *Format == 'l' ) + { + if( *(Format + 1) == 'l' ) + { + LenMod = LEN_LL; + Format += 2; + } + else + { + LenMod = LEN_L; + Format++; + } + } + else if( *Format == 'z' ) + { + LenMod = LEN_Z; + Format++; } // Handle format specifiers @@ -389,11 +464,33 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list case 'd': case 'i': { - if( IsLongLong ) + if( LenMod == LEN_LL ) { int64_t Value = va_arg( Args, int64_t ); Dmod_Print_LongLong( BufPtr, &Pos, Size, Value, &Count ); } + else if( LenMod == LEN_L || LenMod == LEN_Z ) + { + // long and size_t/ssize_t are typically same size + long Value = va_arg( Args, long ); + #if LONG_MAX == INT64_MAX + Dmod_Print_LongLong( BufPtr, &Pos, Size, (int64_t)Value, &Count ); + #else + Dmod_Print_Int( BufPtr, &Pos, Size, (int32_t)Value, &Count ); + #endif + } + else if( LenMod == LEN_HH ) + { + // char is promoted to int in varargs + int Value = va_arg( Args, int ); + Dmod_Print_Int( BufPtr, &Pos, Size, (int32_t)(signed char)Value, &Count ); + } + else if( LenMod == LEN_H ) + { + // short is promoted to int in varargs + int Value = va_arg( Args, int ); + Dmod_Print_Int( BufPtr, &Pos, Size, (int32_t)(short)Value, &Count ); + } else { int32_t Value = va_arg( Args, int32_t ); @@ -403,11 +500,33 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list } case 'u': { - if( IsLongLong ) + if( LenMod == LEN_LL ) { uint64_t Value = va_arg( Args, uint64_t ); Dmod_Print_ULongLong( BufPtr, &Pos, Size, Value, &Count ); } + else if( LenMod == LEN_L || LenMod == LEN_Z ) + { + // unsigned long and size_t are typically same size + unsigned long Value = va_arg( Args, unsigned long ); + #if ULONG_MAX == UINT64_MAX + Dmod_Print_ULongLong( BufPtr, &Pos, Size, (uint64_t)Value, &Count ); + #else + Dmod_Print_UInt( BufPtr, &Pos, Size, (uint32_t)Value, &Count ); + #endif + } + else if( LenMod == LEN_HH ) + { + // unsigned char is promoted to int in varargs + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_UInt( BufPtr, &Pos, Size, (uint32_t)(unsigned char)Value, &Count ); + } + else if( LenMod == LEN_H ) + { + // unsigned short is promoted to int in varargs + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_UInt( BufPtr, &Pos, Size, (uint32_t)(unsigned short)Value, &Count ); + } else { uint32_t Value = va_arg( Args, uint32_t ); @@ -417,11 +536,30 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list } case 'x': { - if( IsLongLong ) + if( LenMod == LEN_LL ) { uint64_t Value = va_arg( Args, uint64_t ); Dmod_Print_Hex64( BufPtr, &Pos, Size, Value, false, &Count ); } + else if( LenMod == LEN_L || LenMod == LEN_Z ) + { + unsigned long Value = va_arg( Args, unsigned long ); + #if ULONG_MAX == UINT64_MAX + Dmod_Print_Hex64( BufPtr, &Pos, Size, (uint64_t)Value, false, &Count ); + #else + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)Value, false, &Count ); + #endif + } + else if( LenMod == LEN_HH ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)(unsigned char)Value, false, &Count ); + } + else if( LenMod == LEN_H ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)(unsigned short)Value, false, &Count ); + } else { uint32_t Value = va_arg( Args, uint32_t ); @@ -431,11 +569,30 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list } case 'X': { - if( IsLongLong ) + if( LenMod == LEN_LL ) { uint64_t Value = va_arg( Args, uint64_t ); Dmod_Print_Hex64( BufPtr, &Pos, Size, Value, true, &Count ); } + else if( LenMod == LEN_L || LenMod == LEN_Z ) + { + unsigned long Value = va_arg( Args, unsigned long ); + #if ULONG_MAX == UINT64_MAX + Dmod_Print_Hex64( BufPtr, &Pos, Size, (uint64_t)Value, true, &Count ); + #else + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)Value, true, &Count ); + #endif + } + else if( LenMod == LEN_HH ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)(unsigned char)Value, true, &Count ); + } + else if( LenMod == LEN_H ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Hex( BufPtr, &Pos, Size, (uint32_t)(unsigned short)Value, true, &Count ); + } else { uint32_t Value = va_arg( Args, uint32_t ); @@ -444,6 +601,39 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list break; } + case 'o': { + if( LenMod == LEN_LL ) + { + uint64_t Value = va_arg( Args, uint64_t ); + Dmod_Print_Octal64( BufPtr, &Pos, Size, Value, &Count ); + } + else if( LenMod == LEN_L || LenMod == LEN_Z ) + { + unsigned long Value = va_arg( Args, unsigned long ); + #if ULONG_MAX == UINT64_MAX + Dmod_Print_Octal64( BufPtr, &Pos, Size, (uint64_t)Value, &Count ); + #else + Dmod_Print_Octal( BufPtr, &Pos, Size, (uint32_t)Value, &Count ); + #endif + } + else if( LenMod == LEN_HH ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Octal( BufPtr, &Pos, Size, (uint32_t)(unsigned char)Value, &Count ); + } + else if( LenMod == LEN_H ) + { + unsigned int Value = va_arg( Args, unsigned int ); + Dmod_Print_Octal( BufPtr, &Pos, Size, (uint32_t)(unsigned short)Value, &Count ); + } + else + { + uint32_t Value = va_arg( Args, uint32_t ); + Dmod_Print_Octal( BufPtr, &Pos, Size, Value, &Count ); + } + break; + } + case 'p': { void* Ptr = va_arg( Args, void* ); Dmod_Print_Pointer( BufPtr, &Pos, Size, Ptr, &Count ); @@ -471,11 +661,28 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list } } // Print length modifier if present - if( IsLongLong ) + if( LenMod == LEN_HH ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, 'h', &Count ); + Dmod_Print_Char( BufPtr, &Pos, Size, 'h', &Count ); + } + else if( LenMod == LEN_H ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, 'h', &Count ); + } + else if( LenMod == LEN_L ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, 'l', &Count ); + } + else if( LenMod == LEN_LL ) { Dmod_Print_Char( BufPtr, &Pos, Size, 'l', &Count ); Dmod_Print_Char( BufPtr, &Pos, Size, 'l', &Count ); } + else if( LenMod == LEN_Z ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, 'z', &Count ); + } Dmod_Print_Char( BufPtr, &Pos, Size, *Format, &Count ); break; } diff --git a/tests/system/if/tests_dmod_snprintf.cpp b/tests/system/if/tests_dmod_snprintf.cpp index 07d3b8d..db95c62 100644 --- a/tests/system/if/tests_dmod_snprintf.cpp +++ b/tests/system/if/tests_dmod_snprintf.cpp @@ -363,4 +363,188 @@ TEST_F(DmodSnPrintfTest, SnPrintfSignedLongLongI) ASSERT_STREQ(buffer, "-1234567890123456789"); } +/** + * @brief Test for Dmod_SnPrintf with %lu (unsigned long) + * + * The test checks if the function handles unsigned long format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfUnsignedLong) +{ + char buffer[64]; + unsigned long value = 4294967295UL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lu", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "4294967295"); +} + +/** + * @brief Test for Dmod_SnPrintf with %ld (signed long) + * + * The test checks if the function handles signed long format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSignedLong) +{ + char buffer[64]; + long value = -2147483648L; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%ld", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "-2147483648"); +} + +/** + * @brief Test for Dmod_SnPrintf with %lx (unsigned long hex) + * + * The test checks if the function handles unsigned long hex format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLongHex) +{ + char buffer[64]; + unsigned long value = 0xFFFFFFFFUL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lx", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "ffffffff"); +} + +/** + * @brief Test for Dmod_SnPrintf with %zu (size_t) + * + * The test checks if the function handles size_t format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSizeT) +{ + char buffer[64]; + size_t value = 12345; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%zu", value); + + ASSERT_EQ(result, 5); + ASSERT_STREQ(buffer, "12345"); +} + +/** + * @brief Test for Dmod_SnPrintf with %hd (short) + * + * The test checks if the function handles short format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfShort) +{ + char buffer[64]; + short value = -32768; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%hd", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "-32768"); +} + +/** + * @brief Test for Dmod_SnPrintf with %hu (unsigned short) + * + * The test checks if the function handles unsigned short format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfUnsignedShort) +{ + char buffer[64]; + unsigned short value = 65535; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%hu", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "65535"); +} + +/** + * @brief Test for Dmod_SnPrintf with %hhd (signed char) + * + * The test checks if the function handles signed char format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSignedChar) +{ + char buffer[64]; + signed char value = -128; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%hhd", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "-128"); +} + +/** + * @brief Test for Dmod_SnPrintf with %hhu (unsigned char) + * + * The test checks if the function handles unsigned char format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfUnsignedChar) +{ + char buffer[64]; + unsigned char value = 255; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%hhu", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "255"); +} + +/** + * @brief Test for Dmod_SnPrintf with %o (octal) + * + * The test checks if the function handles octal format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfOctal) +{ + char buffer[64]; + unsigned int value = 0755; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%o", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "755"); +} + +/** + * @brief Test for Dmod_SnPrintf with %lo (long octal) + * + * The test checks if the function handles long octal format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLongOctal) +{ + char buffer[64]; + unsigned long value = 0xFFFFFFFFUL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lo", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "37777777777"); +} + +/** + * @brief Test for Dmod_SnPrintf with %llo (long long octal) + * + * The test checks if the function handles long long octal format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLongLongOctal) +{ + char buffer[64]; + uint64_t value = 0xFFFFFFFFFFFFFFFFULL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llo", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "1777777777777777777777"); +} + +/** + * @brief Test for Dmod_SnPrintf with mixed new format specifiers + * + * The test checks if the function handles multiple new format specifiers. + */ +TEST_F(DmodSnPrintfTest, SnPrintfMixedNewFormats) +{ + char buffer[128]; + unsigned long ul = 4294967295UL; + short s = -32768; + unsigned char uc = 255; + unsigned int oct = 0755; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), + "Mixed: %lu, %hd, %hhu, %o", ul, s, uc, oct); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "Mixed: 4294967295, -32768, 255, 755"); +} +