Browse Source

[klibc] add rt_vsnprintf_std.c and rename RT_KLIBC_USING_VSNPRINTF_LONGLONG

Meco Man 7 months ago
parent
commit
8e10983c9b
38 changed files with 2102 additions and 842 deletions
  1. 1 1
      bsp/allwinner/d1/rtconfig.h
  2. 1 1
      bsp/allwinner/d1s/rtconfig.h
  3. 1 1
      bsp/bouffalo_lab/bl808/d0/rtconfig.h
  4. 1 1
      bsp/cvitek/c906_little/rtconfig.h
  5. 1 1
      bsp/cvitek/cv18xx_aarch64/rtconfig.h
  6. 1 1
      bsp/cvitek/cv18xx_risc-v/rtconfig.h
  7. 1 1
      bsp/k230/rtconfig.h
  8. 0 27
      bsp/nuvoton/numaker-iot-m467/.config
  9. 0 27
      bsp/nuvoton/numaker-iot-m487/.config
  10. 0 27
      bsp/nuvoton/numaker-m2354/.config
  11. 0 27
      bsp/nuvoton/numaker-m467hj/.config
  12. 0 26
      bsp/nxp/mcx/mcxa/frdm-mcxa153/.config
  13. 0 27
      bsp/nxp/mcx/mcxn/frdm-mcxn236/.config
  14. 0 20
      bsp/nxp/mcx/mcxn/frdm-mcxn947/.config
  15. 1 1
      bsp/phytium/aarch64/configs/e2000d_demo_rtsmart.h
  16. 1 1
      bsp/phytium/aarch64/configs/e2000d_demo_rtthread.h
  17. 1 1
      bsp/phytium/aarch64/configs/e2000d_demo_rtthread_pusb2_dc.h
  18. 1 1
      bsp/phytium/aarch64/configs/e2000q_demo_rtsmart.h
  19. 1 1
      bsp/phytium/aarch64/configs/e2000q_demo_rtthread.h
  20. 1 1
      bsp/phytium/aarch64/configs/phytium_pi_rtsmart.h
  21. 1 1
      bsp/phytium/aarch64/configs/phytium_pi_rtthread.h
  22. 1 1
      bsp/phytium/aarch64/configs/phytium_pi_rtthread_pusb2_hc.h
  23. 1 1
      bsp/phytium/aarch64/configs/phytium_pi_rtthread_xhci.h
  24. 1 1
      bsp/phytium/aarch64/rtconfig.h
  25. 1 1
      bsp/qemu-virt64-riscv/rtconfig.h
  26. 1 1
      bsp/raspberry-pi/raspi-dm2.0/rtconfig.h
  27. 1 1
      bsp/rockchip/rk3500/rtconfig.h
  28. 0 3
      bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/kernel/klibc-printf.attach
  29. 9 0
      bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/kernel/klibc-vsnprintf-std.attach
  30. 0 1
      bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/online-packages/system/enhanced-kservice.attach
  31. 1 1
      components/drivers/sensor/Kconfig
  32. 1 1
      components/utilities/Kconfig
  33. 99 10
      src/Kconfig
  34. 0 8
      src/klibc/kerrno.c
  35. 0 609
      src/klibc/kstdio.c
  36. 0 8
      src/klibc/kstring.c
  37. 1356 0
      src/klibc/rt_vsnprintf_std.c
  38. 616 0
      src/klibc/rt_vsnprintf_tiny.c

+ 1 - 1
bsp/allwinner/d1/rtconfig.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/allwinner/d1s/rtconfig.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/bouffalo_lab/bl808/d0/rtconfig.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/cvitek/c906_little/rtconfig.h

@@ -24,7 +24,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/cvitek/cv18xx_aarch64/rtconfig.h

@@ -24,7 +24,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/cvitek/cv18xx_risc-v/rtconfig.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/k230/rtconfig.h

@@ -27,7 +27,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 0 - 27
bsp/nuvoton/numaker-iot-m467/.config

@@ -821,33 +821,6 @@ CONFIG_PKG_AT_DEVICE_VER_NUM=0x99999
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG=y
-CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER=y
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-# end of enhanced kernel services
-
 # CONFIG_PKG_USING_AUNITY is not set
 
 #

+ 0 - 27
bsp/nuvoton/numaker-iot-m487/.config

@@ -801,33 +801,6 @@ CONFIG_PKG_AT_DEVICE_VER_NUM=0x99999
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG=y
-CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER=y
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-# end of enhanced kernel services
-
 # CONFIG_PKG_USING_AUNITY is not set
 
 #

+ 0 - 27
bsp/nuvoton/numaker-m2354/.config

@@ -735,33 +735,6 @@ CONFIG_PKG_AT_DEVICE_VER_NUM=0x99999
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG=y
-CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER=y
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-# end of enhanced kernel services
-
 # CONFIG_PKG_USING_AUNITY is not set
 
 #

+ 0 - 27
bsp/nuvoton/numaker-m467hj/.config

@@ -773,33 +773,6 @@ CONFIG_RT_USB_MSTORAGE_DISK_NAME="ramdisk1"
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER=y
-CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG=y
-CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER=y
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-# end of enhanced kernel services
-
 # CONFIG_PKG_USING_AUNITY is not set
 
 #

+ 0 - 26
bsp/nxp/mcx/mcxa/frdm-mcxa153/.config

@@ -507,32 +507,6 @@ CONFIG_ULOG_BACKEND_USING_CONSOLE=y
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-# CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG is not set
-# CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-
 #
 # acceleration: Assembly language or algorithmic acceleration packages
 #

+ 0 - 27
bsp/nxp/mcx/mcxn/frdm-mcxn236/.config

@@ -558,33 +558,6 @@ CONFIG_ULOG_BACKEND_USING_CONSOLE=y
 # system packages
 #
 
-#
-# enhanced kernel services
-#
-# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
-# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-# CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG is not set
-# CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
-# end of enhanced kernel services
-
 # CONFIG_PKG_USING_AUNITY is not set
 
 #

+ 0 - 20
bsp/nxp/mcx/mcxn/frdm-mcxn947/.config

@@ -512,26 +512,6 @@ CONFIG_ULOG_BACKEND_USING_CONSOLE=y
 #
 # CONFIG_PKG_USING_RT_MEMCPY_CM is not set
 # CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_PATH="/packages/system/enhanced-kservice/rt_vsnprintf_full"
-# CONFIG_PKG_VSNPRINTF_SUPPORT_DECIMAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_WRITEBACK_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_LONG_LONG is not set
-# CONFIG_PKG_VSNPRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER is not set
-# CONFIG_PKG_VSNPRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_PKG_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_PKG_VSNPRINTF_DEFAULT_FLOAT_PRECISION=6
-CONFIG_PKG_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_PKG_VSNPRINTF_LOG10_TAYLOR_TERMS=4
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_SNPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_PRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSPRINTF is not set
-# CONFIG_RT_VSNPRINTF_FULL_REPLACING_VSNPRINTF is not set
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL_LATEST_VERSION=y
-CONFIG_PKG_RT_VSNPRINTF_FULL_VER="latest"
 
 #
 # acceleration: Assembly language or algorithmic acceleration packages

+ 1 - 1
bsp/phytium/aarch64/configs/e2000d_demo_rtsmart.h

@@ -28,7 +28,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/e2000d_demo_rtthread.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/e2000d_demo_rtthread_pusb2_dc.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/e2000q_demo_rtsmart.h

@@ -28,7 +28,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/e2000q_demo_rtthread.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/phytium_pi_rtsmart.h

@@ -28,7 +28,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/phytium_pi_rtthread.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/phytium_pi_rtthread_pusb2_hc.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/configs/phytium_pi_rtthread_xhci.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/phytium/aarch64/rtconfig.h

@@ -26,7 +26,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/qemu-virt64-riscv/rtconfig.h

@@ -25,7 +25,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_ASSERT

+ 1 - 1
bsp/raspberry-pi/raspi-dm2.0/rtconfig.h

@@ -28,7 +28,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_COLOR

+ 1 - 1
bsp/rockchip/rk3500/rtconfig.h

@@ -30,7 +30,7 @@
 
 /* klibc optimization */
 
-#define RT_KLIBC_USING_PRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
 /* end of klibc optimization */
 #define RT_USING_DEBUG
 #define RT_DEBUGING_COLOR

+ 0 - 3
bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/kernel/klibc-printf.attach

@@ -1,3 +0,0 @@
-# scons: --strict
-
-CONFIG_RT_KLIBC_USING_PRINTF_LONGLONG=y

+ 9 - 0
bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/kernel/klibc-vsnprintf-std.attach

@@ -0,0 +1,9 @@
+# scons: --strict
+
+CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y
+CONFIG_RT_KLIBC_USING_PRINTF_LONGLONG=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS=y

+ 0 - 1
bsp/stm32/stm32f407-rt-spark/.ci/attachconfig/online-packages/system/enhanced-kservice.attach

@@ -1,4 +1,3 @@
 # scons: --strict
 CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE=y
 CONFIG_PKG_USING_RT_MEMCPY_CM=y
-CONFIG_PKG_USING_RT_VSNPRINTF_FULL=y

+ 1 - 1
components/drivers/sensor/Kconfig

@@ -10,6 +10,6 @@ if RT_USING_SENSOR
 
     config RT_USING_SENSOR_CMD
         bool "Using Sensor cmd"
-        select PKG_USING_RT_VSNPRINTF_FULL if RT_USING_SENSOR_V2
+        select RT_KLIBC_USING_VSNPRINTF_STANDARD if RT_USING_SENSOR_V2
         default y
 endif

+ 1 - 1
components/utilities/Kconfig

@@ -137,7 +137,7 @@ menuconfig RT_USING_ULOG
             config ULOG_OUTPUT_FLOAT
                 bool "Enable float number support. It will using more thread stack."
                 default n
-                select PKG_USING_RT_VSNPRINTF_FULL
+                select RT_KLIBC_USING_VSNPRINTF_STANDARD
                 help
                     The default formater is using rt_vsnprint and it not supported float number.
                     When enable this option then it will enable libc. The formater will change to vsnprint on libc.

+ 99 - 10
src/Kconfig

@@ -189,13 +189,13 @@ config RT_USING_CPU_USAGE_TRACER
     default y if RT_USING_SMART
     default n
 
-menu "kservice optimization"
+menu "kservice options"
     config RT_USING_TINY_FFS
         bool "Enable kservice to use tiny finding first bit set method"
         default n
 endmenu
 
-menu "klibc optimization"
+menu "klibc options"
 
     config RT_KLIBC_USING_STDLIB
         bool "Enable klibc to use standard C library"
@@ -213,15 +213,104 @@ menu "klibc optimization"
         bool "Enable tiny size of klibc"
         default n
 
-    config RT_KLIBC_USING_PRINTF_LONGLONG
-        bool "Enable rt_printf-family functions to support long-long format"
-        default y if ARCH_CPU_64BIT
-        default n
-        help
-            Enable rt_printf()/rt_snprintf()/rt_sprintf()/rt_vsnprintf()/rt_vsprintf()
-            functions to support long-long format
+    menu "rt_vsnprintf options"
+        config RT_KLIBC_USING_VSNPRINTF_STANDARD
+            bool "standard rt_vsnprintf version"
+            default y if ARCH_CPU_64BIT
+            default n
+            help
+                Standard version of rt_vsnprintf, which is full function but higher stack usage.
 
