changeset 153:a6a7cd08be72

pack.c: o added two new functions pack_fmtlen, pack_vfmtlen, compute the number of bytes needed for format. o added support for buffer, pack_swrite, pack_vswrite, pack_sread, pack_vsread. Buffer will be automatically allocated for _write functions.
author David Demelier <markand@malikania.fr>
date Sun, 15 Jul 2012 20:40:54 +0200
parents 54976019a5a8
children c7ad101ed4a1
files pack.c pack.h
diffstat 2 files changed, 272 insertions(+), 120 deletions(-) [+]
line wrap: on
line diff
--- a/pack.c	Wed Jun 27 11:23:54 2012 +0200
+++ b/pack.c	Sun Jul 15 20:40:54 2012 +0200
@@ -30,6 +30,18 @@
  * -------------------------------------------------------- */
 
 typedef void (*ConvertFn)(void *);
+typedef void (*WriteFn)(uint64_t, size_t, void *, void *);
+
+struct value {
+	char		tok;		/* token found */
+	int		nelem;		/* number element for this value */
+	size_t		size;		/* size of this element */
+
+	union {
+		uint8_t	*array;		/* if (nelem > 1) */
+		uint64_t value;		/* if (nelem == 1) */
+	} data;
+};
 
 /* --------------------------------------------------------
  * prototypes
@@ -41,8 +53,11 @@
 static void		pack_convert16(uint16_t *);
 static void		pack_convert32(uint32_t *);
 static void		pack_convert64(uint64_t *);
-static void		pack_write_one(int, FILE *, uint64_t, size_t);
-static void		pack_write_multiple(int, FILE *, uint8_t *, int, size_t);
+
+static void		pack_writefunc_fp(uint64_t, size_t, FILE *, void *);
+static void		pack_writefunc_buffer(uint64_t, size_t, uint8_t *, size_t *);
+static void		pack_convert_write(const struct value *, int,
+			    WriteFn, void *, void *);
 
 /* --------------------------------------------------------
  * private functions
@@ -68,45 +83,6 @@
 };
 
 /*
- * Parse the format, return 0 on success or -1 on failure.
- */
-
-#define PACK_GETARG(i, ap, tok)						\
-do {									\
-	switch ((tok)) {						\
-	case 'c':							\
-	case 's':							\
-	case 'i':							\
-		(i) = va_arg((ap), unsigned);				\
-		break;							\
-	case 'l':							\
-		(i) = va_arg((ap), uint64_t);				\
-	default:							\
-		break;							\
-	}								\
-} while (/* CONSTCOND */ 0)
-
-/*
- * Little helper to get number of element when the user wants a array of
- * objects. Sets the nelem to 0 on array failure or 1 on normal count.
- */
-
-#define PACK_GETNELEM(nelem, p)						\
-do {									\
-	if (p[1] == '[') {						\
-		if (p[2] != ']')					\
-			nelem = (int)strtol(p + 2, NULL, 10);		\
-		else if (p[2] == ']')					\
-			nelem = va_arg(ap, int);			\
-									\
-		/* Go to the end of fmt and find ']' */			\
-		while (*p != ']' && *p != '\0')				\
-			++p;						\
-	} else								\
-		nelem = 1;						\
-} while (/* CONSTCOND */ 0)
-
-/*
  * Get the appropriate size associated with the `tok' character. If
  * the token is not found the result is 0.
  */
@@ -182,39 +158,140 @@
 }
 
 static void
-pack_write_one(int ptype, FILE *fp, uint64_t it, size_t size)
+pack_writefunc_fp(uint64_t i, size_t size, FILE *fp, void *unused)
 {
-	uint64_t cv = it;
+	fwrite(&i, size, 1, fp);
 
-	if (ptype != PACK_HOST_BYTEORDER) {
-		ConvertFn conv = pack_getconvert_by_size(size);
-
-		if (conv != NULL)
-			conv(&cv);
-	}
-
-	fwrite(&cv, size, 1, fp);
+	(void)unused;
 }
 
 static void
