login
Header Space

 
 

[PATCH 1/2 v2] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()

Score:
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: Reece Dunn <msclrhd@...>
Cc: Mike Hommey <mh@...>, Junio C Hamano <gitster@...>, Johannes Sixt <johannes.sixt@...>, <git@...>
Date: Thursday, March 6, 2008 - 12:29 pm

The most common use of addf() was to init a strbuf and addf() right away.
Since it is so common, it makes sense to have a function strbuf_initf() to
wrap both calls into one.

To do that, we implement a (really minimal) vaddf() lookalike to
vsprintf().

At the moment, it only handles %u, %i, %d, %l, %o, %x and %X with size
indicators '<number>', ' <number>' and '0<number>', as well as %c and %s,
the latter with size indicators '.*' in addition to the same size
indicators as for numbers.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---

	On Thu, 6 Mar 2008, Johannes Schindelin wrote:

	> Of course, having a simple implementation for addf() _not_ using 
	> vsnprintf() could help, too (and make the process more efficient, 
	> probably).

	Having thought about this a bit, I came up with this 
	implementation (replacing my earlier PATCH 1/2).  It is by far not 
	complete, but that is exactly the idea: we do not _need_ a fully-fledged 
	printf() implementation.

	Besides, it was fun.

 .gitignore       |    1 +
 Makefile         |    4 +-
 strbuf.c         |  134 +++++++++++++++++++++++++++++++++++++++++++++++------
 strbuf.h         |    3 +
 t/t0000-basic.sh |    8 +++
 test-strbuf.c    |   17 +++++++
 6 files changed, 150 insertions(+), 17 deletions(-)
 create mode 100644 test-strbuf.c

diff --git a/.gitignore b/.gitignore
index 219759f..c0ecd41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -152,6 +152,7 @@ test-genrandom
 test-match-trees
 test-parse-options
 test-sha1
+test-strbuf
 common-cmds.h
 *.tar.gz
 *.dsc
diff --git a/Makefile b/Makefile
index cd0b294..58f6238 100644
--- a/Makefile
+++ b/Makefile
@@ -1052,7 +1052,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-absolute-path$X test-parse-options$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-strbuf$X
 
 all:: $(TEST_PROGRAMS)
 
@@ -1071,6 +1071,8 @@ test-delta$X: diff-delta.o patch-delta.o
 
 test-parse-options$X: parse-options.o
 
+test-strbuf$X: strbuf.o
+
 .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
 
 test-%$X: test-%.o $(GITLIBS)
diff --git a/strbuf.c b/strbuf.c
index 5afa8f3..5ad89a8 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -123,28 +123,130 @@ void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
 	strbuf_setlen(sb, sb->len + len);
 }
 
