diff --git a/inc/private/dmod_prf.h b/inc/private/dmod_prf.h index aecd470..c3fd935 100644 --- a/inc/private/dmod_prf.h +++ b/inc/private/dmod_prf.h @@ -68,6 +68,7 @@ extern "C" { * 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) */ extern int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list Args ); diff --git a/src/system/if/dmod_prf.c b/src/system/if/dmod_prf.c index 58317b7..6929264 100644 --- a/src/system/if/dmod_prf.c +++ b/src/system/if/dmod_prf.c @@ -224,6 +224,90 @@ static void Dmod_Print_Pointer( char** Buffer, size_t* Pos, size_t Size, void* P } } +static void Dmod_Print_LongLong( char** Buffer, size_t* Pos, size_t Size, int64_t Value, int* Count ) +{ + char Temp[21]; // Enough for -9223372036854775808 + int i = 0; + bool IsNegative = false; + uint64_t UValue; + + if( Value < 0 ) + { + IsNegative = true; + // Handle most negative int64_t value specially to avoid overflow + if( Value == (int64_t)0x8000000000000000LL ) + { + UValue = 0x8000000000000000ULL; + } + else + { + UValue = (uint64_t)(-Value); + } + } + else + { + UValue = (uint64_t)Value; + } + + // Convert to string (reversed) + do + { + Temp[i++] = '0' + (UValue % 10); + UValue /= 10; + } while( UValue > 0 ); + + // Add sign + if( IsNegative ) + { + Dmod_Print_Char( Buffer, Pos, Size, '-', Count ); + } + + // Print in correct order + while( i > 0 ) + { + Dmod_Print_Char( Buffer, Pos, Size, Temp[--i], Count ); + } +} + +static void Dmod_Print_ULongLong( char** Buffer, size_t* Pos, size_t Size, uint64_t Value, int* Count ) +{ + char Temp[21]; // Enough for 18446744073709551615 + int i = 0; + + // Convert to string (reversed) + do + { + Temp[i++] = '0' + (Value % 10); + Value /= 10; + } while( Value > 0 ); + + // Print in correct order + while( i > 0 ) + { + Dmod_Print_Char( Buffer, Pos, Size, Temp[--i], Count ); + } +} + +static void Dmod_Print_Hex64( char** Buffer, size_t* Pos, size_t Size, uint64_t Value, bool Uppercase, int* Count ) +{ + char Temp[17]; // Enough for 16 hex digits + int i = 0; + const char* HexDigits = Uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; + + // Convert to hex string (reversed) + do + { + Temp[i++] = HexDigits[Value & 0xF]; + Value >>= 4; + } while( Value > 0 ); + + // Print in correct order + while( i > 0 ) + { + Dmod_Print_Char( Buffer, Pos, Size, Temp[--i], Count ); + } +} + //============================================================================== // PUBLIC FUNCTIONS //============================================================================== @@ -271,6 +355,14 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list Format++; } + // Parse length modifier + bool IsLongLong = false; + if( *Format == 'l' && *(Format + 1) == 'l' ) + { + IsLongLong = true; + Format += 2; + } + // Handle format specifiers switch( *Format ) { @@ -297,26 +389,58 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list case 'd': case 'i': { - int32_t Value = va_arg( Args, int32_t ); - Dmod_Print_Int( BufPtr, &Pos, Size, Value, &Count ); + if( IsLongLong ) + { + int64_t Value = va_arg( Args, int64_t ); + Dmod_Print_LongLong( BufPtr, &Pos, Size, Value, &Count ); + } + else + { + int32_t Value = va_arg( Args, int32_t ); + Dmod_Print_Int( BufPtr, &Pos, Size, Value, &Count ); + } break; } case 'u': { - uint32_t Value = va_arg( Args, uint32_t ); - Dmod_Print_UInt( BufPtr, &Pos, Size, Value, &Count ); + if( IsLongLong ) + { + uint64_t Value = va_arg( Args, uint64_t ); + Dmod_Print_ULongLong( BufPtr, &Pos, Size, Value, &Count ); + } + else + { + uint32_t Value = va_arg( Args, uint32_t ); + Dmod_Print_UInt( BufPtr, &Pos, Size, Value, &Count ); + } break; } case 'x': { - uint32_t Value = va_arg( Args, uint32_t ); - Dmod_Print_Hex( BufPtr, &Pos, Size, Value, false, &Count ); + if( IsLongLong ) + { + uint64_t Value = va_arg( Args, uint64_t ); + Dmod_Print_Hex64( BufPtr, &Pos, Size, Value, false, &Count ); + } + else + { + uint32_t Value = va_arg( Args, uint32_t ); + Dmod_Print_Hex( BufPtr, &Pos, Size, Value, false, &Count ); + } break; } case 'X': { - uint32_t Value = va_arg( Args, uint32_t ); - Dmod_Print_Hex( BufPtr, &Pos, Size, Value, true, &Count ); + if( IsLongLong ) + { + uint64_t Value = va_arg( Args, uint64_t ); + Dmod_Print_Hex64( BufPtr, &Pos, Size, Value, true, &Count ); + } + else + { + uint32_t Value = va_arg( Args, uint32_t ); + Dmod_Print_Hex( BufPtr, &Pos, Size, Value, true, &Count ); + } break; } @@ -346,6 +470,12 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list Dmod_Print_Char( BufPtr, &Pos, Size, WidthStr[--i], &Count ); } } + // Print length modifier if present + if( IsLongLong ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, 'l', &Count ); + Dmod_Print_Char( BufPtr, &Pos, Size, 'l', &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 ae42d13..07d3b8d 100644 --- a/tests/system/if/tests_dmod_snprintf.cpp +++ b/tests/system/if/tests_dmod_snprintf.cpp @@ -210,4 +210,157 @@ TEST_F(DmodSnPrintfTest, SnPrintfZeroWidth) ASSERT_STREQ(buffer, "test"); } +/** + * @brief Test for Dmod_SnPrintf with %llu (unsigned long long) + * + * The test checks if the function handles unsigned long long format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfUnsignedLongLong) +{ + char buffer[64]; + uint64_t value = 18446744073709551615ULL; // Max uint64_t + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llu", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "18446744073709551615"); +} + +/** + * @brief Test for Dmod_SnPrintf with %lld (signed long long) + * + * The test checks if the function handles signed long long format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSignedLongLong) +{ + char buffer[64]; + int64_t value = -9223372036854775807LL - 1LL; // Min int64_t + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lld", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "-9223372036854775808"); +} + +/** + * @brief Test for Dmod_SnPrintf with positive %lld + * + * The test checks if the function handles positive signed long long values. + */ +TEST_F(DmodSnPrintfTest, SnPrintfPositiveLongLong) +{ + char buffer[64]; + int64_t value = 9223372036854775807LL; // Max int64_t + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lld", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "9223372036854775807"); +} + +/** + * @brief Test for Dmod_SnPrintf with %llx (long long hex lowercase) + * + * The test checks if the function handles long long hexadecimal format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLongLongHexLowercase) +{ + char buffer[64]; + uint64_t value = 0xFEDCBA9876543210ULL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llx", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "fedcba9876543210"); +} + +/** + * @brief Test for Dmod_SnPrintf with %llX (long long hex uppercase) + * + * The test checks if the function handles long long hexadecimal uppercase format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLongLongHexUppercase) +{ + char buffer[64]; + uint64_t value = 0xFEDCBA9876543210ULL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llX", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "FEDCBA9876543210"); +} + +/** + * @brief Test for Dmod_SnPrintf with small %llu value + * + * The test checks if the function handles small unsigned long long values correctly. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSmallUnsignedLongLong) +{ + char buffer[64]; + uint64_t value = 42ULL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llu", value); + + ASSERT_EQ(result, 2); + ASSERT_STREQ(buffer, "42"); +} + +/** + * @brief Test for Dmod_SnPrintf with zero %llu value + * + * The test checks if the function handles zero unsigned long long value. + */ +TEST_F(DmodSnPrintfTest, SnPrintfZeroLongLong) +{ + char buffer[64]; + uint64_t value = 0ULL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%llu", value); + + ASSERT_EQ(result, 1); + ASSERT_STREQ(buffer, "0"); +} + +/** + * @brief Test for Dmod_SnPrintf with mixed format specifiers including %llu + * + * The test checks if the function handles multiple format specifiers including long long. + */ +TEST_F(DmodSnPrintfTest, SnPrintfMixedWithLongLong) +{ + char buffer[128]; + uint64_t big_value = 1234567890123456789ULL; + int small_value = 42; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), + "Small: %d, Big: %llu, Hex: %llx", + small_value, big_value, big_value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "Small: 42, Big: 1234567890123456789, Hex: 112210f47de98115"); +} + +/** + * @brief Test for Dmod_SnPrintf with negative zero %lld + * + * The test checks if the function handles zero signed long long value. + */ +TEST_F(DmodSnPrintfTest, SnPrintfNegativeZeroLongLong) +{ + char buffer[64]; + int64_t value = 0LL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lld", value); + + ASSERT_EQ(result, 1); + ASSERT_STREQ(buffer, "0"); +} + +/** + * @brief Test for Dmod_SnPrintf with %lli (signed long long, 'i' variant) + * + * The test checks if the function handles %lli format specifier. + */ +TEST_F(DmodSnPrintfTest, SnPrintfSignedLongLongI) +{ + char buffer[64]; + int64_t value = -1234567890123456789LL; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%lli", value); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "-1234567890123456789"); +} +