diff pack.c @ 161:81d64d4a5473

Complete rewrite of pack.c: o more concise, o for buffer, available in autoallocation and static,
author David Demelier <markand@malikania.fr>
date Mon, 03 Sep 2012 21:21:33 +0200
parents 3fde269076a7
children 7f214f26a4c0
line wrap: on
line diff
--- a/pack.c	Fri Aug 03 17:50:46 2012 +0200
+++ b/pack.c	Mon Sep 03 21:21:33 2012 +0200
@@ -18,520 +18,450 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdint.h>
-#include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
 
 #include "pack.h"
 
-/* --------------------------------------------------------
- * structure definitions
- * -------------------------------------------------------- */
+/* Compilation tunables */
+#if !defined(PACK_CHKSIZE)
+#  define PACK_CHKSIZE	(256)		/* default chunk size */
+#endif
 
-typedef void (*ConvertFn)(void *);
-typedef void (*WriteFn)(uint64_t, size_t, void *, void *);
+/* Type */
+enum pack_type {
+	TYPE_FP,
+	TYPE_FIXEDBUF,
+	TYPE_AUTOBUF
+};
+
+struct pack_sc {
+	enum pack_type	type;		/* which kind of work */
+	short		endian;		/* endian */
+
+	/* if TYPE_FP */
+	FILE *		fp;		/* file to write */
 
-struct value {
-	char		tok;		/* token found */
-	int		nelem;		/* number element for this value */
-	size_t		size;		/* size of this element */
+	/* else */
+	char *		buf;		/* bytes converted */
+	size_t		size;		/* current length */
+	size_t		allocated;	/* allocated size */
+
+	/* for reading */
+	unsigned int	position;	/* position where to read */
+};
 
-	union {
-		uint8_t	*array;		/* if (nelem > 1) */
-		uint64_t value;		/* if (nelem == 1) */
-	} data;
+struct pack_value {
+	/* for writing */
+	int		nelem;		/* number of element */
+	int		size;		/* size of elements */
+	long		value;		/* direct value */
+	char *		array;		/* array of values */
+
+	/* for reading */
+	char *		dst;		/* destination */
 };
 
-/* --------------------------------------------------------
- * prototypes
- * -------------------------------------------------------- */
-
-static size_t		pack_getsize(char);
-static ConvertFn	pack_get_convert(size_t);
-static void		pack_convert16(uint16_t *);
-static void		pack_convert32(uint32_t *);
-static void		pack_convert64(uint64_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
- * -------------------------------------------------------- */
+#define TOKNARG(tok, nelem) do {					\
+	if (p[1] == '[') {						\
+		char *end;						\
+									\
+		/* Can statically write the number, or pass it */	\
+		if (p[2] != ']') {					\
+			nelem = (int)strtol(&p[2], &end, 10);		\
+			p = end;					\
+		} else {						\
+			nelem = va_arg(ap, int);			\
+			while (!isspace(*p) && *p != ']' && *p != '\0')	\
+				++p;					\
+		}							\
+	} else								\
+		nelem = 1;						\
+} while (/* CONSTCOND */ 0)
 
-#define LENGTH(x)	(sizeof (x) / sizeof (x[0]))	
-
-/*
- * Associative structure, the `tok' member is used for the format
- * and define the integer size. The convert function reverse the
- * bytes if it is needed.
- */
-
-static struct integer {
-	char		tok;		/* format char */
-	size_t		tocopy;		/* size */
-	ConvertFn	convert;	/* conversion function */
-} sizes[] = {
-	{ 'c',		sizeof (uint8_t),	NULL				},
-	{ 's',		sizeof (uint16_t),	(ConvertFn)pack_convert16	},
-	{ 'i',		sizeof (uint32_t),	(ConvertFn)pack_convert32	},
-	{ 'l',		sizeof (uint64_t),	(ConvertFn)pack_convert64	}
-};
+#define TOKSIZE(tok, size) do {						\
+	switch (tok) {							\
+	case 'c':							\
+		size = sizeof (char);					\
+		break;							\
+	case 's':							\
+		size = sizeof (short);					\
+		break;							\
+	case 'i':							\
+		size = sizeof (int);					\
+		break;							\
+	case 'l':							\
+		size = sizeof (long);					\
+		break;							\
+	default:							\
+		size = 0;						\
+		break;							\
+	}								\
+} while (/* CONSTCOND */ 0)
 