-endmenu
+        config RT_KLIBC_USING_VSNPRINTF_LONGLONG
+            bool "Enable rt_vsnprintf function to support long-long format"
+            default y if RT_KLIBC_USING_VSNPRINTF_STANDARD
+            default n
+                help
+                    Support for the long long integral types (with the ll, z and t length modifiers for specifiers
+                    %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported.
+
+        if RT_KLIBC_USING_VSNPRINTF_STANDARD
+
+            config RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
+                bool "Support decimal notation floating point conversion specifiers (%f, %F)"
+                default y
+                help
+                    Support for the decimal notation floating point conversion specifiers (%f, %F)
+
+            config RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+                bool "Support exponential notation floating point conversion specifiers (%e, %g, %E, %G)"
+                default y
+                help
+                    Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G)
+
+            config RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+                bool "Support length write-back specifier (%n)"
+                default y
+                help
+                    Support for the length write-back specifier (%n)
+
+            config RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
+                bool "safety check: no NULL end string"
+                default y if RT_USING_DEBUG
+                default n
+                help
+                    Be extra-safe, and don't assume format specifiers are completed correctly
+                    before the format string end.
+
+            config RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+                bool "Support MSVC style integer specifiers"
+                default n
+                help
+                    the integer format specifiers used in Microsoft's Visual C++ (MSVC) compiler.
+                    These specifiers, like %I64d for 64-bit integers, deviate slightly from the standard
+                    C format specifiers and are specific to MSVC. They allow for controlled formatting of
+                    integers in printf()-like functions, accommodating different integer sizes and ensuring
+                    compatibility with MSVC's environment. It's important to note that these specifiers might
+                    not be recognized or function in other compilers due to their MSVC-specific nature.
+
+            config RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE
+                int "'ntoa' conversion buffer size"
+                default 32
+                help
+                    'ntoa' conversion buffer size, this must be big enough to hold one converted
+                    numeric number including padded zeros (dynamically created on stack)
+
+            config RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE
+                int "printing individual decimal numbers buffer size"
+                default 32
+                help
+                    size of the fixed (on-stack) buffer for printing individual decimal numbers.
+                    this must be big enough to hold one converted floating-point value including
+                    padded zeros.
+
+            config RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION
+                int "floating point conversion specifiers"
+                default 6
+                help
+                    Default precision for the floating point conversion specifiers (the C standard sets this at 6)
+
+            config RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL
+                int "integral nums printed as float in rt_vsnprint"
+                default 9
+                help
+                    According to the C languages standard, printf() and related functions must be able to print any
+                    integral number in floating-point notation, regardless of length, when using the %f specifier -
+                    possibly hundreds of characters, potentially overflowing your buffers. In this implementation,
+                    all values beyond this threshold are switched to exponential notation.
+
+            config RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS
+                int "the number of terms in a Taylor series expansion of log_10(x)"
+                default 4
+                range 2 99
+                help
+                    The number of terms in a Taylor series expansion of log_10(x) to
+                    use for approximation - including the power-zero term (i.e. the
+                    value at the point of expansion).
+
+        endif
+    endmenu # rt_vsnprintf options
+
+endmenu # klibc options
 
 menuconfig RT_USING_DEBUG
     bool "Enable debugging features"

+ 0 - 8
src/klibc/kerrno.c

@@ -10,14 +10,6 @@
 
 #include <rtthread.h>
 
-#define DBG_TAG           "kernel.errno"
-#ifdef RT_DEBUG_DEVICE
-#define DBG_LVL           DBG_LOG
-#else
-#define DBG_LVL           DBG_WARNING
-#endif /* defined (RT_DEBUG_DEVICE) */
-#include <rtdbg.h>
-
 /**
  * @brief A global variable used to store the error code.
  *

+ 0 - 609
src/klibc/kstdio.c

@@ -10,615 +10,6 @@
 
 #include <rtthread.h>
 
-#define DBG_TAG           "kernel.stdio"
-#ifdef RT_DEBUG_DEVICE
-#define DBG_LVL           DBG_LOG
-#else
-#define DBG_LVL           DBG_WARNING
-#endif /* defined (RT_DEBUG_DEVICE) */
-#include <rtdbg.h>
-
-#define _ISDIGIT(c)  ((unsigned)((c) - '0') < 10)
-
-/**
- * @brief  This function will duplicate a string.
- *
- * @param  n is the string to be duplicated.
- *
- * @param  base is support divide instructions value.
- *
- * @return the duplicated string pointer.
- */
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-rt_inline int divide(unsigned long long *n, int base)
-#else
-rt_inline int divide(unsigned long *n, int base)
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-{
-    int res;
-
-    /* optimized for processor which does not support divide instructions. */
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-    res = (int)((*n) % base);
-    *n = (long long)((*n) / base);
-#else
-    res = (int)((*n) % base);
-    *n = (long)((*n) / base);
-#endif
-
-    return res;
-}
-
-rt_inline int skip_atoi(const char **s)
-{
-    int i = 0;
-    while (_ISDIGIT(**s))
-        i = i * 10 + *((*s)++) - '0';
-
-    return i;
-}
-
-#define ZEROPAD     (1 << 0)    /* pad with zero */
-#define SIGN        (1 << 1)    /* unsigned/signed long */
-#define PLUS        (1 << 2)    /* show plus */
-#define SPACE       (1 << 3)    /* space if plus */
-#define LEFT        (1 << 4)    /* left justified */
-#define SPECIAL     (1 << 5)    /* 0x */
-#define LARGE       (1 << 6)    /* use 'ABCDEF' instead of 'abcdef' */
-
-static char *print_number(char *buf,
-                          char *end,
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-                          unsigned long long  num,
-#else
-                          unsigned long  num,
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-                          int   base,
-                          int   qualifier,
-                          int   s,
-                          int   precision,
-                          int   type)
-{
-    char c = 0, sign = 0;
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-    char tmp[64] = {0};
-#else
-    char tmp[32] = {0};
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-    int precision_bak = precision;
-    const char *digits = RT_NULL;
-    static const char small_digits[] = "0123456789abcdef";
-    static const char large_digits[] = "0123456789ABCDEF";
-    int i = 0;
-    int size = 0;
-
-    size = s;
-
-    digits = (type & LARGE) ? large_digits : small_digits;
-    if (type & LEFT)
-    {
-        type &= ~ZEROPAD;
-    }
-
-    c = (type & ZEROPAD) ? '0' : ' ';
-
-    /* get sign */
-    sign = 0;
-    if (type & SIGN)
-    {
-        switch (qualifier)
-        {
-        case 'h':
-            if ((rt_int16_t)num < 0)
-            {
-                sign = '-';
-                num = (rt_uint16_t)-num;
-            }
-            break;
-        case 'L':
-        case 'l':
-            if ((long)num < 0)
-            {
-                sign = '-';
-                num = (unsigned long)-num;
-            }
-            break;
-        case 0:
-        default:
-            if ((rt_int32_t)num < 0)
-            {
-                sign = '-';
-                num = (rt_uint32_t)-num;
-            }
-            break;
-        }
-
-        if (sign != '-')
-        {
-            if (type & PLUS)
-            {
-                sign = '+';
-            }
-            else if (type & SPACE)
-            {
-                sign = ' ';
-            }
-        }
-    }
-
-    if (type & SPECIAL)
-    {
-        if (base == 2 || base == 16)
-        {
-            size -= 2;
-        }
-        else if (base == 8)
-        {
-            size--;
-        }
-    }
-
-    i = 0;
-    if (num == 0)
-    {
-        tmp[i++] = '0';
-    }
-    else
-    {
-        while (num != 0)
-            tmp[i++] = digits[divide(&num, base)];
-    }
-
-    if (i > precision)
-    {
-        precision = i;
-    }
-    size -= precision;
-
-    if (!(type & (ZEROPAD | LEFT)))
-    {
-        if ((sign) && (size > 0))
-        {
-            size--;
-        }
-
-        while (size-- > 0)
-        {
-            if (buf < end)
-            {
-                *buf = ' ';
-            }
-
-            ++ buf;
-        }
-    }
-
-    if (sign)
-    {
-        if (buf < end)
-        {
-            *buf = sign;
-        }
-        -- size;
-        ++ buf;
-    }
-
-    if (type & SPECIAL)
-    {
-        if (base == 2)
-        {
-            if (buf < end)
-                *buf = '0';
-            ++ buf;
-            if (buf < end)
-                *buf = 'b';
-            ++ buf;
-        }
-        else if (base == 8)
-        {
-            if (buf < end)
-                *buf = '0';
-            ++ buf;
-        }
-        else if (base == 16)
-        {
-            if (buf < end)
-            {
-                *buf = '0';
-            }
-
-            ++ buf;
-            if (buf < end)
-            {
-                *buf = type & LARGE ? 'X' : 'x';
-            }
-            ++ buf;
-        }
-    }
-
-    /* no align to the left */
-    if (!(type & LEFT))
-    {
-        while (size-- > 0)
-        {
-            if (buf < end)
-            {
-                *buf = c;
-            }
-
-            ++ buf;
-        }
-    }
-
-    while (i < precision--)
-    {
-        if (buf < end)
-        {
-            *buf = '0';
-        }
-
-        ++ buf;
-    }
-
-    /* put number in the temporary buffer */
-    while (i-- > 0 && (precision_bak != 0))
-    {
-        if (buf < end)
-        {
-            *buf = tmp[i];
-        }
-
-        ++ buf;
-    }
-
-    while (size-- > 0)
-    {
-        if (buf < end)
-        {
-            *buf = ' ';
-        }
-
-        ++ buf;
-    }
-
-    return buf;
-}
-
-#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
-/* Disable "-Wimplicit-fallthrough" below GNUC V7 */
-#pragma GCC diagnostic push
-/* ignore warning: this statement may fall through */
-#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
-#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
-/**
- * @brief  This function will fill a formatted string to buffer.
- *
- * @param  buf is the buffer to save formatted string.
- *
- * @param  size is the size of buffer.
- *
- * @param  fmt is the format parameters.
- *
- * @param  args is a list of variable parameters.
- *
- * @return The number of characters actually written to buffer.
- */
-rt_weak int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
-{
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-    unsigned long long num = 0;
-#else
-    unsigned long num = 0;
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-    int i = 0, len = 0;
-    char *str = RT_NULL, *end = RT_NULL, c = 0;
-    const char *s = RT_NULL;
-
-    rt_uint8_t base = 0;            /* the base of number */
-    rt_uint8_t flags = 0;           /* flags to print number */
-    rt_uint8_t qualifier = 0;       /* 'h', 'l', or 'L' for integer fields */
-    rt_int32_t field_width = 0;     /* width of output field */
-    int precision = 0;      /* min. # of digits for integers and max for a string */
-
-    str = buf;
-    end = buf + size;
-
-    /* Make sure end is always >= buf */
-    if (end < buf)
-    {
-        end  = ((char *) - 1);
-        size = end - buf;
-    }
-
-    for (; *fmt ; ++fmt)
-    {
-        if (*fmt != '%')
-        {
-            if (str < end)
-            {
-                *str = *fmt;
-            }
-
-            ++ str;
-            continue;
-        }
-
-        /* process flags */
-        flags = 0;
-
-        while (1)
-        {
-            /* skips the first '%' also */
-            ++fmt;
-            if (*fmt == '-') flags |= LEFT;
-            else if (*fmt == '+') flags |= PLUS;
-            else if (*fmt == ' ') flags |= SPACE;
-            else if (*fmt == '#') flags |= SPECIAL;
-            else if (*fmt == '0') flags |= ZEROPAD;
-            else break;
-        }
-
-        /* get field width */
-        field_width = -1;
-        if (_ISDIGIT(*fmt))
-        {
-            field_width = skip_atoi(&fmt);
-        }
-        else if (*fmt == '*')
-        {
-            ++fmt;
-            /* it's the next argument */
-            field_width = va_arg(args, int);
-            if (field_width < 0)
-            {
-                field_width = -field_width;
-                flags |= LEFT;
-            }
-        }
-
-        /* get the precision */
-        precision = -1;
-        if (*fmt == '.')
-        {
-            ++fmt;
-            if (_ISDIGIT(*fmt))
-            {
-                precision = skip_atoi(&fmt);
-            }
-            else if (*fmt == '*')
-            {
-                ++fmt;
-                /* it's the next argument */
-                precision = va_arg(args, int);
-            }
-            if (precision < 0)
-            {
-                precision = 0;
-            }
-        }
-
-        qualifier = 0; /* get the conversion qualifier */
-
-        if (*fmt == 'h' || *fmt == 'l' ||
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-            *fmt == 'L' ||
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-            *fmt == 'z')
-        {
-            qualifier = *fmt;
-            ++fmt;
-#ifdef RT_KLIBC_USING_PRINTF_LONGLONG
-            if (qualifier == 'l' && *fmt == 'l')
-            {
-                qualifier = 'L';
-                ++fmt;
-            }
-#endif /* RT_KLIBC_USING_PRINTF_LONGLONG */
-            if (qualifier == 'h' && *fmt == 'h')
-            {
-                qualifier = 'H';
-                ++fmt;
-            }
-        }
-
-        /* the default base */
-        base = 10;
-
-        switch (*fmt)
-        {
-        case 'c':
-            if (!(flags & LEFT))
-            {
-                while (--field_width > 0)
-                {
-                    if (str < end) *str = ' ';
-                    ++ str;
-                }
-            }
-
-            /* get character */
-            c = (rt_uint8_t)va_arg(args, int);
-            if (str < end)
-            {
-                *str = c;
-            }
-            ++ str;
-
-            /* put width */
-            while (--field_width > 0)
-            {
-                if (str < end) *str = ' ';
-                ++ str;
-            }
-            continue;
-
-        case 's':
-            s = va_arg(args, char *);
-            if (!s)
-            {
-                s = "(NULL)";
-            }
-
-            for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
-
-            if (precision > 0 && len > precision)
-            {
-                len = precision;
-            }
-
-            if (!(flags & LEFT))
-            {
-                while (len < field_width--)
-                {
-                    if (str < end) *str = ' ';
-                    ++ str;
-                }
-            }
-
-            for (i = 0; i < len; ++i)
-            {
-                if (str < end) *str = *s;
-                ++ str;
-                ++ s;
-            }
-
-            while (len < field_width--)
-            {
-                if (str < end) *str = ' ';
-                ++ str;
-            }
-            continue;
-
-        case 'p':
-            if (field_width == -1)
-            {
-                field_width = sizeof(void *) << 1;
-                field_width += 2; /* `0x` prefix */
-                flags |= SPECIAL;
-                flags |= ZEROPAD;
-            }
-            str = print_number(str, end, (unsigned long)va_arg(args, void *),
-                               16, qualifier, field_width, precision, flags);
-            continue;
-
-        case '%':
-            if (str < end)
-            {
-                *str = '%';
-            }
-            ++ str;
-            continue;
-
-        /* integer number formats - set up the flags and "break" */
-        case 'b':
-            base = 2;
-            break;
-        case 'o':
-            base = 8;
-            break;
-
-        case 'X':
-            flags |= LARGE;
-        case 'x':
-            base = 16;
-            break;
-
-        case 'd':
-        case 'i':
-            flags |= SIGN;
-        case 'u':
-            break;
-
-        case 'e':
-        case 'E':
-        case 'G':
-        case 'g':
-        case 'f':
-        case 'F':
-            va_arg(args, double);
-        default:
-            if (str < end)
-            {
-                *str = '%';
-            }
-            ++ str;
-
-            if (*fmt)
-            {
-                if (str < end)
-                {
-                    *str = *fmt;
-                }
-                ++ str;
-            }
-            else
-            {
-                -- fmt;
-            }
-            continue;
-        }
-
-        if (qualifier == 'L')
-        {
-            num = va_arg(args, unsigned long long);
-        }
-        else if (qualifier == 'l')
-        {
-            num = va_arg(args, unsigned long);
-        }
-        else if (qualifier == 'H')
-        {
-            num = (rt_int8_t)va_arg(args, rt_int32_t);
-            if (flags & SIGN)
-            {
-                num = (rt_int8_t)num;
-            }
-        }
-        else if (qualifier == 'h')
-        {
-            num = (rt_uint16_t)va_arg(args, rt_int32_t);
-            if (flags & SIGN)
-            {
-                num = (rt_int16_t)num;
-            }
-        }
-        else if (qualifier == 'z')
-        {
-            num = va_arg(args, rt_size_t);
-            if (flags & SIGN)
-            {
-                num = (rt_ssize_t)num;
-            }
-        }
-        else
-        {
-            num = (rt_uint32_t)va_arg(args, unsigned long);
-        }
-        str = print_number(str, end, num, base, qualifier, field_width, precision, flags);
-    }
-
-    if (size > 0)
-    {
-        if (str < end)
-        {
-            *str = '\0';
-        }
-        else
-        {
-            end[-1] = '\0';
-        }
-    }
-
-    /* the trailing null byte doesn't count towards the total
-    * ++str;
-    */
-    return str - buf;
-}
-RTM_EXPORT(rt_vsnprintf);
-#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
-#pragma GCC diagnostic pop /* ignored "-Wimplicit-fallthrough" */
-#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
-
 /**
  * @brief  This function will fill a formatted string to buffer.
  *

+ 0 - 8
src/klibc/kstring.c

@@ -13,14 +13,6 @@
 #include <string.h>
 #endif /* RT_KLIBC_USING_STDLIB */
 