-pack_write_multiple(int ptype, FILE *fp, uint8_t *arr, int length, size_t size)
+pack_writefunc_buffer(uint64_t i, size_t size, uint8_t *buffer, size_t *offset)
+{
+	memcpy(&buffer[*offset], &i, size);
+
+	(*offset) += size;
+}
+
+/*
+ * Convert data and write it using write func function.
+ */
+
+static void
+pack_convert_write(const struct value *val, int ptype,
+		   WriteFn writefunc, void *data1, void *data2)
 {
-	uint64_t cv;
+	ConvertFn conv = NULL;
+	uint64_t it = 0;
 	int i;
+
+	if (ptype != PACK_HOST_BYTEORDER)
+		conv = pack_getconvert_by_size(val->size);
+
+	for (i = 0; i < val->nelem; ++i) {
+		it = 0;
+
+		if (val->nelem == 1)
+			memcpy(&it, &val->data.value, val->size);
+		else
+			memcpy(&it, &val->data.array[i * val->size], val->size);
+
+		/* Converts and bring back to array */
+		if (conv != NULL)
+			conv(&it);
+
+		writefunc(it, val->size, data1, data2);
+	}
+}
+
+/*
+ * Convert value for reading, dst pointer will be converted if
+ * necessary.
+ */
+
+static void
+pack_convert_read(const struct value *val, uint8_t *dst, int ptype)
+{
 	ConvertFn conv = NULL;
+	int i;
+	uint64_t it = 0;
 
 	if (ptype != PACK_HOST_BYTEORDER)
-		conv = pack_getconvert_by_size(size);
+		conv = pack_getconvert_by_size(val->size);
+
+	for (i = 0; i < val->nelem; ++i) {
+		it = 0;
+		memcpy(&it, &dst[i * val->size], val->size);
+
+		if (conv != NULL) {
+			conv(&it);
+			memset(&dst[i * val->size], 0, val->size);
+		}
+
+		memcpy(&dst[i * val->size], &it, val->size);
+	}
+}
+
+/*
+ * Store the argument into the corrent .data member.
+ */
 