-/*
- * Get the appropriate size associated with the `tok' character. If
- * the token is not found the result is 0.
- */
-
-static size_t
-pack_getsize(char tok)
-{
-	struct integer *s;
-	unsigned int i;
-
-	for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i)
-		if (s->tok == tok)
-			return s->tocopy;
-
-	return 0;
-}
+#define TOKGETARG(serv, tok, val, ap) do {				\
+	if ((val)->nelem == 1 && serv == writeto) {			\
+		long item;						\
+		switch (tok) {						\
+		case 'c': case 's': case 'i':				\
+			item = va_arg(ap, int);				\
+			break;						\
+		case 'l':						\
+			item = va_arg(ap, long);			\
+			break;						\
+		default:						\
+			item = 0;					\
+			break;						\
+		}							\
+									\
+		memset(&(val)->value, 0, sizeof (long));		\
+		memcpy(&(val)->value, &item, (val)->size);		\
+		(val)->array = (char *)&(val)->value;			\
+	} else { /* read is always a pointer */				\
+		(val)->array = va_arg(ap, void *);			\
+		(val)->dst = (val)->array;				\
+	}								\
+} while (/* CONSTCOND */ 0)
 
-/*
- * Same but by size.
- */
-
-static ConvertFn
-pack_get_convert(size_t size)
-{
-	struct integer *s;
-	unsigned int i;
-
-	for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i)
-		if (s->tocopy == size)
-			return s->convert;
-
-	return NULL;
-}
-
-/*
- * Conversion functions. They reverse the bytes without any
- * check.
- */
+#define GETCONV(ed, size, conv) do {					\
+	if (ed != PACK_HOST_BYTEORDER) {				\
+		switch ((size)) {					\
+		case (sizeof (short)):					\
+			conv = conv16; break;				\
+		case (sizeof (int)):					\
+			conv = conv32; break;				\
+		case (sizeof (long)):					\
+			conv = conv64; break;				\
+		default:						\
+			conv = NULL;					\
+		}							\
+	} else								\
+		conv = NULL;						\
+} while (/* CONSTCOND */ 0)
 
 static void
-pack_convert16(uint16_t *x)
+conv16(void *item)
 {
-	*x = pack_swap16(*x);
+	*(short *)item = pack_swap16(*(short *)item);
 }
 
 static void
-pack_convert32(uint32_t *x)
+conv32(void *item)
 {
-	*x = pack_swap32(*x);
-}
-
-static void
-pack_convert64(uint64_t *x)
-{
-	*x = pack_swap64(*x);
+	*(int *)item = pack_swap32(*(int *)item);
 }
 
 static void
-pack_writefunc_fp(uint64_t i, size_t size, FILE *fp, void *unused)
+conv64(void *item)
 {
-	fwrite(&i, size, 1, fp);
-
-	(void)unused;
-}
-
-static void
-pack_writefunc_buffer(uint64_t i, size_t size, uint8_t *buffer, size_t *offset)
-{
-	memcpy(&buffer[*offset], &i, size);
-
-	(*offset) += size;
+	*(long *)item = pack_swap64(*(long *)item);
 }
 
 /*
- * Convert data and write it using write func function.
+ * Function that write content into the buffer or file depending
+ * on the calling function. Returns the number of bytes written
+ * or -1 on failure.
  */
-
-static void
-pack_convert_write(const struct value *val, int ptype,
-		   WriteFn writefunc, void *data1, void *data2)
+static long
+writeto(struct pack_sc *sc, struct pack_value *val)
 {
-	ConvertFn conv = NULL;
-	uint64_t it = 0;
+	void (*conv)(void *);
 	int i;
+	long item, nbytes = 0;
 
-	if (ptype != PACK_HOST_BYTEORDER)
-		conv = pack_get_convert(val->size);
+	/* Get converter */
+	GETCONV(sc->endian, val->size, conv);
 
 	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);
