[Concept,07/35] lib: Add format_size() to format sizes as strings

Message ID 20251210000737.180797-8-sjg@u-boot.org
State New
Headers
Series malloc: Add heap debugging commands and mcheck caller tracking |

Commit Message

Simon Glass Dec. 10, 2025, 12:06 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Refactor print_size() to use a new format_size() helper that formats
a size into a buffer. This allows callers to get the formatted string
without printing it directly.

The format_size() function is only exported in U-Boot proper (controlled
by CONFIG_LIB_FORMAT_SIZE) to avoid code-size impact in SPL/TPL where it
remains static.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 include/display_options.h | 14 ++++++++++++++
 lib/Kconfig               |  7 +++++++
 lib/display_options.c     | 34 ++++++++++++++++++++++++----------
 test/lib/test_print.c     | 14 ++++++++++++++
 4 files changed, 59 insertions(+), 10 deletions(-)
  

Patch

diff --git a/include/display_options.h b/include/display_options.h
index f3bdff172eb..5941d6bd37a 100644
--- a/include/display_options.h
+++ b/include/display_options.h
@@ -11,6 +11,20 @@ 
 
 #include <linux/types.h>
 
+#if CONFIG_IS_ENABLED(LIB_FORMAT_SIZE)
+/**
+ * format_size() - Format a size with a suffix
+ *
+ * Format sizes as "xxx KiB", "xxx.y KiB", "xxx MiB", "xxx.y MiB",
+ * xxx GiB, xxx.y GiB, etc as needed
+ *
+ * @buf:	Buffer to write to (must be at least 12 bytes)
+ * @size:	Size to format
+ * Return: @buf
+ */
+char *format_size(char *buf, uint64_t size);
+#endif
+
 /**
  * print_size() - Print a size with a suffix
  *
diff --git a/lib/Kconfig b/lib/Kconfig
index d7f791f77f3..79a75f98446 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -36,6 +36,13 @@  config BACKTRACE
 	  stack. This is currently only available on sandbox. The backtrace
 	  command can be used to print the backtrace.
 
+config LIB_FORMAT_SIZE
+	bool
+	default y
+	help
+	  Enables the format_size() helper which formats a size as a
+	  human-readable string.
+
 config BCH
 	bool "Enable Software based BCH ECC"
 	help
diff --git a/lib/display_options.c b/lib/display_options.c
index 7f9dfcc43ee..3aee08c7b00 100644
--- a/lib/display_options.c
+++ b/lib/display_options.c
@@ -98,7 +98,11 @@  void print_freq(uint64_t freq, const char *s)
 	printf(" %cHz%s", c, s);
 }
 
-void print_size(uint64_t size, const char *s)
+#if CONFIG_IS_ENABLED(LIB_FORMAT_SIZE)
+char *format_size(char *buf, uint64_t size)
+#else
+static char *format_size(char *buf, uint64_t size)
+#endif
 {
 	unsigned long m = 0, n;
 	uint64_t f;
@@ -107,6 +111,7 @@  void print_size(uint64_t size, const char *s)
 	char c = 0;
 	unsigned int i;
 
+	/* Find the best unit to display */
 	for (i = 0; i < ARRAY_SIZE(names); i++, d -= 10) {
 		if (size >> d) {
 			c = names[i];
@@ -116,12 +121,12 @@  void print_size(uint64_t size, const char *s)
 
 	if (!c) {
 		/*
-		 * SPL tiny-printf is not capable for printing uint64_t.
-		 * We have just checked that the size is small enought to fit
+		 * SPL tiny-printf is not capable of printing uint64_t.
+		 * We have just checked that the size is small enough to fit
 		 * unsigned int safely.
 		 */
-		printf("%u Bytes%s", (unsigned int)size, s);
-		return;
+		sprintf(buf, "%u Bytes", (unsigned int)size);
+		return buf;
 	}
 
 	n = size >> d;
@@ -143,11 +148,20 @@  void print_size(uint64_t size, const char *s)
 		}
 	}
 
-	printf ("%lu", n);
-	if (m) {
-		printf (".%ld", m);
-	}
-	printf (" %ciB%s", c, s);
+	if (m)
+		sprintf(buf, "%lu.%ld %ciB", n, m, c);
+	else
+		sprintf(buf, "%lu %ciB", n, c);
+
+	return buf;
+}
+
+void print_size(uint64_t size, const char *s)
+{
+	char buf[12];
+
+	format_size(buf, size);
+	printf("%s%s", buf, s);
 }
 
 #define MAX_LINE_LENGTH_BYTES		64
diff --git a/test/lib/test_print.c b/test/lib/test_print.c
index cd7f3f85769..f9b04a3e3cb 100644
--- a/test/lib/test_print.c
+++ b/test/lib/test_print.c
@@ -68,3 +68,17 @@  static int lib_test_print_size(struct unit_test_state *uts)
 	return 0;
 }
 LIB_TEST(lib_test_print_size, UTF_CONSOLE);
+
+static int lib_test_format_size(struct unit_test_state *uts)
+{
+	char buf[12];
+
+	ut_asserteq_str("321 Bytes", format_size(buf, 321));
+	ut_asserteq_str("4.2 KiB", format_size(buf, 4321));
+	ut_asserteq_str("53 KiB", format_size(buf, 54321));
+	ut_asserteq_str("1 GiB", format_size(buf, 1073741824));
+	ut_asserteq_str("49.4 TiB", format_size(buf, 54321987654321));
+
+	return 0;
+}
+LIB_TEST(lib_test_format_size, 0);