changeset 56:770737295987

Added pack.c pack.h libpack-like for opening binary file endianness safe.
author David Demelier <markand@malikania.fr>
date Tue, 08 Nov 2011 18:26:05 +0100
parents cda80ba48029
children 311eaa9d004c
files pack.c pack.h
diffstat 2 files changed, 524 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pack.c	Tue Nov 08 18:26:05 2011 +0100
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2011, David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "pack.h"
+
+/* --------------------------------------------------------
+ * structure definitions
+ * -------------------------------------------------------- */
+
+/*
+ * Conversion function pointer.
+ */
+typedef void (*convert_fn)(void *);
+
+/*
+ * Item structure store the integer into the largest data type
+ * uint64_t.
+ */
+
+struct item {
+	size_t		size;	/* 8, 16, 32 or 64 bits? */
+	uint64_t	i;	/* the data */
+	convert_fn	conv;	/* conversion function */
+	STAILQ_ENTRY(item) next;
+};
+
+/*
+ * List of item structure.
+ */
+
+STAILQ_HEAD(item_list, item);
+
+/* --------------------------------------------------------
+ * prototypes
+ * -------------------------------------------------------- */
+
+static int		pack_item_add(struct item_list *, const struct item *);
+static int		pack_parse(struct item_list *, const char *, va_list);
+static size_t		pack_getsize(char);
+static convert_fn	pack_getconvert(char);
+static void		pack_convert16(void *);
+static void		pack_convert32(void *);
+static void		pack_convert64(void *);
+static int		pack_fatal(struct item_list *);
+
+/* --------------------------------------------------------
+ * private functions
+ * -------------------------------------------------------- */
+
+#define LENGTH(x)	(sizeof (x) / sizeof (x[0]))	
+
+/*
+ * Macros that converts byte order or keep them if the host matches
+ * the byte order requested.
+ */
+
+#define CONVERT_UINT16(i) do {						\
+	if (ptype != PACK_HOST_BYTEORDER)				\
+		i = pack_swap16(i); 					\
+} while (/* CONSTCOND */ 0)
+
+#define CONVERT_UINT32(i) do {						\
+	if (ptype != PACK_HOST_BYTEORDER)				\
+		i = pack_swap32(i); 					\
+} while (/* CONSTCOND */ 0)
+
+#define CONVERT_UINT64(i) do {						\
+	if (ptype != PACK_HOST_BYTEORDER)				\
+		i = pack_swap64(i); 					\
+} while (/* CONSTCOND */ 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 */
+	convert_fn	convert;	/* conversion function */
+} sizes[] = {
+	{ 'c',		sizeof (uint8_t),	NULL		},
+	{ 's',		sizeof (uint16_t),	&pack_convert16	},
+	{ 'i',		sizeof (uint32_t),	&pack_convert32	},
+	{ 'l',		sizeof (uint64_t),	&pack_convert64	}
+};
+
+/*
+ * Try to append a new item to the list. The function create a new item
+ * object since the pack_parse() use a stack'ed item and not an
+ * allocated object. Returns 0 or -1 on failure.
+ */
+
+static int
+pack_item_add(struct item_list *list, const struct item *item)
+{
+	struct item *res;
+
+	if ((res = malloc(sizeof (struct item))) == NULL)
+		return -1;
+
+	res->size	= item->size;
+	res->i		= item->i;
+	res->conv	= item->conv;
+
+	STAILQ_INSERT_TAIL(list, res, next);
+
+	return 0;
+}
+
+/*
+ * 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)
+
+static int
+pack_parse(struct item_list *list, const char *fmt, va_list ap)
+{
+	const char *p;
+	char tok;
+	struct item item;
+
+	STAILQ_INIT(list);
+
+	for (p = fmt; *p != '\0'; ++p) {
+		int i, nelem = 1;
+
+		if (isspace(*p))
+			continue;
+
+		tok = *p;
+		item.size = pack_getsize(tok);
+
+		/* Bad character */
+		if (item.size == 0)
+			continue;
+
+		PACK_GETNELEM(nelem, p);
+		if (nelem == 0)
+			continue;
+
+		for (i = 0; i < nelem; ++i) {
+			PACK_GETARG(item.i, ap, tok);
+			item.conv	= pack_getconvert(tok);
+
+			if (pack_item_add(list, &item) < 0)
+				return pack_fatal(list);
+		}
+	}
+
+	return 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;
+}
+
+/*
+ * Return the conversion function.
+ */
+
+static convert_fn
+pack_getconvert(char tok)
+{
+	struct integer *s;
+	unsigned int i;
+
+	for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i)
+		if (s->tok == tok)
+			return s->convert;
+
+	return NULL;
+}
+
+/*
+ * Conversion functions. They reverse the bytes without any
+ * check.
+ */
+
+static void
+pack_convert16(void *obj)
+{
+	uint16_t *x = obj;
+
+	*x = pack_swap16(*x);
+}
+
+static void
+pack_convert32(void *obj)
+{
+	uint32_t *x = obj;
+
+	*x = pack_swap32(*x);
+}
+
+static void
+pack_convert64(void *obj)
+{
+	uint64_t *x = obj;
+
+	*x = pack_swap64(*x);
+}
+
+static int
+pack_fatal(struct item_list *list)
+{
+	struct item *item, *tmp;
+
+	STAILQ_FOREACH_SAFE(item, list, next, tmp)
+		free(item);
+
+	return -1;
+}
+
+/* --------------------------------------------------------
+ * public functions
+ * -------------------------------------------------------- */
+
+/*
+ * 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(int ptype, const char *path, const char *fmt, ...)
+{
+	va_list ap;
+	int status;
+
+	va_start(ap, fmt);
+	status = vpack(ptype, path, fmt, ap);
+	va_end(ap);
+	
+	return status;
+}
+
+int
+vpack(int ptype, const char *path, const char *fmt, va_list ap)
+{
+	FILE *fp;
+	int status;
+
+	if ((fp = fopen(path, "w+b")) == NULL)
+		return -1;
+
+	status = vfpack(ptype, fp, fmt, ap);
+	fclose(fp);
+
+	return status;
+}
+
+/*
+ * Write to a file that is already open. The function does not call
+ * fclose() so you need to do it yourself later.
+ */
+
+int
+fpack(int ptype, FILE *fp, const char *fmt, ...)
+{
+	va_list ap;
+	int status;
+
+	va_start(ap, fmt);
+	status = vfpack(ptype, fp, fmt, ap);
+	va_end(ap);
+
+	return status;
+}
+
+int
+vfpack(int ptype, FILE *fp, const char *fmt, va_list ap)
+{
+	struct item_list list;
+	int status;
+
+	if ((status = pack_parse(&list, fmt, ap)) == 0) {
+		struct item *item, *tmp;
+
+		STAILQ_FOREACH_SAFE(item, &list, next, tmp) {
+			/* 8 bits does not need to be converted */
+			if (ptype != PACK_HOST_BYTEORDER && item->conv != NULL)
+				item->conv(&item->i);
+
+			fwrite(&item->i, item->size, 1, fp);
+
+			free(item);
+		}
+	}
+
+	return status;
+}
+
+/*
+ * 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
+unpack(int ptype, const char *path, const char *fmt, ...)
+{
+	va_list ap;
+	int status;
+
+	va_start(ap, fmt);
+	status = vunpack(ptype, path, fmt, ap);
+	va_end(ap);
+
+	return status;
+}
+
+int
+vunpack(int ptype, const char *path, const char *fmt, va_list ap)
+{
+	FILE *fp;
+	int status;
+
+	if ((fp = fopen(path, "rb")) == NULL)
+		return -1;
+
+	status = vfunpack(ptype, fp, fmt, ap);
+	fclose(fp);
+
+	return status;
+
+}
+
+int
+funpack(int ptype, FILE *fp, const char *fmt, ...)
+{
+	va_list ap;
+	int status;
+
+	va_start(ap, fmt);
+	status = vfunpack(ptype, fp, fmt, ap);
+	va_end(ap);
+
+	return status;
+}
+
+int
+vfunpack(int ptype, FILE *fp, const char *fmt, va_list ap)
+{
+	const char *p;
+	void *ptr;
+	size_t tocopy;
+	convert_fn convert;
+
+	for (p = fmt; *p != '\0'; ++p) {
+		char tok;
+		int nelem, i;
+
+		if (isspace(*p))
+			continue;
+
+		tok = *p;
+		tocopy = pack_getsize(tok);
+
+		/* Bad character */
+		if (tocopy == 0)
+			continue;
+
+		/* Determine how many element to read and copy */
+		PACK_GETNELEM(nelem, p);
+		if (nelem == 0)
+			continue;
+
+		if ((ptr = va_arg(ap, void *)) == NULL)
+			continue;
+
+		for (i = 0; i < nelem; ++i) {
+			fread((char *) ptr + (tocopy * i), tocopy, 1, fp);
+
+			/* Convert if needed */
+			convert = pack_getconvert(tok);
+			if (ptype != PACK_HOST_BYTEORDER && convert != NULL)
+				convert((char *) ptr + (tocopy * i));
+		}
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pack.h	Tue Nov 08 18:26:05 2011 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2011, David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PACK_H_
+#define _PACK_H_
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Endian detection based on SDL_endian.h
+ */
+
+#undef PACK_HOST_BYTEORDER
+
+#define PACK_LIL_ENDIAN	1234
+#define PACK_BIG_ENDIAN	4321
+
+#if defined(__hppa__) ||						\
+    defined(__m68k__) || defined(mc68000) || defined(_M_M68K) ||	\
+    (defined(__MIPS__) && defined(__MISPEB__)) ||			\
+    defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) ||	\
+    defined(__sparc__)
+#  define PACK_HOST_BYTEORDER	PACK_BIG_ENDIAN
+#else
+#  define PACK_HOST_BYTEORDER	PACK_LIL_ENDIAN
+#endif
+
+#define pack_swap16(x)							\
+	((((x) >> 8) & 0x00FFL) | (((x) << 8) & 0xFF00L))
+
+#define pack_swap32(x) ((((x) >> 24) & 0x000000FFL)			\
+	| (((x) >> 8)  & 0x0000FF00L)					\
+	| (((x) << 8)  & 0x00FF0000L)					\
+	| (((x) << 24) & 0xFF000000L))
+
+#define pack_swap64(x) (((uint64_t)					\
+	((uint32_t) pack_swap32((uint32_t) (((x) << 32) >> 32))) << 32)	\
+	    | (uint32_t) pack_swap32((uint32_t) ((x) >> 32)))
+
+int	pack(int, const char *, const char *, ...);
+int	vpack(int, const char *, const char *, va_list);
+int	vfpack(int, FILE *, const char *, va_list);
+int	fpack(int, FILE *, const char *, ...);
+
+int	unpack(int, const char *, const char *, ...);
+int	vunpack(int, const char *, const char *, va_list);
+int	funpack(int, FILE *, const char *, ...);
+int	vfunpack(int, FILE *, const char *, va_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PACK_H_ */