From: Marcus Griep <marcus@griep.us>
To: Git Mailing List <git@vger.kernel.org>
Cc: Junio C Hamano <gitster@pobox.com>, Marcus Griep <marcus@griep.us>
Subject: [PATCH v3 2/3] strbuf: Add method to convert byte-size to human readable form
Date: Fri, 15 Aug 2008 00:20:21 -0400 [thread overview]
Message-ID: <1218774022-30198-2-git-send-email-marcus@griep.us> (raw)
In-Reply-To: <1218774022-30198-1-git-send-email-marcus@griep.us>
Takes a strbuf as its first argument and appends the human-readable
form of 'value', the second argument, to that buffer.
e.g. strbuf_append_human_readable(sb, 1012, 0, 0, HR_SPACE)
produces "0.9 Ki".
Documented in strbuf.h; units can be directly appended to the strbuf
to produce "0.9 KiB/s", "0.9 KiB", or other unit.
Supports SI magnitude prefixes as well:
e.g. strbuf_append_human_readable(sb, 2024, 0, 0, HR_USE_SI)
produces "2.0k", to which a unit can be appended, such as " objects"
to produce "2.0k objects".
Also, add in test cases to ensure it produces the expected output
and to demonstrate what different arguments do.
Signed-off-by: Marcus Griep <marcus@griep.us>
---
.gitignore | 1 +
Makefile | 2 +-
strbuf.c | 92 +++++++++++++++++++++++++++++++++++++++++++++
strbuf.h | 30 +++++++++++++++
t/t0031-human-readable.sh | 9 ++++
test-human-read.c | 68 +++++++++++++++++++++++++++++++++
6 files changed, 201 insertions(+), 1 deletions(-)
create mode 100755 t/t0031-human-readable.sh
create mode 100644 test-human-read.c
diff --git a/.gitignore b/.gitignore
index a213e8e..d65fa75 100644
--- a/.gitignore
+++ b/.gitignore
@@ -146,6 +146,7 @@ test-date
test-delta
test-dump-cache-tree
test-genrandom
+test-human-read
test-match-trees
test-parse-options
test-path-utils
diff --git a/Makefile b/Makefile
index 90c5a13..f17ab76 100644
--- a/Makefile
+++ b/Makefile
@@ -1297,7 +1297,7 @@ endif
### Testing rules
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X test-human-read$X
all:: $(TEST_PROGRAMS)
diff --git a/strbuf.c b/strbuf.c
index 720737d..d9888fb 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -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;
+}
diff --git a/strbuf.h b/strbuf.h
index eba7ba4..305ef55 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -125,4 +125,34 @@ extern int strbuf_getline(struct strbuf *, FILE *, int);
extern void stripspace(struct strbuf *buf, int skip_comments);
extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
+/*
+ * strbuf_append_human_readable
+ *
+ * 'val': value to be metrically-reduced to a human-readable number
+ * 'maxlen': maximum number of characters to be taken up by the reduced 'val'
+ * not including the sign or magnitude (i.e. 'Ki') characters;
+ * when 'maxlen' is 0 length is controled by 'scale'
+ * 'scale': when 'val' is greater than 'scale', 'val' is reduced by the
+ * period (default 1024, see 'flags') until it is less than 'scale';
+ * when 'scale' is 0, 'val' is reduced until it fits in 'maxlen';
+ * when 'scale' and 'maxlen' are both zero, 'scale' defaults to 1000
+ * 'flags': HR_USE_SI: uses a period of 1000 and uses SI magnitude prefixes
+ * HR_SPACE: inserts a space between the reduced 'val' and the units
+ * HR_PAD_UNIT: instead of an empty string for singles, pads with
+ * spaces to the length of the magnitude prefixes
+ *
+ * Returns 0 if 'val' is successfully reduced and fits in 'maxlen', otherwise
+ * returns -n where n is the number of additional characters necessary to
+ * fully fit the reduced value.
+ */
+
+#define HR_USE_SI 0x01
+#define HR_SPACE 0x02
+#define HR_PAD_UNIT 0x04
+
+extern int strbuf_append_human_readable(struct strbuf *,
+ double val,
+ int maxlen, int scale,
+ int flags);
+
#endif /* STRBUF_H */
diff --git a/t/t0031-human-readable.sh b/t/t0031-human-readable.sh
new file mode 100755
index 0000000..ce2fd77
--- /dev/null
+++ b/t/t0031-human-readable.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description="Test human-readable formatting"
+
+. ./test-lib.sh
+
+test_expect_success 'human-readable formatting tests' 'test-human-read'
+
+test_done
diff --git a/test-human-read.c b/test-human-read.c
new file mode 100644
index 0000000..855296c
--- /dev/null
+++ b/test-human-read.c
@@ -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;
+}
--
1.6.0.rc2.6.g8eda3
next prev parent reply other threads:[~2008-08-15 4:21 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-08-14 22:18 [PATCH v2 0/3] count-objects size and strbuf human-readable Marcus Griep
2008-08-14 22:18 ` [PATCH v2 1/3] count-objects: Add total pack size to verbose output Marcus Griep
2008-08-14 22:18 ` [PATCH v2 2/3] strbuf: Add method to convert byte-size to human readable form Marcus Griep
2008-08-14 22:18 ` [PATCH v2 3/3] count-objects: add human-readable size option Marcus Griep
2008-08-14 22:37 ` Petr Baudis
2008-08-14 23:52 ` Marcus Griep
2008-08-15 0:10 ` Junio C Hamano
2008-08-14 22:34 ` [PATCH v2 2/3] strbuf: Add method to convert byte-size to human readable form Petr Baudis
2008-08-14 23:04 ` Junio C Hamano
2008-08-14 23:24 ` Petr Baudis
2008-08-14 23:40 ` Junio C Hamano
2008-08-15 0:53 ` Marcus Griep
2008-08-15 0:46 ` Marcus Griep
2008-08-15 0:52 ` Shawn O. Pearce
2008-08-15 4:20 ` [PATCH v3 1/3] count-objects: Add total pack size to verbose output Marcus Griep
2008-08-15 4:20 ` Marcus Griep [this message]
2008-08-15 4:20 ` [PATCH v3 3/3] count-objects: add human-readable size option Marcus Griep
2008-08-15 15:47 ` [PATCH v3.1 1/3] count-objects: Add total pack size to verbose output Marcus Griep
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1218774022-30198-2-git-send-email-marcus@griep.us \
--to=marcus@griep.us \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).