+static int number_length(unsigned long number, long base)
+{
+	int length = 1;
+	while (number >= base) {
+		number /= base;
+		length++;
+	}
+	return length;
+}
+
+/*
+ * Only supports %u, %i, %d, %l, %o, %x and %X with size indicators
+ *   '<number>', '0<number>', and ' <number>',
+ * as well as %c,
+ * and %s with size indicators '<number>', ' <number>' and '.*'.
+ */
+void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)
+{
+	while (*fmt) {
+		char fill = '\0';
+		int size = -1, max_size = -1;
+		char *p = (char *)fmt;
+
+		if (*p != '%' || *(++p) == '%') {
+			strbuf_addch(sb, *p++);
+			fmt = p;
+			continue;
+		}
+		if (*p == ' ' || *p == '0')
+			fill = *p++;
+		if (isdigit(*p))
+			size = (int)strtol(p, &p, 10);
+		else if (!prefixcmp(p, ".*")) {
+			max_size = va_arg(ap, int);
+			p += 2;
+		}
+		switch (*p) {
+		case 's': {
+			const char *s = va_arg(ap, const char *);
+			if (fill) {
+				int len = size - strlen(s);
+				while (len-- > 0)
+					strbuf_addch(sb, fill);
+			}
+			while (*s && max_size--)
+				strbuf_addch(sb, *s++);
+			break;
+		}
+		case 'c':
+			strbuf_addch(sb, va_arg(ap, int));
+			break;
+		case 'u':
+		case 'i':
+		case 'l':
+		case 'd':
+		case 'o':
+		case 'x':
+		case 'X': {
+			int base = *p == 'x' || *p == 'X' ? 16 :
+				*p == 'o' ? 8 : 10;
+			int negative = 0, len;
+			unsigned long number, power;
+
+			if (*p == 'u')
+				number = va_arg(ap, unsigned int);
+			else {
+				long signed_number;
+				if (*p == 'l')
+					signed_number = va_arg(ap, long);
+				else
+					signed_number = va_arg(ap, int);
+				if (signed_number < 0) {
+					negative = 1;
+					number = -signed_number;
+				} else
+					number = signed_number;
+			}
+
+			/* pad */
+			len = number_length(number, base);
+			while (size-- > len + negative)
+				strbuf_addch(sb, fill ? fill : ' ');
+			if (negative)
+				strbuf_addch(sb, '-');
+
+			/* output number */
+			power = 1;
+			while (len-- > 1)
+				power *= base;
+			while (power) {
+				int digit = number / power;
+				strbuf_addch(sb, digit < 10 ? '0' + digit
+					: *p + 'A' - 'X' + digit - 10);
+				number -= digit * power;
+				power /= base;
+			}
+
+			break;
+		}
+		default:
+			/* unknown / invalid format: copy verbatim */
+			strbuf_insert(sb, sb->len, fmt, p - fmt + 1);
+		}
+		fmt = p + (*p != '\0');
+	}
+}
+
 void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 {
-	int len;
 	va_list ap;
 
-	if (!strbuf_avail(sb))
-		strbuf_grow(sb, 64);
 	va_start(ap, fmt);
-	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
+	strbuf_vaddf(sb, fmt, ap);
+	va_end(ap);
+}
+
+void strbuf_initf(struct strbuf *sb, const char *fmt, ...)
+{
+	va_list ap;
+
+	strbuf_init(sb, strlen(fmt) + 64);
+	va_start(ap, fmt);
+	strbuf_vaddf(sb, fmt, ap);
 	va_end(ap);
-	if (len < 0)
-		die("your vsnprintf is broken");
-	if (len > strbuf_avail(sb)) {
-		strbuf_grow(sb, len);
-		va_start(ap, fmt);
-		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
-		va_end(ap);
-		if (len > strbuf_avail(sb)) {
-			die("this should not happen, your snprintf is broken");
-		}
-	}
-	strbuf_setlen(sb, sb->len + len);
 }
 
 void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
diff --git a/strbuf.h b/strbuf.h
index faec229..d7c7aaf 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -106,8 +106,11 @@ extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
 typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
 extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
 
+extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list args);
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
+__attribute__((format(printf,2,3)))
+extern void strbuf_initf(struct strbuf *sb, const char *fmt, ...);
 
 extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 /* XXX: if read fails, any partial read is undone */
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 27b54cb..c1d4639 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -311,6 +311,14 @@ test_expect_success 'absolute path works as expected' '
 	test "$sym" = "$(test-absolute-path $dir2/syml)"
 '
 
+test_expect_success 'strbuf_initf() works as expected' '
+
+	eval $(test-strbuf) &&
+	test ! -z "$result" &&
+	test "$result" = "$expect"
+
+'
+
 test_expect_success 'very long name in the index handled sanely' '
 
 	a=a && # 1