-	for (i = 0; i < length; ++i) {
-		cv = 0;
-		memcpy(&cv, &arr[i * size], size);
+static void
+pack_get_arg(struct value *val, va_list ap, int ptype)
+{
+	if (val->nelem == 1) {
+		switch (val->tok) {
+		case 'c':
+		case 's':
+		case 'i':
+			val->data.value = va_arg(ap, unsigned);
+			break;
+		case 'l':
+			val->data.value = va_arg(ap, uint64_t);
+		default:
+			break;
+		}
+	} else if (val->nelem > 1)
+		val->data.array = va_arg(ap, uint8_t *);
+}
 
-		if (conv != NULL)
-			conv(&cv);
+/*
+ * Get the properties, the token size, the number of element to read.
+ */
+
+static void
+pack_get_properties(struct value *val, char **p, va_list ap, int ptype)
+{
+	memset(val, 0, sizeof (struct value));
+
+	if (isspace(**p))
+		goto skip;
+	if ((val->size = pack_getsize(**p)) == 0)
+		goto skip;
+
+	val->tok = **p;
 
-		fwrite(&cv, size, 1, fp);
+	/*
+	 * Now there is valid char, c, s, i or l, we must determine
+	 * if the next character is [ (array) or anything else.
+	 */
+	if ((*p)[1] == '[') {
+		if ((*p)[2] != ']')
+			val->nelem = (int)strtol((*p) + 2, NULL, 10);
+		else if ((*p)[2] == ']')
+			val->nelem = va_arg(ap, int);
+
+		/* Go to the end of fmt and find ']' */
+		while (**p != ']' && **p != '\0')
+			++ (*p);
+	} else {
+		val->nelem = 1;
+		++ (*p);
 	}
+
+	return;
+skip:
+	++ (*p);
 }
 
 /* --------------------------------------------------------
@@ -222,6 +299,87 @@
  * -------------------------------------------------------- */
 
 /*
+ * Returns the number of bytes needed to write the specified format.
+ */
+
+size_t
+pack_fmtlen(int ptype, const char *fmt, ...)
+{
+	va_list ap;
+	size_t count;
+
+	va_start(ap, fmt);
+	count = pack_vfmtlen(ptype, fmt, ap);
+	va_end(ap);
+
+	return count;
+}
+
+size_t
+pack_vfmtlen(int ptype, const char *fmt, va_list ap)
+{
+	const char *p;
+	struct value v;
+	size_t count = 0;
+
+	p = fmt;
+	while (*p != '\0') {
+		pack_get_properties(&v, (char **)&p, ap, ptype);
+		count += (v.size * v.nelem);
+	}
+
+	return count;
+}
+
+/*
+ * Functions that write anything to the buffer, the buffer is automatically
+ * allocated for you. Argument buflen will be set to the buffer length and
+ * must not be NULL.
+ */
+
+int
+pack_swrite(int ptype, uint8_t **buf, size_t *buflen, const char *fmt, ...)
+{
+	va_list ap;
+	int rv;
+
+	va_start(ap, fmt);
+	rv = pack_vswrite(ptype, buf, buflen, fmt, ap);
+	va_end(ap);
+
+	return rv;
+}
+
+int
+pack_vswrite(int ptype, uint8_t **buf, size_t *len, const char *fmt, va_list ap)
+{
+	const char *p;
+	uint8_t *b = NULL;
+	size_t offset = 0;
+	struct value v;
+
+	p = fmt;
+	while (*p != '\0') {
+		pack_get_properties(&v, (char **)&p, ap, ptype);
+		pack_get_arg(&v, ap, ptype);
+
+		if ((b = realloc(b, offset + (v.nelem * v.size))) == NULL) {
+			*buf	= NULL;
+			*len	= 0;
+			return -1;
+		}
+
+		pack_convert_write(&v, ptype,
+		    (WriteFn)pack_writefunc_buffer, b, &offset);
+	}
+
+	*buf	= b;
+	*len	= offset;
+
+	return 0;
+}
+
+/*
  * Function that writes everything to the file `path'. These functions
  * does not append to the file, so if you want successive call to append
  * variables use fpack instead.
@@ -277,41 +435,52 @@
 int
 pack_vfwrite(int ptype, FILE *fp, const char *fmt, va_list ap)
 {
-	int nelem;
-	size_t size;
-	char tok;
 	const char *p;
+	struct value v;
+
+	p = fmt;
+	while (*p != '\0') {
+		pack_get_properties(&v, (char **)&p, ap, ptype);
+		pack_get_arg(&v, ap, ptype);
+		pack_convert_write(&v, ptype,
+		    (WriteFn)pack_writefunc_fp, fp, NULL);
+	}
+
+	return 0;
+}
+
+int
+pack_sread(int ptype, uint8_t *buf, size_t len, const char *fmt, ...)
+{
+	va_list ap;
+	int rv;
 
-	for (p = fmt; *p != '\0'; ++p) {
-		if (isspace(*p))
-			continue;
+	va_start(ap, fmt);
+	rv = pack_vsread(ptype, buf, len, fmt, ap);
+	va_end(ap);
+
+	return rv;
+}
 
-		tok = *p;
-		size = pack_getsize(tok);
+int
+pack_vsread(int ptype, uint8_t *buf, size_t len, const char *fmt, va_list ap)
+{
+	const char *p;
+	uint8_t *ptr;
+	size_t offset = 0;
+	struct value v;
 
-		/* Bad character */
-		if (size == 0)
+	p = fmt;
+	while (*p != '\0' && offset <= len) {
+		pack_get_properties(&v, (char **)&p, ap, ptype);
+
+		if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0)
 			continue;
 
-		PACK_GETNELEM(nelem, p);
-		if (nelem == 0)
-			continue;
+		memcpy(ptr, &buf[offset], v.nelem * v.size);
+		pack_convert_read(&v, ptr, ptype);
 
-		/*
-		 * If i is 1, then we only have one integer, if it's more
-		 * than one, user may have given an array of something else.
-		 */
-		if (nelem == 1) {
-			uint64_t item;
-
-			PACK_GETARG(item, ap, tok);
-			pack_write_one(ptype, fp, item, size);
-		} else {
-			uint8_t *arr;
-
-			arr = va_arg(ap, uint8_t *);
-			pack_write_multiple(ptype, fp, arr, nelem, size);
-		}
+		offset += (v.nelem * v.size);
 	}
 
 	return 0;
