# HG changeset patch # User David Demelier # Date 1346700093 -7200 # Node ID 81d64d4a54732df2d0a6dc88bbcdcc71d5e21b97 # Parent bcf15f0d243fceb9b62cf4bda49bf87314e535f2 Complete rewrite of pack.c: o more concise, o for buffer, available in autoallocation and static, diff -r bcf15f0d243f -r 81d64d4a5473 pack.c --- 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 #include -#include -#include #include #include #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); } diff -r bcf15f0d243f -r 81d64d4a5473 pack.h --- a/pack.h Fri Aug 03 17:50:46 2012 +0200 +++ b/pack.h Mon Sep 03 21:21:33 2012 +0200 @@ -20,11 +20,9 @@ #define _PACK_H_ #include -#include -#ifdef __cplusplus -extern "C" { -#endif +#define PACK_LE 1234 +#define PACK_BE 4321 /* * Endian detection based on SDL_endian.h @@ -32,9 +30,6 @@ #undef PACK_HOST_BYTEORDER -#define PACK_LE 1234 -#define PACK_BE 4321 - #if defined(__hppa__) || \ defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ (defined(__MIPS__) && defined(__MISPEB__)) || \ @@ -53,29 +48,24 @@ | (((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))) - -size_t pack_fmtlen(const char *, ...); -size_t pack_vfmtlen(const char *, va_list); +#define pack_swap64(x) (((long) \ + ((int) pack_swap32((int) (((x) << 32) >> 32))) << 32) \ + | (int) pack_swap32((int) ((x) >> 32))) -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); +long pack_write(short, const char *, const char *, ...); +long pack_vwrite(short, const char *, const char *, va_list); +long pack_fwrite(short, FILE *, const char *, ...); +long pack_vfwrite(short, FILE *, const char *, va_list); +long pack_swrite(short, char *, size_t, const char *, ...); +long pack_vswrite(short, char *, size_t, const char *, va_list); +long pack_aswrite(short, char **, const char *, ...); +long pack_vaswrite(short, char **, 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 *, ...); -int pack_vfread(int, FILE *, const char *, va_list); - -#ifdef __cplusplus -} -#endif +long pack_read(short, const char *, const char *, ...); +long pack_vread(short, const char *, const char *, va_list); +long pack_fread(short, FILE *, const char *, ...); +long pack_vfread(short, FILE *, const char *, va_list); +long pack_sread(short, const char *, size_t, const char *, ...); +long pack_vsread(short, const char *, size_t, const char *, va_list); #endif /* _PACK_H_ */