[stgt] [PATCH 1/4] struct concat_buf and concat_printf() for printing to buffers

Alexander Nezhinsky alexandern at mellanox.com
Sat Nov 26 12:21:12 CET 2011


Text queries issued through tgtadm use a fixed-size buffer that is filled,
step by step, typically by a few calls to snprintf() or an equivalent.
This requires tracking the current position within the buffer and guarding
against overflows. Currently these goals are achived by few separate macros:
shprintf(), buffer_check() and _buffer_check().
This approach relies upon local/global counter variables that describe
the buffer state and in some cases a label to escape through on errors.
Two kinds of problems arise in such settings:
First, using hardcoded variable names and especially 'goto' statements in
macros is unsafe, and second, it becomes difficult to use nested function
calls if the called functions also append some strings to the buffer.

The alternative offered by this patch uses a buffer descriptor context,
'struct concat_buf', which stores the buffer pointer and relevant counters
and error code. This internal state information avoids writes when buffer is
full and in all cases of previosuly encountered errors.

Thus we can continue with the function flow as if the buffer is unlimited 
and in the end return an appropriate error code using concat_buf_ret().
Code is simplified and the tedious checks afer each snprintf are avoided. 

As the context may be passed to other functions easily and its state is
preserved upon return, nested calls are naturally enabled.

Signed-off-by: Alexander Nezhinsky <alexandern at mellanox.com>
---
 usr/util.c |   26 ++++++++++++++++++++++++++
 usr/util.h |   40 +++++++++++++++++++++++++++++-----------
 2 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/usr/util.c b/usr/util.c
index c78a999..43d2690 100644
--- a/usr/util.c
+++ b/usr/util.c
@@ -194,3 +194,29 @@ int get_blk_shift(unsigned int size)
 	return shift;
 }
 
+int concat_printf(struct concat_buf *b, const char *format, ...)
+{
+	va_list args;
+	int rest = b->size - b->used;
+	int nprinted;
+
+	if (b->err)
+		return b->err;
+	if (!rest)
+		return (b->err = -ENOSPC);
+
+	va_start(args, format);
+	nprinted = vsnprintf(b->buf + b->used, rest, format, args);
+	va_end(args);
+
+	if (nprinted < 0)
+		return (b->err = nprinted);
+
+	if (nprinted >= rest) {
+		nprinted = rest;
+		b->err = -ENOSPC;
+	}
+	b->used += nprinted;
+	return b->err;
+}
+
diff --git a/usr/util.h b/usr/util.h
index 8abdb94..18d81b8 100644
--- a/usr/util.h
+++ b/usr/util.h
@@ -8,6 +8,7 @@
 #include <signal.h>
 #include <syscall.h>
 #include <unistd.h>
+#include <stdarg.h>
 #include <limits.h>
 #include <linux/types.h>
 
@@ -88,17 +89,6 @@ static inline int between(uint32_t seq1, uint32_t seq2, uint32_t seq3)
 	return seq3 - seq2 >= seq1 - seq2;
 }
 
-#define shprintf(total, buf, rest, fmt, args...)			\
-do {									\
-	int len;							\
-	len = snprintf(buf, rest, fmt, ##args);				\
-	if (len > rest)							\
-		goto overflow;						\
-	buf += len;							\
-	total += len;							\
-	rest -= len;							\
-} while (0)
-
 extern unsigned long pagesize, pageshift;
 
 #if defined(__NR_signalfd) && defined(USE_SIGNALFD)
@@ -151,4 +141,32 @@ struct signalfd_siginfo {
 	ret;						\
 })
 
+struct concat_buf {
+	char *buf;
+	int size;
+	int used;
+	int err;
+};
+
+static inline void concat_buf_init(struct concat_buf *b,
+				   char *buf, int size)
+{
+	b->buf = buf;
+	b->size = size;
+	b->used = 0;
+	b->err = 0;
+}
+
+static inline int concat_buf_ret(struct concat_buf *b)
+{
+	return !b->err ? b->used : b->err;
+}
+
+static inline const char *concat_delim(struct concat_buf *b, const char *delim)
+{
+	return !b->used ? "" : delim;
+}
+
+extern int concat_printf(struct concat_buf *b, const char *format, ...);
+
 #endif
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the stgt mailing list