-	}
-}
+		item = 0;
+		memcpy(&item, &val->array[i * val->size], val->size);
 
-/*
- * 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_get_convert(val->size);
+		if (conv != NULL)
+			conv(&item);
 
-	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);
-	}
-}
+		switch (sc->type) {
+		case TYPE_FP:
+			fwrite(&item, val->size, 1, sc->fp);
+			break;
+		case TYPE_AUTOBUF:
+			if (sc->size + val->size >= sc->allocated) {
+				sc->allocated += PACK_CHKSIZE;
+				sc->buf = realloc(sc->buf, sc->allocated);
 
-/*
- * Store the argument into the corrent .data member.
- */
-
-static void
-pack_get_arg(struct value *val, va_list ap)
-{
-	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);
+				if (sc->buf == NULL)
+					return -1;
+			}
+			/* FALLTHROUGH */
+		case TYPE_FIXEDBUF:
+			if (sc->size + val->size <= sc->allocated) {
+				memcpy(&sc->buf[sc->size], &item, val->size);
+				sc->size += val->size;
+			} else
+				continue;
 		default:
 			break;
 		}
-	} else if (val->nelem > 1)
-		val->data.array = va_arg(ap, uint8_t *);
+
+		nbytes += val->size;
+	}
+
+	return nbytes;
 }
 
 /*
- * Get the properties, the token size, the number of element to read.
+ * Function that read the file or buffer and fill array.
  */
-
-static void
-pack_get_properties(struct value *val, char **p, va_list ap)
+static long
+readfrom(struct pack_sc *sc, struct pack_value *val)
 {
-	memset(val, 0, sizeof (struct value));
+	void (*conv)(void *);
+	int i, oktoread;
+	long item, nbytes = 0;
 
-	if (isspace(**p))
-		goto skip;
-	if ((val->size = pack_getsize(**p)) == 0)
-		goto skip;
+	/* Get converter */
+	GETCONV(sc->endian, val->size, conv);
 
-	val->tok = **p;
+	for (i = 0; i < val->nelem; ++i) {
+		item = oktoread = 0;
 
-	/*
-	 * 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);
+		switch (sc->type) {
+		case TYPE_FP:
+			fread(&item, val->size, 1, sc->fp);
+			oktoread = 1;
+			break;
+		case TYPE_FIXEDBUF:
+			if (sc->position + val->size <= sc->allocated) {
+				memcpy(&item, &sc->buf[sc->position], val->size);
+				sc->position += val->size;
+				oktoread = 1;
+			}
+			break;
+		default:
+			break;
+		}
 
-		/* Go to the end of fmt and find ']' */
-		while (**p != ']' && **p != '\0')
-			++ (*p);
-	} else {
-		val->nelem = 1;
-		++ (*p);
+		if (conv != NULL)
+			conv(&item);
+
+		if (oktoread) {
+			memcpy(&val->dst[i * val->size], &item, val->size);
+			nbytes += val->size;
+		}
 	}
 
-	return;
-skip:
-	++ (*p);
-}
-
-/* --------------------------------------------------------
- * public functions
- * -------------------------------------------------------- */
-
-/*
- * Returns the number of bytes needed to write the specified format.
- */
-
-size_t
-pack_fmtlen(const char *fmt, ...)
-{
-	va_list ap;
-	size_t count;
-
-	va_start(ap, fmt);
-	count = pack_vfmtlen(fmt, ap);
-	va_end(ap);
-
-	return count;
-}
-
-size_t
-pack_vfmtlen(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);
-		count += (v.size * v.nelem);
-	}
-
-	return count;
+	return nbytes;
 }
 
 /*
- * 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.
+ * Function that analyses the format, write or read the values depending
+ * on the calling functions.
  */
