@@ -308,3 +308,95 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
return len;
}
+
+int strbuf_append_human_readable(struct strbuf *sb,
+ double val,
+ int maxlen, int scale,
+ int flags)
+{
+ const int maxscale = 7;
+
+ char *hr_prefixes[] = {
+ "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi", NULL
+ };
+ char *hr_si_prefixes[] = {
+ "", "k", "M", "G", "T", "P", "E", "Z", "Y", NULL
+ };
+ char **prefix = &hr_prefixes[0];
+ int period = 1024;
+ int sign = val < 0 ? -1 : 1;
+ int retval = 0;
+
+ val *= sign;
+
+ if (flags & HR_PAD_UNIT) {
+ hr_prefixes[0] = " ";
+ hr_si_prefixes[0] = " ";
+ }
+
+ if (flags & HR_USE_SI) {
+ period = 1000;
+ prefix = &hr_si_prefixes[0];
+ }
+
+ if (scale == 0) {
+ if (maxlen == 0) {
+ scale = 1000;
+ maxlen = 3;
+ }
+ else {
+ int space = maxlen;
+ scale = 10;
+ while (--space > 0) {
+ scale *= 10;
+ }
+ }
+ }
+ else {
+ int space = 1;
+ int check = 10;
+ int setscale = scale;
+ while (check < scale) {
+ check *= 10;
+ ++space;
+ if (maxlen - space == 0)
+ setscale = check;
+ }
+ if (!maxlen)
+ maxlen = space;
+ scale = setscale;
+ }
+
+ while (val >= scale && *prefix++)
+ val /= period;
+
+ if (val >= scale) {
+ int needed = 0;
+ while (val >= scale) {
+ val /= period;
+ --needed;
+ }
+ if (needed < retval)
+ retval = needed;
+ }
+
+ strbuf_addf(sb, "%f", sign * val);
+
+ if (maxlen) {
+ int signlen = sign == -1 ? 1 : 0;
+ maxlen -= (sb->buf[maxlen-1+signlen] == '.' ? 1 : 0);
+ if (maxlen <= 0) {
+ strbuf_setlen(sb, 0);
+ retval = maxlen - 1;
+ } else {
+ strbuf_setlen(sb, maxlen + signlen);
+ }
+ }
+
+ strbuf_addf(sb, "%s%s",
+ flags & HR_SPACE ? " " : "",
+ *prefix
+ );
+
+ return retval;
+}@@ -0,0 +1,68 @@
+#include "builtin.h"
+#include "strbuf.h"
+
+struct test {
+ double val;
+ int len;
+ int scale;
+ int flags;
+
+ char *out;
+ int retval;
+};
+
+int main(int argc, char **argv) {
+ int failures = 0, i = 0, retval;
+ struct test tests[] = {
+ { 1012, 0, 0, HR_SPACE, "0.9 Ki", 0 },
+ { 1012, 0, 0, HR_SPACE, "0.9 Ki", 0 },
+ { 1012, 0, 0, HR_SPACE | HR_USE_SI, "1.0 k", 0 },
+ { -1012, 0, 0, HR_SPACE | HR_USE_SI, "-1.0 k", 0 },
+ { 1012, 5, 0, HR_SPACE | HR_PAD_UNIT, "1012 ", 0 },
+ { 1012, 5, 0, HR_SPACE, "1012 ", 0 },
+ { 1012, 5, 0, HR_USE_SI | HR_SPACE | HR_PAD_UNIT, "1012 ", 0 },
+ { 1012, 5, 0, HR_USE_SI | HR_SPACE, "1012 ", 0 },
+ { 1012, 4, 0, 0, "1012", 0 },
+ { 1012, 3, 0, 0, "0.9Ki", 0 },
+ { 1012, 2, 0, 0, "0Ki", 0 },
+ { 1012, 1, 0, 0, "0Ki", 0 },
+ { 1012, 0, 0, 0, "0.9Ki", 0 },
+ { -1012, 3, 0, 0, "-0.9Ki", 0 },
+ { -1012, 2, 0, 0, "-0Ki", 0 },
+ { -1012, 1, 0, 0, "-0Ki", 0 },
+ { -1012, 0, 0, 0, "-0.9Ki", 0 },
+ { 2024, 4, 0, 0, "2024", 0 },
+ { 20240, 4, 0, 0, "19.7Ki", 0 },
+ { 1012, 0, 1000, 0, "0.9Ki", 0 },
+ { 506.0 * 1024 * 1024, 0, 1000000, 0, "518144Ki", 0 },
+ { 506.0 * 1024 * 1024, 0, 1000000, 0, "518144Ki", 0 },
+ { 506.0 * 1024 * 1024, 7, 1000000, 0, "518144Ki", 0 },
+ { 506.0 * 1024 * 1024, 6, 1000000, 0, "518144Ki", 0 },
+ { 506.0 * 1024 * 1024, 5, 1000000, 0, "506.0Mi", 0 },
+ { 506.0 * 1024 * 1024, 4, 1000000, 0, "506Mi", 0 },
+ { 506.0 * 1024 * 1024, 3, 1000000, 0, "506Mi", 0 },
+ { 506.0 * 1024 * 1024, 2, 1000000, 0, "0Gi", 0 },
+ { 506.0 * 1024 * 1024 * 1024, 0, 1000000, 0, "518144Mi", 0 },
+ { 506.0 * 1024 * 1024 * 1024, 0, 1000000, HR_USE_SI, "543313M", 0 },
+ { 0, 0, 0, 0, NULL, 0 }
+ };
+ struct strbuf sb;
+ struct test *current = &tests[0];
+ strbuf_init(&sb, 0);
+
+ while(current->out) {
+ printf("Test %2d - ", ++i);
+ strbuf_setlen(&sb, 0);
+ retval = strbuf_append_human_readable(&sb, current->val, current->len,
+ current->scale, current->flags);
+ if(strcmp(sb.buf, current->out) || retval != current->retval) {
+ printf("Failure: Act '%s' (%d); Exp '%s' (%d)\n",
+ sb.buf, retval, current->out, current->retval);
+ ++failures;
+ } else
+ printf("Success: '%s' (%d)\n", sb.buf, retval);
+ ++current;
+ }
+
+ return failures;
+}