diff --git a/test-strbuf.c b/test-strbuf.c
new file mode 100644
index 0000000..479fa08
--- /dev/null
+++ b/test-strbuf.c
@@ -0,0 +1,17 @@
+#include "cache.h"
+#include "strbuf.h"
+
+int main(int argc, char **argv)
+{
+	struct strbuf buf;
+#define TEST_FORMAT \
+	"'%%%.*s,%x,%05X,%u,%i,% 4d,%3d,%c,%3d'", \
+	5, "Hello, World!", 27, 27, -1, -1, 1, 5, ':', 1234
+
+	strbuf_initf(&buf, TEST_FORMAT);
+	printf("result=%s\n", buf.buf);
+	printf("expect=" TEST_FORMAT);
+	strbuf_release(&buf);
+
+	return 0;
+}
-- 
1.5.4.3.571.g9aec3.dirty

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[PATCH 00/40] MinGW port, Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 00/40] MinGW port, Johannes Schindelin, (Wed Feb 27, 7:58 pm)
Re: [PATCH 00/40] MinGW port, Marius Storm-Olsen, (Wed Feb 27, 6:01 pm)
Re: [PATCH 00/40] MinGW port, Martin Langhoff, (Wed Feb 27, 7:34 pm)
Re: [PATCH 00/40] MinGW port, Nguyen Thai Ngoc Duy, (Wed Feb 27, 11:38 pm)
Re: [PATCH 00/40] MinGW port, Johannes Sixt, (Sun Mar 2, 5:20 pm)
Re: [PATCH 00/40] MinGW port, Johannes Schindelin, (Sun Mar 2, 6:07 pm)
Re: [PATCH 00/40] MinGW port, Johannes Sixt, (Mon Mar 3, 2:34 pm)
Re: [PATCH 08/40] Windows: always chmod(, 0666) before unlin..., Johannes Schindelin, (Thu Feb 28, 8:09 am)
Re: [PATCH 04/40] Windows: Use the Windows style PATH separa..., Johannes Schindelin, (Thu Feb 28, 9:09 pm)
Re: [PATCH 04/40] Windows: Use the Windows style PATH separa..., Johannes Schindelin, (Fri Feb 29, 8:19 am)
Re: [PATCH 04/40] Windows: Use the Windows style PATH separa..., Johannes Schindelin, (Fri Feb 29, 8:59 am)
Re: [PATCH 40/40] compat/pread.c: Add foward decl to fix war..., Johannes Schindelin, (Thu Feb 28, 11:51 am)
[PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Thu Feb 28, 8:05 am)
Re: [PATCH 03/40] Add target architecture MinGW., Paolo Bonzini, (Thu Feb 28, 8:57 am)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Thu Feb 28, 10:56 am)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Wed Mar 5, 5:21 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Tue Mar 11, 5:30 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Tue Mar 11, 7:28 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Wed Mar 12, 6:59 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Wed Mar 12, 7:06 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Wed Mar 5, 6:18 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Thu Mar 6, 4:38 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Junio C Hamano, (Wed Mar 5, 6:22 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Wed Mar 5, 6:28 pm)
[PATCH 2/2] format-patch: add --reviewed-by=&lt;ident&gt;, Johannes Schindelin, (Wed Mar 5, 9:15 pm)
Re: [PATCH 2/2] format-patch: add --reviewed-by=&lt;ident&gt;, Johannes Schindelin, (Thu Mar 6, 6:40 am)
[PATCH 1/2] Add strbuf_initf(), Johannes Schindelin, (Wed Mar 5, 9:14 pm)
Re: [PATCH 1/2] Add strbuf_initf(), Mike Hommey, (Thu Mar 6, 2:33 am)
Re: [PATCH 1/2] Add strbuf_initf(), Johannes Schindelin, (Thu Mar 6, 6:53 am)
Re: [PATCH 1/2] Add strbuf_initf(), Jeff King, (Thu Mar 6, 8:09 am)
Re: [PATCH 1/2] Add strbuf_initf(), Reece Dunn, (Thu Mar 6, 5:03 am)
Re: [PATCH 1/2] Add strbuf_initf(), Johannes Schindelin, (Thu Mar 6, 6:55 am)
Re: [PATCH 1/2] Add strbuf_initf(), Reece Dunn, (Thu Mar 6, 7:53 am)
Re: [PATCH 1/2] Add strbuf_initf(), Johannes Schindelin, (Thu Mar 6, 8:52 am)
[PATCH 1/2 v2] Add strbuf_vaddf(), use it in strbuf_addf(), ..., Johannes Schindelin, (Thu Mar 6, 12:29 pm)
Re: [PATCH 1/2 v2] Add strbuf_vaddf(), use it in strbuf_addf..., Johannes Schindelin, (Thu Mar 6, 12:59 pm)
Re: [PATCH 1/2] Add strbuf_initf(), Kristian , (Thu Mar 6, 2:18 pm)
Re: [PATCH 1/2] Add strbuf_initf(), Johannes Schindelin, (Thu Mar 6, 2:26 pm)
Re: [PATCH 1/2] Add strbuf_initf(), Mike Hommey, (Thu Mar 6, 3:10 pm)
Re: [PATCH 1/2] Add strbuf_initf(), Kristian , (Thu Mar 6, 2:35 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Junio C Hamano, (Wed Mar 5, 6:51 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Wed Mar 5, 8:11 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Thu Feb 28, 4:40 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Thu Feb 28, 9:07 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Sixt, (Fri Feb 29, 5:03 pm)
Re: [PATCH 03/40] Add target architecture MinGW., Johannes Schindelin, (Fri Feb 29, 5:54 pm)
[PATCH 37/40] Windows: Make 'git help -a' work., Johannes Sixt, (Wed Feb 27, 2:55 pm)
Re: [PATCH 37/40] Windows: Make 'git help -a' work., Paolo Bonzini, (Thu Feb 28, 5:52 am)
Re: [PATCH 36/40] Avoid the "dup dance" in wt_status_print_v..., Johannes Schindelin, (Thu Feb 28, 11:48 am)
[PATCH 34/40] Windows: Make the pager work., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 33/40] When installing, be prepared that template..., Johannes Schindelin, (Thu Feb 28, 11:45 am)
Re: [PATCH 33/40] When installing, be prepared that template..., Johannes Schindelin, (Thu Feb 28, 9:21 pm)
[PATCH 30/40] Turn builtin_exec_path into a function., Johannes Sixt, (Wed Feb 27, 2:54 pm)
[PATCH 02/40] Compile some programs only conditionally., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 02/40] Compile some programs only conditionally., Johannes Schindelin, (Thu Feb 28, 7:57 am)
Re: [PATCH 02/40] Compile some programs only conditionally., Johannes Schindelin, (Thu Feb 28, 8:47 pm)
Re: [PATCH 02/40] Compile some programs only conditionally., Johannes Schindelin, (Fri Feb 29, 5:53 pm)
[PATCH 27/40] Windows: Implement a custom spawnve()., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 27/40] Windows: Implement a custom spawnve()., Johannes Schindelin, (Thu Feb 28, 11:36 am)
Re: [PATCH 27/40] Windows: Implement a custom spawnve()., Johannes Sixt, (Thu Feb 28, 5:04 pm)
Re: [PATCH 27/40] Windows: Implement a custom spawnve()., Johannes Schindelin, (Thu Feb 28, 9:18 pm)
Re: [PATCH 23/40] Windows: Local clone must use the drive le..., Johannes Schindelin, (Thu Feb 28, 11:31 am)
Re: [PATCH 22/40] Windows: Implement asynchronous functions ..., Johannes Schindelin, (Thu Feb 28, 11:28 am)
Re: [PATCH 22/40] Windows: Implement asynchronous functions ..., Johannes Schindelin, (Thu Feb 28, 9:27 pm)
Re: [PATCH 22/40] Windows: Implement asynchronous functions ..., Johannes Schindelin, (Thu Feb 28, 9:54 pm)
Re: [PATCH 22/40] Windows: Implement asynchronous functions ..., Johannes Schindelin, (Fri Feb 29, 6:26 am)
Re: [PATCH 22/40] Windows: Implement asynchronous functions ..., Johannes Schindelin, (Thu Feb 28, 9:17 pm)
Re: [PATCH 21/40] Windows: Disambiguate DOS style paths from..., Johannes Schindelin, (Thu Feb 28, 11:22 am)
[PATCH 20/40] Windows: A rudimentary poll() emulation., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 20/40] Windows: A rudimentary poll() emulation., Robin Rosenberg, (Sat Mar 1, 11:48 am)
Re: [PATCH 19/40] Windows: Change the name of hook scripts t..., Johannes Schindelin, (Thu Feb 28, 11:20 am)
Re: [PATCH 19/40] Windows: Change the name of hook scripts t..., Johannes Schindelin, (Thu Feb 28, 9:11 pm)
Re: [PATCH 01/40] Add compat/regex.[ch] and compat/fnmatch.[..., Johannes Schindelin, (Wed Feb 27, 7:43 pm)
[PATCH 18/40] Windows: Implement start_command()., Johannes Sixt, (Wed Feb 27, 2:54 pm)
[PATCH 13/40] Windows: Fix PRIuMAX definition., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 13/40] Windows: Fix PRIuMAX definition., Johannes Schindelin, (Thu Feb 28, 8:21 am)
Re: [PATCH 13/40] Windows: Fix PRIuMAX definition., Johannes Sixt, (Thu Feb 28, 4:45 pm)
[PATCH 12/40] Windows: Implement gettimeofday()., Johannes Sixt, (Wed Feb 27, 2:54 pm)
[PATCH 10/40] Windows: Treat Windows style path names., Johannes Sixt, (Wed Feb 27, 2:54 pm)
Re: [PATCH 10/40] Windows: Treat Windows style path names., Johannes Schindelin, (Thu Feb 28, 8:18 am)
[PATCH 09/40] Windows: Work around misbehaved rename()., Johannes Sixt, (Wed Feb 27, 2:54 pm)
speck-geostationary