-#define DBG_TAG           "kernel.string"
-#ifdef RT_DEBUG_DEVICE
-#define DBG_LVL           DBG_LOG
-#else
-#define DBG_LVL           DBG_WARNING
-#endif /* defined (RT_DEBUG_DEVICE) */
-#include <rtdbg.h>
-
 /**
  * @brief  This function will set the content of memory to specified value.
  *

+ 1356 - 0
src/klibc/rt_vsnprintf_std.c

@@ -0,0 +1,1356 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-11-27     Meco Man     porting for rt_vsnprintf as the fully functional version
+ * 2024-11-19     Meco Man     move into rtklibc
+ */
+
+/**
+ * @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
+ *             2021-2022, Haifa, Palestine/Israel
+ * @author (c) Marco Paland (info@paland.com)
+ *             2014-2019, PALANDesign Hannover, Germany
+ *
+ * @note Others have made smaller contributions to this file: see the
+ * contributors page at https://github.com/eyalroz/printf/graphs/contributors
+ * or ask one of the authors. The original code for exponential specifiers was
+ * contributed by Martijn Jasperse <m.jasperse@gmail.com>.
+ *
+ * @brief Small stand-alone implementation of the printf family of functions
+ * (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with
+ * a very limited resources.
+ *
+ * @note the implementations are thread-safe; re-entrant; use no functions from
+ * the standard library; and do not dynamically allocate any memory.
+ *
+ * @license The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <rtthread.h>
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdbool.h>
+
+// 'ntoa' conversion buffer size, this must be big enough to hold one converted
+// numeric number including padded zeros (dynamically created on stack)
+#ifndef RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE
+#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE    32
+#endif
+
+// size of the fixed (on-stack) buffer for printing individual decimal numbers.
+// this must be big enough to hold one converted floating-point value including
+// padded zeros.
+#ifndef RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE
+#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE    32
+#endif
+
+// Support for the decimal notation floating point conversion specifiers (%f, %F)
+#ifndef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
+#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
+#endif
+
+// Support for the exponential notation floating point conversion specifiers (%e, %g, %E, %G)
+#ifndef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+#define RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+#endif
+
+// Support for the length write-back specifier (%n)
+#ifndef RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+#define RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+#endif
+
+// Default precision for the floating point conversion specifiers (the C standard sets this at 6)
+#ifndef RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION
+#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION  6
+#endif
+
+// According to the C languages standard, printf() and related functions must be able to print any
+// integral number in floating-point notation, regardless of length, when using the %f specifier -
+// possibly hundreds of characters, potentially overflowing your buffers. In this implementation,
+// all values beyond this threshold are switched to exponential notation.
+#ifndef RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL
+#define RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9
+#endif
+
+// Support for the long long integral types (with the ll, z and t length modifiers for specifiers
+// %d,%i,%o,%x,%X,%u, and with the %p specifier). Note: 'L' (long double) is not supported.
+#ifndef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
+#endif
+
+// The number of terms in a Taylor series expansion of log_10(x) to
+// use for approximation - including the power-zero term (i.e. the
+// value at the point of expansion).
+#ifndef RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS
+#define RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS 4
+#endif
+
+// Be extra-safe, and don't assume format specifiers are completed correctly
+// before the format string end.
+#if !defined(RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER) || defined(RT_USING_DEBUG)
+#define RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
+#endif
+
+#if RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS <= 1
+#error "At least one non-constant Taylor expansion is necessary for the log10() calculation"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define PRINTF_PREFER_DECIMAL     false
+#define PRINTF_PREFER_EXPONENTIAL true
+
+// The following will convert the number-of-digits into an exponential-notation literal
+#define PRINTF_CONCATENATE(s1, s2) s1##s2
+#define PRINTF_EXPAND_THEN_CONCATENATE(s1, s2) PRINTF_CONCATENATE(s1, s2)
+#define PRINTF_FLOAT_NOTATION_THRESHOLD PRINTF_EXPAND_THEN_CONCATENATE(1e,RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL)
+
+// internal flag definitions
+#define FLAGS_ZEROPAD   (1U <<  0U)
+#define FLAGS_LEFT      (1U <<  1U)
+#define FLAGS_PLUS      (1U <<  2U)
+#define FLAGS_SPACE     (1U <<  3U)
+#define FLAGS_HASH      (1U <<  4U)
+#define FLAGS_UPPERCASE (1U <<  5U)
+#define FLAGS_CHAR      (1U <<  6U)
+#define FLAGS_SHORT     (1U <<  7U)
+#define FLAGS_INT       (1U <<  8U)
+// Only used with RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+#define FLAGS_LONG      (1U <<  9U)
+#define FLAGS_LONG_LONG (1U << 10U)
+#define FLAGS_PRECISION (1U << 11U)
+#define FLAGS_ADAPT_EXP (1U << 12U)
+#define FLAGS_POINTER   (1U << 13U)
+// Note: Similar, but not identical, effect as FLAGS_HASH
+#define FLAGS_SIGNED    (1U << 14U)
+// Only used with RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+
+#define FLAGS_INT8 FLAGS_CHAR
+
+#if   (SHRT_MAX   == 32767LL)
+#define FLAGS_INT16       FLAGS_SHORT
+#elif (INT_MAX    == 32767LL)
+#define FLAGS_INT16       FLAGS_INT
+#elif (LONG_MAX   == 32767LL)
+#define FLAGS_INT16       FLAGS_LONG
+#elif (LLONG_MAX  == 32767LL)
+#define FLAGS_INT16       FLAGS_LONG_LONG
+#else
+#error "No basic integer type has a size of 16 bits exactly"
+#endif
+
+#if   (SHRT_MAX   == 2147483647LL)
+#define FLAGS_INT32       FLAGS_SHORT
+#elif (INT_MAX    == 2147483647LL)
+#define FLAGS_INT32       FLAGS_INT
+#elif (LONG_MAX   == 2147483647LL)
+#define FLAGS_INT32       FLAGS_LONG
+#elif (LLONG_MAX  == 2147483647LL)
+#define FLAGS_INT32       FLAGS_LONG_LONG
+#else
+#error "No basic integer type has a size of 32 bits exactly"
+#endif
+
+#if   (SHRT_MAX   == 9223372036854775807LL)
+#define FLAGS_INT64       FLAGS_SHORT
+#elif (INT_MAX    == 9223372036854775807LL)
+#define FLAGS_INT64       FLAGS_INT
+#elif (LONG_MAX   == 9223372036854775807LL)
+#define FLAGS_INT64       FLAGS_LONG
+#elif (LLONG_MAX  == 9223372036854775807LL)
+#define FLAGS_INT64       FLAGS_LONG_LONG
+#else
+#error "No basic integer type has a size of 64 bits exactly"
+#endif
+
+#endif // RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+
+
+typedef unsigned int printf_flags_t;
+
+#define BASE_BINARY    2
+#define BASE_OCTAL     8
+#define BASE_DECIMAL  10
+#define BASE_HEX      16
+
+typedef uint8_t numeric_base_t;
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+typedef unsigned long long printf_unsigned_value_t;
+typedef long long          printf_signed_value_t;
+#else
+typedef unsigned long printf_unsigned_value_t;
+typedef long          printf_signed_value_t;
+#endif
+
+// The printf()-family functions return an `int`; it is therefore
+// unnecessary/inappropriate to use size_t - often larger than int
+// in practice - for non-negative related values, such as widths,
+// precisions, offsets into buffers used for printing and the sizes
+// of these buffers. instead, we use:
+typedef unsigned int printf_size_t;
+#define PRINTF_MAX_POSSIBLE_BUFFER_SIZE INT_MAX
+  // If we were to nitpick, this would actually be INT_MAX + 1,
+  // since INT_MAX is the maximum return value, which excludes the
+  // trailing '\0'.
+
+#if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS)
+#include <float.h>
+#if FLT_RADIX != 2
+#error "Non-binary-radix floating-point types are unsupported."
+#endif
+
+#if DBL_MANT_DIG == 24
+
+#define DOUBLE_SIZE_IN_BITS 32
+typedef uint32_t double_uint_t;
+#define DOUBLE_EXPONENT_MASK 0xFFU
+#define DOUBLE_BASE_EXPONENT 127
+#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -38
+#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 1e-38
+
+#elif DBL_MANT_DIG == 53
+
+#define DOUBLE_SIZE_IN_BITS 64
+typedef uint64_t double_uint_t;
+#define DOUBLE_EXPONENT_MASK 0x7FFU
+#define DOUBLE_BASE_EXPONENT 1023
+#define DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10 -308
+#define DOUBLE_MAX_SUBNORMAL_POWER_OF_10 ((double)1e-308L)
+
+#else
+#error "Unsupported double type configuration"
+#endif
+#define DOUBLE_STORED_MANTISSA_BITS (DBL_MANT_DIG - 1)
+
+typedef union {
+  double_uint_t U;
+  double        F;
+} double_with_bit_access;
+
+// This is unnecessary in C99, since compound initializers can be used,
+// but:
+// 1. Some compilers are finicky about this;
+// 2. Some people may want to convert this to C89;
+// 3. If you try to use it as C++, only C++20 supports compound literals
+static inline double_with_bit_access get_bit_access(double x)
+{
+  double_with_bit_access dwba;
+  dwba.F = x;
+  return dwba;
+}
+
+static inline int get_sign_bit(double x)
+{
+  // The sign is stored in the highest bit
+  return (int) (get_bit_access(x).U >> (DOUBLE_SIZE_IN_BITS - 1));
+}
+
+static inline int get_exp2(double_with_bit_access x)
+{
+  // The exponent in an IEEE-754 floating-point number occupies a contiguous
+  // sequence of bits (e.g. 52..62 for 64-bit doubles), but with a non-trivial representation: An
+  // unsigned offset from some negative value (with the extremal offset values reserved for
+  // special use).
+  return (int)((x.U >> DOUBLE_STORED_MANTISSA_BITS ) & DOUBLE_EXPONENT_MASK) - DOUBLE_BASE_EXPONENT;
+}
+#define PRINTF_ABS(_x) ( (_x) > 0 ? (_x) : -(_x) )
+
+#endif // (RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS)
+
+// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid
+// and well-defined, but if you're not careful you can easily trigger undefined
+// behavior with -LONG_MIN or -LLONG_MIN
+#define ABS_FOR_PRINTING(_x) ((printf_unsigned_value_t) ( (_x) > 0 ? (_x) : -((printf_signed_value_t)_x) ))
+
+// wrapper (used as buffer) for output function type
+//
+// One of the following must hold:
+// 1. max_chars is 0
+// 2. buffer is non-null
+// 3. function is non-null
+//
+// ... otherwise bad things will happen.
+typedef struct {
+  void (*function)(char c, void* extra_arg);
+  void* extra_function_arg;
+  char* buffer;
+  printf_size_t pos;
+  printf_size_t max_chars;
+} output_gadget_t;
+
+// Note: This function currently assumes it is not passed a '\0' c,
+// or alternatively, that '\0' can be passed to the function in the output
+// gadget. The former assumption holds within the printf library. It also
+// assumes that the output gadget has been properly initialized.
+static inline void putchar_via_gadget(output_gadget_t* gadget, char c)
+{
+  printf_size_t write_pos = gadget->pos++;
+    // We're _always_ increasing pos, so as to count how may characters
+    // _would_ have been written if not for the max_chars limitation
+  if (write_pos >= gadget->max_chars) {
+    return;
+  }
+  if (gadget->function != NULL) {
+    // No check for c == '\0' .
+    gadget->function(c, gadget->extra_function_arg);
+  }
+  else {
+    // it must be the case that gadget->buffer != NULL , due to the constraint
+    // on output_gadget_t ; and note we're relying on write_pos being non-negative.
+    gadget->buffer[write_pos] = c;
+  }
+}
+
+// Possibly-write the string-terminating '\0' character
+static inline void append_termination_with_gadget(output_gadget_t* gadget)
+{
+  if (gadget->function != NULL || gadget->max_chars == 0) {
+    return;
+  }
+  if (gadget->buffer == NULL) {
+    return;
+  }
+  printf_size_t null_char_pos = gadget->pos < gadget->max_chars ? gadget->pos : gadget->max_chars - 1;
+  gadget->buffer[null_char_pos] = '\0';
+}
+
+static inline output_gadget_t discarding_gadget(void)
+{
+  output_gadget_t gadget;
+  gadget.function = NULL;
+  gadget.extra_function_arg = NULL;
+  gadget.buffer = NULL;
+  gadget.pos = 0;
+  gadget.max_chars = 0;
+  return gadget;
+}
+
+static inline output_gadget_t buffer_gadget(char* buffer, size_t buffer_size)
+{
+  printf_size_t usable_buffer_size = (buffer_size > PRINTF_MAX_POSSIBLE_BUFFER_SIZE) ?
+    PRINTF_MAX_POSSIBLE_BUFFER_SIZE : (printf_size_t) buffer_size;
+  output_gadget_t result = discarding_gadget();
+  if (buffer != NULL) {
+    result.buffer = buffer;
+    result.max_chars = usable_buffer_size;
+  }
+  return result;
+}
+
+// internal secure strlen
+// @return The length of the string (excluding the terminating 0) limited by 'maxsize'
+// @note strlen uses size_t, but wes only use this function with printf_size_t
+// variables - hence the signature.
+static inline printf_size_t strnlen_s_(const char* str, printf_size_t maxsize)
+{
+  const char* s;
+  for (s = str; *s && maxsize--; ++s);
+  return (printf_size_t)(s - str);
+}
+
+
+// internal test if char is a digit (0-9)
+// @return true if char is a digit
+static inline bool is_digit_(char ch)
+{
+  return (ch >= '0') && (ch <= '9');
+}
+
+
+// internal ASCII string to printf_size_t conversion
+static printf_size_t atou_(const char** str)
+{
+  printf_size_t i = 0U;
+  while (is_digit_(**str)) {
+    i = i * 10U + (printf_size_t)(*((*str)++) - '0');
+  }
+  return i;
+}
+
+
+// output the specified string in reverse, taking care of any zero-padding
+static void out_rev_(output_gadget_t* output, const char* buf, printf_size_t len, printf_size_t width, printf_flags_t flags)
+{
+  const printf_size_t start_pos = output->pos;
+
+  // pad spaces up to given width
+  if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+    for (printf_size_t i = len; i < width; i++) {
+      putchar_via_gadget(output, ' ');
+    }
+  }
+
+  // reverse string
+  while (len) {
+    putchar_via_gadget(output, buf[--len]);
+  }
+
+  // append pad spaces up to given width
+  if (flags & FLAGS_LEFT) {
+    while (output->pos - start_pos < width) {
+      putchar_via_gadget(output, ' ');
+    }
+  }
+}
+
+
+// Invoked by print_integer after the actual number has been printed, performing necessary
+// work on the number's prefix (as the number is initially printed in reverse order)
+static void print_integer_finalization(output_gadget_t* output, char* buf, printf_size_t len, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags)
+{
+  printf_size_t unpadded_len = len;
+
+  // pad with leading zeros
+  {
+    if (!(flags & FLAGS_LEFT)) {
+      if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+        width--;
+      }
+      while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) {
+        buf[len++] = '0';
+      }
+    }
+
+    while ((len < precision) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) {
+      buf[len++] = '0';
+    }
+
+    if (base == BASE_OCTAL && (len > unpadded_len)) {
+      // Since we've written some zeros, we've satisfied the alternative format leading space requirement
+      flags &= ~FLAGS_HASH;
+    }
+  }
+
+  // handle hash
+  if (flags & (FLAGS_HASH | FLAGS_POINTER)) {
+    if (!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) {
+      // Let's take back some padding digits to fit in what will eventually
+      // be the format-specific prefix
+      if (unpadded_len < len) {
+        len--; // This should suffice for BASE_OCTAL
+      }
+      if (len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) {
+        len--; // ... and an extra one for 0x or 0b
+      }
+    }
+    if ((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) {
+      buf[len++] = 'x';
+    }
+    else if ((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) {
+      buf[len++] = 'X';
+    }
+    else if ((base == BASE_BINARY) && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE)) {
+      buf[len++] = 'b';
+    }
+    if (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE) {
+      buf[len++] = '0';
+    }
+  }
+
+  if (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE) {
+    if (negative) {
+      buf[len++] = '-';
+    }
+    else if (flags & FLAGS_PLUS) {
+      buf[len++] = '+';  // ignore the space if the '+' exists
+    }
+    else if (flags & FLAGS_SPACE) {
+      buf[len++] = ' ';
+    }
+  }
+
+  out_rev_(output, buf, len, width, flags);
+}
+
+// An internal itoa-like function
+static void print_integer(output_gadget_t* output, printf_unsigned_value_t value, bool negative, numeric_base_t base, printf_size_t precision, printf_size_t width, printf_flags_t flags)
+{
+  char buf[RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE];
+  printf_size_t len = 0U;
+
+  if (!value) {
+    if ( !(flags & FLAGS_PRECISION) ) {
+      buf[len++] = '0';
+      flags &= ~FLAGS_HASH;
+      // We drop this flag this since either the alternative and regular modes of the specifier
+      // don't differ on 0 values, or (in the case of octal) we've already provided the special
+      // handling for this mode.
+    }
+    else if (base == BASE_HEX) {
+      flags &= ~FLAGS_HASH;
+      // We drop this flag this since either the alternative and regular modes of the specifier
+      // don't differ on 0 values
+    }
+  }
+  else {
+    do {
+      const char digit = (char)(value % base);
+      buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
+      value /= base;
+    } while (value && (len < RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE));
+  }
+
+  print_integer_finalization(output, buf, len, negative, base, precision, width, flags);
+}
+
+#if defined(RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS) || defined(RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS)
+
+// Stores a fixed-precision representation of a double relative
+// to a fixed precision (which cannot be determined by examining this structure)
+struct double_components {
+  int_fast64_t integral;
+  int_fast64_t fractional;
+    // ... truncation of the actual fractional part of the double value, scaled
+    // by the precision value
+  bool is_negative;
+};
+
+#define NUM_DECIMAL_DIGITS_IN_INT64_T 18
+#define PRINTF_MAX_PRECOMPUTED_POWER_OF_10  NUM_DECIMAL_DIGITS_IN_INT64_T
+static const double powers_of_10[NUM_DECIMAL_DIGITS_IN_INT64_T] = {
+  1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08,
+  1e09, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17
+};
+
+#define PRINTF_MAX_SUPPORTED_PRECISION NUM_DECIMAL_DIGITS_IN_INT64_T - 1
+
+
+// Break up a double number - which is known to be a finite non-negative number -
+// into its base-10 parts: integral - before the decimal point, and fractional - after it.
+// Taken the precision into account, but does not change it even internally.
+static struct double_components get_components(double number, printf_size_t precision)
+{
+  struct double_components number_;
+  number_.is_negative = get_sign_bit(number);
+  double abs_number = (number_.is_negative) ? -number : number;
+  number_.integral = (int_fast64_t)abs_number;
+  double remainder = (abs_number - (double) number_.integral) * powers_of_10[precision];
+  number_.fractional = (int_fast64_t)remainder;
+
+  remainder -= (double) number_.fractional;
+
+  if (remainder > 0.5) {
+    ++number_.fractional;
+    // handle rollover, e.g. case 0.99 with precision 1 is 1.0
+    if ((double) number_.fractional >= powers_of_10[precision]) {
+      number_.fractional = 0;
+      ++number_.integral;
+    }
+  }
+  else if ((remainder == 0.5) && ((number_.fractional == 0U) || (number_.fractional & 1U))) {
+    // if halfway, round up if odd OR if last digit is 0
+    ++number_.fractional;
+  }
+
+  if (precision == 0U) {
+    remainder = abs_number - (double) number_.integral;
+    if ((!(remainder < 0.5) || (remainder > 0.5)) && (number_.integral & 1)) {
+      // exactly 0.5 and ODD, then round up
+      // 1.5 -> 2, but 2.5 -> 2
+      ++number_.integral;
+    }
+  }
+  return number_;
+}
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+struct scaling_factor {
+  double raw_factor;
+  bool multiply; // if true, need to multiply by raw_factor; otherwise need to divide by it
+};
+
+static double apply_scaling(double num, struct scaling_factor normalization)
+{
+  return normalization.multiply ? num * normalization.raw_factor : num / normalization.raw_factor;
+}
+
+static double unapply_scaling(double normalized, struct scaling_factor normalization)
+{
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */
+// accounting for a static analysis bug in GCC 6.x and earlier
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+  return normalization.multiply ? normalized / normalization.raw_factor : normalized * normalization.raw_factor;
+#if defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */
+#pragma GCC diagnostic pop
+#endif
+}
+
+static struct scaling_factor update_normalization(struct scaling_factor sf, double extra_multiplicative_factor)
+{
+  struct scaling_factor result;
+  if (sf.multiply) {
+    result.multiply = true;
+    result.raw_factor = sf.raw_factor * extra_multiplicative_factor;
+  }
+  else {
+    int factor_exp2 = get_exp2(get_bit_access(sf.raw_factor));
+    int extra_factor_exp2 = get_exp2(get_bit_access(extra_multiplicative_factor));
+
+    // Divide the larger-exponent raw raw_factor by the smaller
+    if (PRINTF_ABS(factor_exp2) > PRINTF_ABS(extra_factor_exp2)) {
+      result.multiply = false;
+      result.raw_factor = sf.raw_factor / extra_multiplicative_factor;
+    }
+    else {
+      result.multiply = true;
+      result.raw_factor = extra_multiplicative_factor / sf.raw_factor;
+    }
+  }
+  return result;
+}
+
+static struct double_components get_normalized_components(bool negative, printf_size_t precision, double non_normalized, struct scaling_factor normalization, int floored_exp10)
+{
+  struct double_components components;
+  components.is_negative = negative;
+  double scaled = apply_scaling(non_normalized, normalization);
+
+  bool close_to_representation_extremum = ( (-floored_exp10 + (int) precision) >= DBL_MAX_10_EXP - 1 );
+  if (close_to_representation_extremum) {
+    // We can't have a normalization factor which also accounts for the precision, i.e. moves
+    // some decimal digits into the mantissa, since it's unrepresentable, or nearly unrepresentable.
+    // So, we'll give up early on getting extra precision...
+    return get_components(negative ? -scaled : scaled, precision);
+  }
+  components.integral = (int_fast64_t) scaled;
+  double remainder = non_normalized - unapply_scaling((double) components.integral, normalization);
+  double prec_power_of_10 = powers_of_10[precision];
+  struct scaling_factor account_for_precision = update_normalization(normalization, prec_power_of_10);
+  double scaled_remainder = apply_scaling(remainder, account_for_precision);
+  double rounding_threshold = 0.5;
+
+  components.fractional = (int_fast64_t) scaled_remainder; // when precision == 0, the assigned value should be 0
+  scaled_remainder -= (double) components.fractional; //when precision == 0, this will not change scaled_remainder
+
+  components.fractional += (scaled_remainder >= rounding_threshold);
+  if (scaled_remainder == rounding_threshold) {
+    // banker's rounding: Round towards the even number (making the mean error 0)
+    components.fractional &= ~((int_fast64_t) 0x1);
+  }
+  // handle rollover, e.g. the case of 0.99 with precision 1 becoming (0,100),
+  // and must then be corrected into (1, 0).
+  // Note: for precision = 0, this will "translate" the rounding effect from
+  // the fractional part to the integral part where it should actually be
+  // felt (as prec_power_of_10 is 1)
+  if ((double) components.fractional >= prec_power_of_10) {
+    components.fractional = 0;
+    ++components.integral;
+  }
+  return components;
+}
+#endif // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+
+static void print_broken_up_decimal(
+  struct double_components number_, output_gadget_t* output, printf_size_t precision,
+  printf_size_t width, printf_flags_t flags, char *buf, printf_size_t len)
+{
+  if (precision != 0U) {
+    // do fractional part, as an unsigned number
+
+    printf_size_t count = precision;
+
+    // %g/%G mandates we skip the trailing 0 digits...
+    if ((flags & FLAGS_ADAPT_EXP) && !(flags & FLAGS_HASH) && (number_.fractional > 0)) {
+      while(true) {
+        int_fast64_t digit = number_.fractional % 10U;
+        if (digit != 0) {
+          break;
+        }
+        --count;
+        number_.fractional /= 10U;
+
+      }
+      // ... and even the decimal point if there are no
+      // non-zero fractional part digits (see below)
+    }
+
+    if (number_.fractional > 0 || !(flags & FLAGS_ADAPT_EXP) || (flags & FLAGS_HASH) ) {
+      while (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) {
+        --count;
+        buf[len++] = (char)('0' + number_.fractional % 10U);
+        if (!(number_.fractional /= 10U)) {
+          break;
+        }
+      }
+      // add extra 0s
+      while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (count > 0U)) {
+        buf[len++] = '0';
+        --count;
+      }
+      if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) {
+        buf[len++] = '.';
+      }
+    }
+  }
+  else {
+    if ((flags & FLAGS_HASH) && (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) {
+      buf[len++] = '.';
+    }
+  }
+
+  // Write the integer part of the number (it comes after the fractional
+  // since the character order is reversed)
+  while (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) {
+    buf[len++] = (char)('0' + (number_.integral % 10));
+    if (!(number_.integral /= 10)) {
+      break;
+    }
+  }
+
+  // pad leading zeros
+  if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+    if (width && (number_.is_negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+      width--;
+    }
+    while ((len < width) && (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE)) {
+      buf[len++] = '0';
+    }
+  }
+
+  if (len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) {
+    if (number_.is_negative) {
+      buf[len++] = '-';
+    }
+    else if (flags & FLAGS_PLUS) {
+      buf[len++] = '+';  // ignore the space if the '+' exists
+    }
+    else if (flags & FLAGS_SPACE) {
+      buf[len++] = ' ';
+    }
+  }
+
+  out_rev_(output, buf, len, width, flags);
+}
+
+      // internal ftoa for fixed decimal floating point
+static void print_decimal_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
+{
+  struct double_components value_ = get_components(number, precision);
+  print_broken_up_decimal(value_, output, precision, width, flags, buf, len);
+}
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+
+// A floor function - but one which only works for numbers whose
+// floor value is representable by an int.
+static int bastardized_floor(double x)
+{
+  if (x >= 0) { return (int) x; }
+  int n = (int) x;
+  return ( ((double) n) == x ) ? n : n-1;
+}
+
+// Computes the base-10 logarithm of the input number - which must be an actual
+// positive number (not infinity or NaN, nor a sub-normal)
+static double log10_of_positive(double positive_number)
+{
+  // The implementation follows David Gay (https://www.ampl.com/netlib/fp/dtoa.c).
+  //
+  // Since log_10 ( M * 2^x ) = log_10(M) + x , we can separate the components of
+  // our input number, and need only solve log_10(M) for M between 1 and 2 (as
+  // the base-2 mantissa is always 1-point-something). In that limited range, a
+  // Taylor series expansion of log10(x) should serve us well enough; and we'll
+  // take the mid-point, 1.5, as the point of expansion.
+
+  double_with_bit_access dwba = get_bit_access(positive_number);
+  // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
+  int exp2 = get_exp2(dwba);
+  // drop the exponent, so dwba.F comes into the range [1,2)
+  dwba.U = (dwba.U & (((double_uint_t) (1) << DOUBLE_STORED_MANTISSA_BITS) - 1U)) |
+           ((double_uint_t) DOUBLE_BASE_EXPONENT << DOUBLE_STORED_MANTISSA_BITS);
+  double z = (dwba.F - 1.5);
+  return (
+    // Taylor expansion around 1.5:
+    0.1760912590556812420           // Expansion term 0: ln(1.5)            / ln(10)
+    + z     * 0.2895296546021678851 // Expansion term 1: (M - 1.5)   * 2/3  / ln(10)
+#if RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS > 2
+    - z*z   * 0.0965098848673892950 // Expansion term 2: (M - 1.5)^2 * 2/9  / ln(10)
+#if RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS > 3
+    + z*z*z * 0.0428932821632841311 // Expansion term 2: (M - 1.5)^3 * 8/81 / ln(10)
+#endif
+#endif
+    // exact log_2 of the exponent x, with logarithm base change
+    + exp2 * 0.30102999566398119521 // = exp2 * log_10(2) = exp2 * ln(2)/ln(10)
+  );
+}
+
+
+static double pow10_of_int(int floored_exp10)
+{
+  // A crude hack for avoiding undesired behavior with barely-normal or slightly-subnormal values.
+  if (floored_exp10 == DOUBLE_MAX_SUBNORMAL_EXPONENT_OF_10) {
+    return DOUBLE_MAX_SUBNORMAL_POWER_OF_10;
+  }
+  // Compute 10^(floored_exp10) but (try to) make sure that doesn't overflow
+  double_with_bit_access dwba;
+  int exp2 = bastardized_floor(floored_exp10 * 3.321928094887362 + 0.5);
+  const double z  = floored_exp10 * 2.302585092994046 - exp2 * 0.6931471805599453;
+  const double z2 = z * z;
+  dwba.U = ((double_uint_t)(exp2) + DOUBLE_BASE_EXPONENT) << DOUBLE_STORED_MANTISSA_BITS;
+  // compute exp(z) using continued fractions,
+  // see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+  dwba.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+  return dwba.F;
+}
+
+static void print_exponential_number(output_gadget_t* output, double number, printf_size_t precision, printf_size_t width, printf_flags_t flags, char* buf, printf_size_t len)
+{
+  const bool negative = get_sign_bit(number);
+  // This number will decrease gradually (by factors of 10) as we "extract" the exponent out of it
+  double abs_number =  negative ? -number : number;
+
+  int floored_exp10;
+  bool abs_exp10_covered_by_powers_table;
+  struct scaling_factor normalization;
+
+
+  // Determine the decimal exponent
+  if (abs_number == 0.0) {
+    // TODO: This is a special-case for 0.0 (and -0.0); but proper handling is required for denormals more generally.
+    floored_exp10 = 0; // ... and no need to set a normalization factor or check the powers table
+  }
+  else  {
+    double exp10 = log10_of_positive(abs_number);
+    floored_exp10 = bastardized_floor(exp10);
+    double p10 = pow10_of_int(floored_exp10);
+    // correct for rounding errors
+    if (abs_number < p10) {
+      floored_exp10--;
+      p10 /= 10;
+    }
+    abs_exp10_covered_by_powers_table = PRINTF_ABS(floored_exp10) < PRINTF_MAX_PRECOMPUTED_POWER_OF_10;
+    normalization.raw_factor = abs_exp10_covered_by_powers_table ? powers_of_10[PRINTF_ABS(floored_exp10)] : p10;
+  }
+
+  // We now begin accounting for the widths of the two parts of our printed field:
+  // the decimal part after decimal exponent extraction, and the base-10 exponent part.
+  // For both of these, the value of 0 has a special meaning, but not the same one:
+  // a 0 exponent-part width means "don't print the exponent"; a 0 decimal-part width
+  // means "use as many characters as necessary".
+
+  bool fall_back_to_decimal_only_mode = false;
+  if (flags & FLAGS_ADAPT_EXP) {
+    int required_significant_digits = (precision == 0) ? 1 : (int) precision;
+    // Should we want to fall-back to "%f" mode, and only print the decimal part?
+    fall_back_to_decimal_only_mode = (floored_exp10 >= -4 && floored_exp10 < required_significant_digits);
+    // Now, let's adjust the precision
+    // This also decided how we adjust the precision value - as in "%g" mode,
+    // "precision" is the number of _significant digits_, and this is when we "translate"
+    // the precision value to an actual number of decimal digits.
+    int precision_ = fall_back_to_decimal_only_mode ?
+                     (int) precision - 1 - floored_exp10 :
+        (int) precision - 1; // the presence of the exponent ensures only one significant digit comes before the decimal point
+    precision = (precision_ > 0 ? (unsigned) precision_ : 0U);
+    flags |= FLAGS_PRECISION;   // make sure print_broken_up_decimal respects our choice above
+  }
+
+  normalization.multiply = (floored_exp10 < 0 && abs_exp10_covered_by_powers_table);
+  bool should_skip_normalization = (fall_back_to_decimal_only_mode || floored_exp10 == 0);
+  struct double_components decimal_part_components =
+    should_skip_normalization ?
+    get_components(negative ? -abs_number : abs_number, precision) :
+    get_normalized_components(negative, precision, abs_number, normalization, floored_exp10);
+
+  // Account for roll-over, e.g. rounding from 9.99 to 100.0 - which effects
+  // the exponent and may require additional tweaking of the parts
+  if (fall_back_to_decimal_only_mode) {
+    if ((flags & FLAGS_ADAPT_EXP) && floored_exp10 >= -1 && decimal_part_components.integral == powers_of_10[floored_exp10 + 1]) {
+      floored_exp10++; // Not strictly necessary, since floored_exp10 is no longer really used
+      precision--;
+      // ... and it should already be the case that decimal_part_components.fractional == 0
+    }
+    // TODO: What about rollover strictly within the fractional part?
+  }
+  else {
+    if (decimal_part_components.integral >= 10) {
+      floored_exp10++;
+      decimal_part_components.integral = 1;
+      decimal_part_components.fractional = 0;
+    }
+  }
+
+  // the floored_exp10 format is "E%+03d" and largest possible floored_exp10 value for a 64-bit double
+  // is "307" (for 2^1023), so we set aside 4-5 characters overall
+  printf_size_t exp10_part_width = fall_back_to_decimal_only_mode ? 0U : (PRINTF_ABS(floored_exp10) < 100) ? 4U : 5U;
+
+  printf_size_t decimal_part_width =
+    ((flags & FLAGS_LEFT) && exp10_part_width) ?
+      // We're padding on the right, so the width constraint is the exponent part's
+      // problem, not the decimal part's, so we'll use as many characters as we need:
+      0U :
+      // We're padding on the left; so the width constraint is the decimal part's
+      // problem. Well, can both the decimal part and the exponent part fit within our overall width?
+      ((width > exp10_part_width) ?
+        // Yes, so we limit our decimal part's width.
+        // (Note this is trivially valid even if we've fallen back to "%f" mode)
+        width - exp10_part_width :
+        // No; we just give up on any restriction on the decimal part and use as many
+        // characters as we need
+        0U);
+
+  const printf_size_t printed_exponential_start_pos = output->pos;
+  print_broken_up_decimal(decimal_part_components, output, precision, decimal_part_width, flags, buf, len);
+
+  if (! fall_back_to_decimal_only_mode) {
+    putchar_via_gadget(output, (flags & FLAGS_UPPERCASE) ? 'E' : 'e');
+    print_integer(output,
+                  ABS_FOR_PRINTING(floored_exp10),
+                  floored_exp10 < 0, 10, 0, exp10_part_width - 1,
+                FLAGS_ZEROPAD | FLAGS_PLUS);
+    if (flags & FLAGS_LEFT) {
+      // We need to right-pad with spaces to meet the width requirement
+      while (output->pos - printed_exponential_start_pos < width) {
+        putchar_via_gadget(output, ' ');
+      }
+    }
+  }
+}
+#endif  // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+
+static void print_floating_point(output_gadget_t* output, double value, printf_size_t precision, printf_size_t width, printf_flags_t flags, bool prefer_exponential)
+{
+  char buf[RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE];
+  printf_size_t len = 0U;
+
+  // test for special values
+  if (value != value) {
+    out_rev_(output, "nan", 3, width, flags);
+    return;
+  }
+  if (value < -DBL_MAX) {
+    out_rev_(output, "fni-", 4, width, flags);
+    return;
+  }
+  if (value > DBL_MAX) {
+    out_rev_(output, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
+    return;
+  }
+
+  if (!prefer_exponential &&
+      ((value > PRINTF_FLOAT_NOTATION_THRESHOLD) || (value < -PRINTF_FLOAT_NOTATION_THRESHOLD))) {
+    // The required behavior of standard printf is to print _every_ integral-part digit -- which could mean
+    // printing hundreds of characters, overflowing any fixed internal buffer and necessitating a more complicated
+    // implementation.
+#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+    print_exponential_number(output, value, precision, width, flags, buf, len);
+#endif
+    return;
+  }
+
+  // set default precision, if not set explicitly
+  if (!(flags & FLAGS_PRECISION)) {
+    precision = RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION;
+  }
+
+  // limit precision so that our integer holding the fractional part does not overflow
+  while ((len < RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE) && (precision > PRINTF_MAX_SUPPORTED_PRECISION)) {
+    buf[len++] = '0'; // This respects the precision in terms of result length only
+    precision--;
+  }
+
+#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+  if (prefer_exponential)
+    print_exponential_number(output, value, precision, width, flags, buf, len);
+  else
+#endif
+    print_decimal_number(output, value, precision, width, flags, buf, len);
+}
+
+#endif  // (RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS || RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS)
+
+// Advances the format pointer past the flags, and returns the parsed flags
+// due to the characters passed
+static printf_flags_t parse_flags(const char** format)
+{
+  printf_flags_t flags = 0U;
+  do {
+    switch (**format) {
+      case '0': flags |= FLAGS_ZEROPAD; (*format)++; break;
+      case '-': flags |= FLAGS_LEFT;    (*format)++; break;
+      case '+': flags |= FLAGS_PLUS;    (*format)++; break;
+      case ' ': flags |= FLAGS_SPACE;   (*format)++; break;
+      case '#': flags |= FLAGS_HASH;    (*format)++; break;
+      default : return flags;
+    }
+  } while (true);
+}
+
+static inline void format_string_loop(output_gadget_t* output, const char* format, va_list args)
+{
+#ifdef RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
+#define ADVANCE_IN_FORMAT_STRING(cptr_) do { (cptr_)++; if (!*(cptr_)) return; } while(0)
+#else
+#define ADVANCE_IN_FORMAT_STRING(cptr_) (cptr_)++
+#endif
+
+
+  while (*format)
+  {
+    if (*format != '%') {
+      // A regular content character
+      putchar_via_gadget(output, *format);
+      format++;
+      continue;
+    }
+    // We're parsing a format specifier: %[flags][width][.precision][length]
+    ADVANCE_IN_FORMAT_STRING(format);
+
+    printf_flags_t flags = parse_flags(&format);
+
+    // evaluate width field
+    printf_size_t width = 0U;
+    if (is_digit_(*format)) {
+      width = (printf_size_t) atou_(&format);
+    }
+    else if (*format == '*') {
+      const int w = va_arg(args, int);
+      if (w < 0) {
+        flags |= FLAGS_LEFT;    // reverse padding
+        width = (printf_size_t)-w;
+      }
+      else {
+        width = (printf_size_t)w;
+      }
+      ADVANCE_IN_FORMAT_STRING(format);
+    }
+
+    // evaluate precision field
+    printf_size_t precision = 0U;
+    if (*format == '.') {
+      flags |= FLAGS_PRECISION;
+      ADVANCE_IN_FORMAT_STRING(format);
+      if (is_digit_(*format)) {
+        precision = (printf_size_t) atou_(&format);
+      }
+      else if (*format == '*') {
+        const int precision_ = va_arg(args, int);
+        precision = precision_ > 0 ? (printf_size_t) precision_ : 0U;
+        ADVANCE_IN_FORMAT_STRING(format);
+      }
+    }
+
+    // evaluate length field
+    switch (*format) {
+#ifdef RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS
+      case 'I' : {
+        ADVANCE_IN_FORMAT_STRING(format);
+        // Greedily parse for size in bits: 8, 16, 32 or 64
+        switch(*format) {
+          case '8':               flags |= FLAGS_INT8;
+            ADVANCE_IN_FORMAT_STRING(format);
+            break;
+          case '1':
+            ADVANCE_IN_FORMAT_STRING(format);
+          if (*format == '6') { format++; flags |= FLAGS_INT16; }
+            break;
+          case '3':
+            ADVANCE_IN_FORMAT_STRING(format);
+            if (*format == '2') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT32; }
+            break;
+          case '6':
+            ADVANCE_IN_FORMAT_STRING(format);
+            if (*format == '4') { ADVANCE_IN_FORMAT_STRING(format); flags |= FLAGS_INT64; }
+            break;
+          default: break;
+        }
+        break;
+      }
+#endif
+      case 'l' :
+        flags |= FLAGS_LONG;
+        ADVANCE_IN_FORMAT_STRING(format);
+        if (*format == 'l') {
+          flags |= FLAGS_LONG_LONG;
+          ADVANCE_IN_FORMAT_STRING(format);
+        }
+        break;
+      case 'h' :
+        flags |= FLAGS_SHORT;
+        ADVANCE_IN_FORMAT_STRING(format);
+        if (*format == 'h') {
+          flags |= FLAGS_CHAR;
+          ADVANCE_IN_FORMAT_STRING(format);
+        }
+        break;
+      case 't' :
+        flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+        ADVANCE_IN_FORMAT_STRING(format);
+        break;
+      case 'j' :
+        flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+        ADVANCE_IN_FORMAT_STRING(format);
+        break;
+      case 'z' :
+        flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+        ADVANCE_IN_FORMAT_STRING(format);
+        break;
+      default:
+        break;
+    }
+
+    // evaluate specifier
+    switch (*format) {
+      case 'd' :
+      case 'i' :
+      case 'u' :
+      case 'x' :
+      case 'X' :
+      case 'o' :
+      case 'b' : {
+
+        if (*format == 'd' || *format == 'i') {
+          flags |= FLAGS_SIGNED;
+        }
+
+        numeric_base_t base;
+        if (*format == 'x' || *format == 'X') {
+          base = BASE_HEX;
+        }
+        else if (*format == 'o') {
+          base =  BASE_OCTAL;
+        }
+        else if (*format == 'b') {
+          base =  BASE_BINARY;
+        }
+        else {
+          base = BASE_DECIMAL;
+          flags &= ~FLAGS_HASH; // decimal integers have no alternative presentation
+        }
+
+        if (*format == 'X') {
+          flags |= FLAGS_UPPERCASE;
+        }
+
+        format++;
+        // ignore '0' flag when precision is given
+        if (flags & FLAGS_PRECISION) {
+          flags &= ~FLAGS_ZEROPAD;
+        }
+
+        if (flags & FLAGS_SIGNED) {
+          // A signed specifier: d, i or possibly I + bit size if enabled
+
+          if (flags & FLAGS_LONG_LONG) {
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+            const long long value = va_arg(args, long long);
+            print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags);
+#endif
+          }
+          else if (flags & FLAGS_LONG) {
+            const long value = va_arg(args, long);
+            print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags);
+          }
+          else {
+            // We never try to interpret the argument as something potentially-smaller than int,
+            // due to integer promotion rules: Even if the user passed a short int, short unsigned
+            // etc. - these will come in after promotion, as int's (or unsigned for the case of
+            // short unsigned when it has the same size as int)
+            const int value =
+              (flags & FLAGS_CHAR) ? (signed char) va_arg(args, int) :
+              (flags & FLAGS_SHORT) ? (short int) va_arg(args, int) :
+              va_arg(args, int);
+            print_integer(output, ABS_FOR_PRINTING(value), value < 0, base, precision, width, flags);
+          }
+        }
+        else {
+          // An unsigned specifier: u, x, X, o, b
+
+          flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+
+          if (flags & FLAGS_LONG_LONG) {
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+            print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long long), false, base, precision, width, flags);
+#endif
+          }
+          else if (flags & FLAGS_LONG) {
+            print_integer(output, (printf_unsigned_value_t) va_arg(args, unsigned long), false, base, precision, width, flags);
+          }
+          else {
+            const unsigned int value =
+              (flags & FLAGS_CHAR) ? (unsigned char)va_arg(args, unsigned int) :
+              (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(args, unsigned int) :
+              va_arg(args, unsigned int);
+            print_integer(output, (printf_unsigned_value_t) value, false, base, precision, width, flags);
+          }
+        }
+        break;
+      }
+#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
+      case 'f' :
+      case 'F' :
+        if (*format == 'F') flags |= FLAGS_UPPERCASE;
+        print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_DECIMAL);
+        format++;
+        break;
+#endif
+#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+      case 'e':
+      case 'E':
+      case 'g':
+      case 'G':
+        if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
+        if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
+        print_floating_point(output, va_arg(args, double), precision, width, flags, PRINTF_PREFER_EXPONENTIAL);
+        format++;
+        break;
+#endif  // RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+      case 'c' : {
+        printf_size_t l = 1U;
+        // pre padding
+        if (!(flags & FLAGS_LEFT)) {
+          while (l++ < width) {
+            putchar_via_gadget(output, ' ');
+          }
+        }
+        // char output
+        putchar_via_gadget(output, (char) va_arg(args, int) );
+        // post padding
+        if (flags & FLAGS_LEFT) {
+          while (l++ < width) {
+            putchar_via_gadget(output, ' ');
+          }
+        }
+        format++;
+        break;
+      }
+
+      case 's' : {
+        const char* p = va_arg(args, char*);
+        if (p == NULL) {
+          out_rev_(output, ")llun(", 6, width, flags);
+        }
+        else {
+          printf_size_t l = strnlen_s_(p, precision ? precision : PRINTF_MAX_POSSIBLE_BUFFER_SIZE);
+          // pre padding
+          if (flags & FLAGS_PRECISION) {
+            l = (l < precision ? l : precision);
+          }
+          if (!(flags & FLAGS_LEFT)) {
+            while (l++ < width) {
+              putchar_via_gadget(output, ' ');
+            }
+          }
+          // string output
+          while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision)) {
+            putchar_via_gadget(output, *(p++));
+            --precision;
+          }
+          // post padding
+          if (flags & FLAGS_LEFT) {
+            while (l++ < width) {
+              putchar_via_gadget(output, ' ');
+            }
+          }
+        }
+        format++;
+        break;
+      }
+
+      case 'p' : {
+        width = sizeof(void*) * 2U + 2; // 2 hex chars per byte + the "0x" prefix
+        flags |= FLAGS_ZEROPAD | FLAGS_POINTER;
+        uintptr_t value = (uintptr_t)va_arg(args, void*);
+        (value == (uintptr_t) NULL) ?
+          out_rev_(output, ")lin(", 5, width, flags) :
+          print_integer(output, (printf_unsigned_value_t) value, false, BASE_HEX, precision, width, flags);
+        format++;
+        break;
+      }
+
+      case '%' :
+        putchar_via_gadget(output, '%');
+        format++;
+        break;
+
+      // Many people prefer to disable support for %n, as it lets the caller
+      // engineer a write to an arbitrary location, of a value the caller
+      // effectively controls - which could be a security concern in some cases.
+#ifdef RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+      case 'n' : {
+        if       (flags & FLAGS_CHAR)      *(va_arg(args, char*))      = (char) output->pos;
+        else if  (flags & FLAGS_SHORT)     *(va_arg(args, short*))     = (short) output->pos;
+        else if  (flags & FLAGS_LONG)      *(va_arg(args, long*))      = (long) output->pos;
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+        else if  (flags & FLAGS_LONG_LONG) *(va_arg(args, long long*)) = (long long int) output->pos;
+#endif // RT_KLIBC_USING_VSNPRINTF_LONGLONG
+        else                               *(va_arg(args, int*))       = (int) output->pos;
+        format++;
+        break;
+      }
+#endif // RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+
+      default :
+        putchar_via_gadget(output, *format);
+        format++;
+        break;
+    }
+  }
+}
+
+// internal vsnprintf - used for implementing _all library functions
+static int vsnprintf_impl(output_gadget_t* output, const char* format, va_list args)
+{
+  // Note: The library only calls vsnprintf_impl() with output->pos being 0. However, it is
+  // possible to call this function with a non-zero pos value for some "remedial printing".
+  format_string_loop(output, format, args);
+
+  // termination
+  append_termination_with_gadget(output);
+
+  // return written chars without terminating \0
+  return (int)output->pos;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief  This function will fill a formatted string to buffer.
+ *
+ * @param  buf is the buffer to save formatted string.
+ *
+ * @param  size is the size of buffer.
+ *
+ * @param  fmt is the format parameters.
+ *
+ * @param  args is a list of variable parameters.
+ *
+ * @return The number of characters actually written to buffer.
+ */
+int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
+{
+  output_gadget_t gadget = buffer_gadget(buf, size);
+  return vsnprintf_impl(&gadget, fmt, args);
+}
+
+#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */

+ 616 - 0
src/klibc/rt_vsnprintf_tiny.c

@@ -0,0 +1,616 @@
+/*
+ * Copyright (c) 2006-2024, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2024-11-19     Meco Man     the first version
+ */
+
+#include <rtthread.h>
+
+#ifndef RT_KLIBC_USING_VSNPRINTF_STANDARD
+
+#define _ISDIGIT(c)  ((unsigned)((c) - '0') < 10)
+
+/**
+ * @brief  This function will duplicate a string.
+ *
+ * @param  n is the string to be duplicated.
+ *
+ * @param  base is support divide instructions value.
+ *
+ * @return the duplicated string pointer.
+ */
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+rt_inline int divide(unsigned long long *n, int base)
+#else
+rt_inline int divide(unsigned long *n, int base)
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+{
+    int res;
+
+    /* optimized for processor which does not support divide instructions. */
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+    res = (int)((*n) % base);
+    *n = (long long)((*n) / base);
+#else
+    res = (int)((*n) % base);
+    *n = (long)((*n) / base);
+#endif
+
+    return res;
+}
+
+rt_inline int skip_atoi(const char **s)
+{
+    int i = 0;
+    while (_ISDIGIT(**s))
+        i = i * 10 + *((*s)++) - '0';
+
+    return i;
+}
+
+#define ZEROPAD     (1 << 0)    /* pad with zero */
+#define SIGN        (1 << 1)    /* unsigned/signed long */
+#define PLUS        (1 << 2)    /* show plus */
+#define SPACE       (1 << 3)    /* space if plus */
+#define LEFT        (1 << 4)    /* left justified */
+#define SPECIAL     (1 << 5)    /* 0x */
+#define LARGE       (1 << 6)    /* use 'ABCDEF' instead of 'abcdef' */
+
+static char *print_number(char *buf,
+                          char *end,
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+                          unsigned long long  num,
+#else
+                          unsigned long  num,
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+                          int   base,
+                          int   qualifier,
+                          int   s,
+                          int   precision,
+                          int   type)
+{
+    char c = 0, sign = 0;
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+    char tmp[64] = {0};
+#else
+    char tmp[32] = {0};
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+    int precision_bak = precision;
+    const char *digits = RT_NULL;
+    static const char small_digits[] = "0123456789abcdef";
+    static const char large_digits[] = "0123456789ABCDEF";
+    int i = 0;
+    int size = 0;
+
+    size = s;
+
+    digits = (type & LARGE) ? large_digits : small_digits;
+    if (type & LEFT)
+    {
+        type &= ~ZEROPAD;
+    }
+
+    c = (type & ZEROPAD) ? '0' : ' ';
+
+    /* get sign */
+    sign = 0;
+    if (type & SIGN)
+    {
+        switch (qualifier)
+        {
+        case 'h':
+            if ((rt_int16_t)num < 0)
+            {
+                sign = '-';
+                num = (rt_uint16_t)-num;
+            }
+            break;
+        case 'L':
+        case 'l':
+            if ((long)num < 0)
+            {
+                sign = '-';
+                num = (unsigned long)-num;
+            }
+            break;
+        case 0:
+        default:
+            if ((rt_int32_t)num < 0)
+            {
+                sign = '-';
+                num = (rt_uint32_t)-num;
+            }
+            break;
+        }
+
+        if (sign != '-')
+        {
+            if (type & PLUS)
+            {
+                sign = '+';
+            }
+            else if (type & SPACE)
+            {
+                sign = ' ';
+            }
+        }
+    }
+
+    if (type & SPECIAL)
+    {
+        if (base == 2 || base == 16)
+        {
+            size -= 2;
+        }
+        else if (base == 8)
+        {
+            size--;
+        }
+    }
+
+    i = 0;
+    if (num == 0)
+    {
+        tmp[i++] = '0';
+    }
+    else
+    {
+        while (num != 0)
+            tmp[i++] = digits[divide(&num, base)];
+    }
+
+    if (i > precision)
+    {
+        precision = i;
+    }
+    size -= precision;
+
+    if (!(type & (ZEROPAD | LEFT)))
+    {
+        if ((sign) && (size > 0))
+        {
+            size--;
+        }
+
+        while (size-- > 0)
+        {
+            if (buf < end)
+            {
+                *buf = ' ';
+            }
+
+            ++ buf;
+        }
+    }
+
+    if (sign)
+    {
+        if (buf < end)
+        {
+            *buf = sign;
+        }
+        -- size;
+        ++ buf;
+    }
+
+    if (type & SPECIAL)
+    {
+        if (base == 2)
+        {
+            if (buf < end)
+                *buf = '0';
+            ++ buf;
+            if (buf < end)
+                *buf = 'b';
+            ++ buf;
+        }
+        else if (base == 8)
+        {
+            if (buf < end)
+                *buf = '0';
+            ++ buf;
+        }
+        else if (base == 16)
+        {
+            if (buf < end)
+            {
+                *buf = '0';
+            }
+
+            ++ buf;
+            if (buf < end)
+            {
+                *buf = type & LARGE ? 'X' : 'x';
+            }
+            ++ buf;
+        }
+    }
+
+    /* no align to the left */
+    if (!(type & LEFT))
+    {
+        while (size-- > 0)
+        {
+            if (buf < end)
+            {
+                *buf = c;
+            }
+
+            ++ buf;
+        }
+    }
+
+    while (i < precision--)
+    {
+        if (buf < end)
+        {
+            *buf = '0';
+        }
+
+        ++ buf;
+    }
+
+    /* put number in the temporary buffer */
+    while (i-- > 0 && (precision_bak != 0))
+    {
+        if (buf < end)
+        {
+            *buf = tmp[i];
+        }
+
+        ++ buf;
+    }
+
+    while (size-- > 0)
+    {
+        if (buf < end)
+        {
+            *buf = ' ';
+        }
+
+        ++ buf;
+    }
+
+    return buf;
+}
+
+#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
+/* Disable "-Wimplicit-fallthrough" below GNUC V7 */
+#pragma GCC diagnostic push
+/* ignore warning: this statement may fall through */
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
+/**
+ * @brief  This function will fill a formatted string to buffer.
+ *
+ * @param  buf is the buffer to save formatted string.
+ *
+ * @param  size is the size of buffer.
+ *
+ * @param  fmt is the format parameters.
+ *
+ * @param  args is a list of variable parameters.
+ *
+ * @return The number of characters actually written to buffer.
+ */
+int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
+{
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+    unsigned long long num = 0;
+#else
+    unsigned long num = 0;
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+    int i = 0, len = 0;
+    char *str = RT_NULL, *end = RT_NULL, c = 0;
+    const char *s = RT_NULL;
+
+    rt_uint8_t base = 0;            /* the base of number */
+    rt_uint8_t flags = 0;           /* flags to print number */
+    rt_uint8_t qualifier = 0;       /* 'h', 'l', or 'L' for integer fields */
+    rt_int32_t field_width = 0;     /* width of output field */
+    int precision = 0;      /* min. # of digits for integers and max for a string */
+
+    str = buf;
+    end = buf + size;
+
+    /* Make sure end is always >= buf */
+    if (end < buf)
+    {
+        end  = ((char *) - 1);
+        size = end - buf;
+    }
+
+    for (; *fmt ; ++fmt)
+    {
+        if (*fmt != '%')
+        {
+            if (str < end)
+            {
+                *str = *fmt;
+            }
+
+            ++ str;
+            continue;
+        }
+
+        /* process flags */
+        flags = 0;
+
+        while (1)
+        {
+            /* skips the first '%' also */
+            ++fmt;
+            if (*fmt == '-') flags |= LEFT;
+            else if (*fmt == '+') flags |= PLUS;
+            else if (*fmt == ' ') flags |= SPACE;
+            else if (*fmt == '#') flags |= SPECIAL;
+            else if (*fmt == '0') flags |= ZEROPAD;
+            else break;
+        }
+
+        /* get field width */
+        field_width = -1;
+        if (_ISDIGIT(*fmt))
+        {
+            field_width = skip_atoi(&fmt);
+        }
+        else if (*fmt == '*')
+        {
+            ++fmt;
+            /* it's the next argument */
+            field_width = va_arg(args, int);
+            if (field_width < 0)
+            {
+                field_width = -field_width;
+                flags |= LEFT;
+            }
+        }
+
+        /* get the precision */
+        precision = -1;
+        if (*fmt == '.')
+        {
+            ++fmt;
+            if (_ISDIGIT(*fmt))
+            {
+                precision = skip_atoi(&fmt);
+            }
+            else if (*fmt == '*')
+            {
+                ++fmt;
+                /* it's the next argument */
+                precision = va_arg(args, int);
+            }
+            if (precision < 0)
+            {
+                precision = 0;
+            }
+        }
+
+        qualifier = 0; /* get the conversion qualifier */
+
+        if (*fmt == 'h' || *fmt == 'l' ||
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+            *fmt == 'L' ||
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+            *fmt == 'z')
+        {
+            qualifier = *fmt;
+            ++fmt;
+#ifdef RT_KLIBC_USING_VSNPRINTF_LONGLONG
+            if (qualifier == 'l' && *fmt == 'l')
+            {
+                qualifier = 'L';
+                ++fmt;
+            }
+#endif /* RT_KLIBC_USING_VSNPRINTF_LONGLONG */
+            if (qualifier == 'h' && *fmt == 'h')
+            {
+                qualifier = 'H';
+                ++fmt;
+            }
+        }
+
+        /* the default base */
+        base = 10;
+
+        switch (*fmt)
+        {
+        case 'c':
+            if (!(flags & LEFT))
+            {
+                while (--field_width > 0)
+                {
+                    if (str < end) *str = ' ';
+                    ++ str;
+                }
+            }
+
+            /* get character */
+            c = (rt_uint8_t)va_arg(args, int);
+            if (str < end)
+            {
+                *str = c;
+            }
+            ++ str;
+
+            /* put width */
+            while (--field_width > 0)
+            {
+                if (str < end) *str = ' ';
+                ++ str;
+            }
+            continue;
+
+        case 's':
+            s = va_arg(args, char *);
+            if (!s)
+            {
+                s = "(NULL)";
+            }
+
+            for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
+
+            if (precision > 0 && len > precision)
+            {
+                len = precision;
+            }
+
+            if (!(flags & LEFT))
+            {
+                while (len < field_width--)
+                {
+                    if (str < end) *str = ' ';
+                    ++ str;
+                }
+            }
+
+            for (i = 0; i < len; ++i)
+            {
+                if (str < end) *str = *s;
+                ++ str;
+                ++ s;
+            }
+
+            while (len < field_width--)
+            {
+                if (str < end) *str = ' ';
+                ++ str;
+            }
+            continue;
+
+        case 'p':
+            if (field_width == -1)
+            {
+                field_width = sizeof(void *) << 1;
+                field_width += 2; /* `0x` prefix */
+                flags |= SPECIAL;
+                flags |= ZEROPAD;
+            }
+            str = print_number(str, end, (unsigned long)va_arg(args, void *),
+                               16, qualifier, field_width, precision, flags);
+            continue;
+
+        case '%':
+            if (str < end)
+            {
+                *str = '%';
+            }
+            ++ str;
+            continue;
+
+        /* integer number formats - set up the flags and "break" */
+        case 'b':
+            base = 2;
+            break;
+        case 'o':
+            base = 8;
+            break;
+
+        case 'X':
+            flags |= LARGE;
+        case 'x':
+            base = 16;
+            break;
+
+        case 'd':
+        case 'i':
+            flags |= SIGN;
+        case 'u':
+            break;
+
+        case 'e':
+        case 'E':
+        case 'G':
+        case 'g':
+        case 'f':
+        case 'F':
+            va_arg(args, double);
+        default:
+            if (str < end)
+            {
+                *str = '%';
+            }
+            ++ str;
+
+            if (*fmt)
+            {
+                if (str < end)
+                {
+                    *str = *fmt;
+                }
+                ++ str;
+            }
+            else
+            {
+                -- fmt;
+            }
+            continue;
+        }
+
+        if (qualifier == 'L')
+        {
+            num = va_arg(args, unsigned long long);
+        }
+        else if (qualifier == 'l')
+        {
+            num = va_arg(args, unsigned long);
+        }
+        else if (qualifier == 'H')
+        {
+            num = (rt_int8_t)va_arg(args, rt_int32_t);
+            if (flags & SIGN)
+            {
+                num = (rt_int8_t)num;
+            }
+        }
+        else if (qualifier == 'h')
+        {
+            num = (rt_uint16_t)va_arg(args, rt_int32_t);
+            if (flags & SIGN)
+            {
+                num = (rt_int16_t)num;
+            }
+        }
+        else if (qualifier == 'z')
+        {
+            num = va_arg(args, rt_size_t);
+            if (flags & SIGN)
+            {
+                num = (rt_ssize_t)num;
+            }
+        }
+        else
+        {
+            num = (rt_uint32_t)va_arg(args, unsigned long);
+        }
+        str = print_number(str, end, num, base, qualifier, field_width, precision, flags);
+    }
+
+    if (size > 0)
+    {
+        if (str < end)
+        {
+            *str = '\0';
+        }
+        else
+        {
+            end[-1] = '\0';
+        }
+    }
+
+    /* the trailing null byte doesn't count towards the total
+    * ++str;
+    */
+    return str - buf;
+}
+RTM_EXPORT(rt_vsnprintf);
+#if (defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */) && (__GNUC__ >= 7)
+#pragma GCC diagnostic pop /* ignored "-Wimplicit-fallthrough" */
+#endif /* (defined(__GNUC__) && !defined(__ARMCC_VERSION)) && (__GNUC__ >= 7 */
+
+#endif /* !RT_KLIBC_USING_VSNPRINTF_STANDARD */