Mercurial > code
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_ */