Mercurial > code
view pack.c @ 158:44e7b975618a
Fix warning in buf
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 23 Jul 2012 14:44:34 +0200 |
parents | 3fde269076a7 |
children | 81d64d4a5473 |
line wrap: on
line source
/* * pack.c -- endian dependant binary file reader * * Copyright (c) 2011, 2012, 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 <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #include "pack.h" /* -------------------------------------------------------- * structure definitions * -------------------------------------------------------- */ 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 * -------------------------------------------------------- */ 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 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 } }; /* * 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; } /* * 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. */ static void pack_convert16(uint16_t *x) { *x = pack_swap16(*x); } static void pack_convert32(uint32_t *x) { *x = pack_swap32(*x); } static void pack_convert64(uint64_t *x) { *x = pack_swap64(*x); } static void pack_writefunc_fp(uint64_t i, size_t size, FILE *fp, void *unused) { 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; } /* * 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) { ConvertFn conv = NULL; uint64_t it = 0; int i; if (ptype != PACK_HOST_BYTEORDER) conv = pack_get_convert(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_get_convert(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. */ 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); default: break; } } else if (val->nelem > 1) val->data.array = va_arg(ap, uint8_t *); } /* * 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) { memset(val, 0, sizeof (struct value)); if (isspace(**p)) goto skip; if ((val->size = pack_getsize(**p)) == 0) goto skip; val->tok = **p; /* * 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); } /* -------------------------------------------------------- * 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; } /* * 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); 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) { FILE *fp; int status; if ((fp = fopen(path, "w+b")) == NULL) return -1; status = pack_vfwrite(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 pack_fwrite(int ptype, FILE *fp, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vfwrite(ptype, fp, fmt, ap); va_end(ap); return status; } int pack_vfwrite(int ptype, FILE *fp, const char *fmt, va_list ap) { const char *p; struct value v; 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); } return 0; } int pack_sread(int ptype, uint8_t *buf, size_t len, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = pack_vsread(ptype, buf, len, fmt, ap); va_end(ap); return rv; } 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; p = fmt; while (*p != '\0' && offset <= len) { pack_get_properties(&v, (char **)&p, ap); if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0) continue; memcpy(ptr, &buf[offset], v.nelem * v.size); pack_convert_read(&v, ptr, ptype); offset += (v.nelem * v.size); } return 0; } /* * 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, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vread(ptype, path, fmt, ap); va_end(ap); return status; } int pack_vread(int ptype, const char *path, const char *fmt, va_list ap) { FILE *fp; int status; if ((fp = fopen(path, "rb")) == NULL) return -1; status = pack_vfread(ptype, fp, fmt, ap); fclose(fp); return status; } int pack_fread(int ptype, FILE *fp, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vfread(ptype, fp, fmt, ap); va_end(ap); return status; } int pack_vfread(int ptype, FILE *fp, const char *fmt, va_list ap) { const char *p; uint8_t *ptr; struct value v; p = fmt; while (*p != '\0') { pack_get_properties(&v, (char **)&p, ap); if ((ptr = va_arg(ap, uint8_t *)) == NULL || v.nelem == 0) continue; fread(ptr, v.size, v.nelem, fp); pack_convert_read(&v, ptr, ptype); } return 0; }