+static long
+packit(struct pack_sc *sc, long (*func)(struct pack_sc *, struct pack_value *),
+       const char *fmt, va_list ap)
+{
+	struct pack_value val;
+	const char *p;
+	int tok;
+	long nbytes = 0, rv;
 
-int
-pack_swrite(int ptype, uint8_t **buf, size_t *buflen, const char *fmt, ...)
+	for (p = fmt; *p != '\0'; ++p) {
+		tok = *p;
+
+		if (isspace(*p))
+			continue;
+
+		TOKNARG(tok, val.nelem);
+		TOKSIZE(tok, val.size);
+
+		if (val.size == 0 || val.nelem == 0)
+			continue;
+
+		TOKGETARG(func, tok, &val, ap);
+		if ((rv = func(sc, &val)) == -1)
+			return -1;
+
+		nbytes += rv;
+	}
+
+	return nbytes;
+}
+
+long
+pack_write(short ed, const char *path, const char *fmt, ...)
 {
 	va_list ap;
-	int rv;
+	long rv;
 
 	va_start(ap, fmt);
-	rv = pack_vswrite(ptype, buf, buflen, fmt, ap);
+	rv = pack_vwrite(ed, path, 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);
-		pack_get_arg(&v, ap);
-
-		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.
- * Returns 0 on success or -1 on failure.
- */
-
-int
-pack_write(int ptype, const char *path, const char *fmt, ...)
-{
-	va_list ap;
-	int status;
-
-	va_start(ap, fmt);
-	status = pack_vwrite(ptype, path, fmt, ap);
-	va_end(ap);
-	
-	return status;
-}
-
-int
-pack_vwrite(int ptype, const char *path, const char *fmt, va_list ap)
+long
+pack_vwrite(short ed, const char *path, const char *fmt, va_list ap)
 {
 	FILE *fp;
-	int status;
+	long rv;
 
-	if ((fp = fopen(path, "w+b")) == NULL)
+	if ((fp = fopen(path, "wb")) == NULL)
 		return -1;
 
-	status = pack_vfwrite(ptype, fp, fmt, ap);
+	rv = pack_vfwrite(ed, fp, fmt, ap);
 	fclose(fp);
 
-	return status;
+	return rv;
 }
 
-/*
- * Write to a file that is already open. The function does not call
- * fclose() so you need to do it yourself later.
- */
-
-int
-pack_fwrite(int ptype, FILE *fp, const char *fmt, ...)
+long
+pack_fwrite(short ed, FILE *fp, const char *fmt, ...)
 {
 	va_list ap;
-	int status;
+	long rv;
 
 	va_start(ap, fmt);
-	status = pack_vfwrite(ptype, fp, fmt, ap);
+	rv = pack_vfwrite(ed, fp, fmt, ap);
 	va_end(ap);
 
-	return status;
+	return rv;
+}
+
+long
+pack_vfwrite(short ed, FILE *fp, const char *fmt, va_list ap)
+{
+	struct pack_sc sc = {
+		TYPE_FP, ed, fp, NULL, 0, 0, 0
+	};
+
+	return packit(&sc, writeto, fmt, ap);
 }
 
-int
-pack_vfwrite(int ptype, FILE *fp, const char *fmt, va_list ap)
+long
+pack_swrite(short ed, char *buf, size_t max, const char *fmt, ...)
 {
-	const char *p;
-	struct value v;
+	va_list ap;	
+	long rv;
 
-	p = fmt;
-	while (*p != '\0') {
-		pack_get_properties(&v, (char **)&p, ap);
-		pack_get_arg(&v, ap);
-		pack_convert_write(&v, ptype,
-		    (WriteFn)pack_writefunc_fp, fp, NULL);
-	}
+	va_start(ap, fmt);
+	rv = pack_vswrite(ed, buf, max, fmt, ap);
+	va_end(ap);
 
-	return 0;
+	return rv;
 }
 
-int
-pack_sread(int ptype, uint8_t *buf, size_t len, const char *fmt, ...)
+long
+pack_vswrite(short ed, char *buf, size_t max, const char *fmt, va_list ap)
 {
-	va_list ap;
-	int rv;
+	struct pack_sc sc = {
+		TYPE_FIXEDBUF, ed, NULL, buf, 0, max, 0
+	};
+
+	return packit(&sc, writeto, fmt, ap);
+}
+
+long
+pack_aswrite(short ed, char **bufp, const char *fmt, ...)
+{
+	va_list ap;	
+	long rv;
 
 	va_start(ap, fmt);
-	rv = pack_vsread(ptype, buf, len, fmt, ap);
+	rv = pack_vaswrite(ed, bufp, fmt, ap);
 	va_end(ap);
 
 	return rv;
 }
 
-int
-pack_vsread(int ptype, uint8_t *buf, size_t len, const char *fmt, va_list ap)
+long
+pack_vaswrite(short ed, char **bufp, const char *fmt, va_list ap)
 {
-	const char *p;
-	uint8_t *ptr;
-	size_t offset = 0;
-	struct value v;
+	struct pack_sc sc = {
+		TYPE_AUTOBUF, ed, NULL, NULL, 0, PACK_CHKSIZE, 0
+	};
 
-	p = fmt;
-	while (*p != '\0' && offset <= len) {
-		pack_get_properties(&v, (char **)&p, ap);
+	int rv;
 
-		if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0)
-			continue;
+	if ((sc.buf = malloc(PACK_CHKSIZE)) == NULL)
+		return -1;
 
-		memcpy(ptr, &buf[offset], v.nelem * v.size);
-		pack_convert_read(&v, ptr, ptype);
+	rv = packit(&sc, writeto, fmt, ap);
+	*bufp = sc.buf;
 
-		offset += (v.nelem * v.size);
-	}
-
-	return 0;
+	return rv;
 }
 