@@ -349,7 +518,6 @@
 	fclose(fp);
 
 	return status;
-
 }
 
 int
@@ -370,41 +538,17 @@
 {
 	const char *p;
 	uint8_t *ptr;
-	size_t tocopy;
-	ConvertFn convert;
-
-	for (p = fmt; *p != '\0'; ++p) {
-		char tok;
-		int nelem, i;
+	struct value v;
 
-		if (isspace(*p))
-			continue;
+	p = fmt;
+	while (*p != '\0') {
+		pack_get_properties(&v, (char **)&p, ap, ptype);
 
-		tok = *p;
-		tocopy = pack_getsize(tok);
-
-		/* Bad character */
-		if (tocopy == 0)
+		if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0)
 			continue;
 
-		/* Determine how many element to read and copy */
-		PACK_GETNELEM(nelem, p);
-		if (nelem == 0)
-			continue;
-
-		if ((ptr = va_arg(ap, uint8_t *)) == NULL)
-			continue;
-
-		for (i = 0; i < nelem; ++i) {
-			memset(&ptr[tocopy * i], 0, tocopy);
-			if (fread(&ptr[tocopy * i], tocopy, 1, fp) <= 0)
-				return -1;
-
-			/* Convert if needed */
-			convert = pack_getconvert_by_tok(tok);
-			if (ptype != PACK_HOST_BYTEORDER && convert != NULL)
-				convert((char *)ptr + (tocopy * i));
-		}
+		fread(ptr, v.size, v.nelem, fp);
+		pack_convert_read(&v, ptr, ptype);
 	}
 
 	return 0;
--- a/pack.h	Wed Jun 27 11:23:54 2012 +0200
+++ b/pack.h	Sun Jul 15 20:40:54 2012 +0200
@@ -20,6 +20,7 @@
 #define _PACK_H_
 
 #include <stdarg.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -56,11 +57,18 @@
 	((uint32_t) pack_swap32((uint32_t) (((x) << 32) >> 32))) << 32)	\
 	    | (uint32_t) pack_swap32((uint32_t) ((x) >> 32)))
 
+size_t	pack_fmtlen(int, const char *, ...);
+size_t	pack_vfmtlen(int, const char *, va_list);
+
+int	pack_swrite(int, uint8_t **, size_t *, const char *, ...);
+int	pack_vswrite(int, uint8_t **, size_t *, const char *, va_list);
 int	pack_write(int, const char *, const char *, ...);
 int	pack_vwrite(int, const char *, const char *, va_list);
 int	pack_fwrite(int, FILE *, const char *, ...);
 int	pack_vfwrite(int, FILE *, const char *, va_list);
 
+int	pack_sread(int, uint8_t *, size_t, const char *, ...);
+int	pack_vsread(int, uint8_t *, size_t, const char *, va_list);
 int	pack_read(int, const char *, const char *, ...);
 int	pack_vread(int, const char *, const char *, va_list);
 int	pack_fread(int, FILE *, const char *, ...);