-/*
- * Function that read the binary file and restore values to the same format
- * as pack functions. Arguments must be pointer of enough space to store
- * the values.
- */
-
-int
-pack_read(int ptype, const char *path, const char *fmt, ...)
+long
+pack_read(short ed, const char *path, const char *fmt, ...)
 {
 	va_list ap;
-	int status;
+	long rv;
 
 	va_start(ap, fmt);
-	status = pack_vread(ptype, path, fmt, ap);
+	rv = pack_vread(ed, path, fmt, ap);
 	va_end(ap);
 
-	return status;
+	return rv;
 }
 
-int
-pack_vread(int ptype, const char *path, const char *fmt, va_list ap)
+long
+pack_vread(short ed, const char *path, const char *fmt, va_list ap)
 {
 	FILE *fp;
-	int status;
+	long rv;
 
 	if ((fp = fopen(path, "rb")) == NULL)
 		return -1;
 
-	status = pack_vfread(ptype, fp, fmt, ap);
+	rv = pack_vfread(ed, fp, fmt, ap);
 	fclose(fp);
 
-	return status;
+	return rv;
 }
 
-int
-pack_fread(int ptype, FILE *fp, const char *fmt, ...)
+long
+pack_fread(short ed, FILE *fp, const char *fmt, ...)
 {
 	va_list ap;
-	int status;
+	long rv;
 
 	va_start(ap, fmt);
-	status = pack_vfread(ptype, fp, fmt, ap);
+	rv = pack_vfread(ed, fp, fmt, ap);
 	va_end(ap);
 
-	return status;
+	return rv;
 }
 
-int
-pack_vfread(int ptype, FILE *fp, const char *fmt, va_list ap)
+long
+pack_vfread(short ed, FILE *fp, const char *fmt, va_list ap)
 {
-	const char *p;
-	uint8_t *ptr;
-	struct value v;
+	struct pack_sc sc = {
+		TYPE_FP, ed, fp, NULL, 0, 0, 0
+	};
+
+	return packit(&sc, readfrom, fmt, ap);
+}
+
+long
+pack_sread(short ed, const char *buf, size_t max, const char *fmt, ...)
+{
+	va_list ap;	
+	long rv;
 
-	p = fmt;
-	while (*p != '\0') {
-		pack_get_properties(&v, (char **)&p, ap);
+	va_start(ap, fmt);
+	rv = pack_vsread(ed, buf, max, fmt, ap);
+	va_end(ap);
 
-		if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0)
-			continue;
+	return rv;
+}
 
-		fread(ptr, v.size, v.nelem, fp);
-		pack_convert_read(&v, ptr, ptype);
-	}
+long
+pack_vsread(short ed, const char *buf, size_t max, const char *fmt, va_list ap)
+{
+	struct pack_sc sc = {
+		TYPE_FIXEDBUF, ed, NULL, (char *)buf, 0, max, 0
+	};
 
-	return 0;
+	return packit(&sc, readfrom, fmt, ap);
 }