Skip to content

Instantly share code, notes, and snippets.

@neurodrone
Created September 25, 2013 03:10
Show Gist options
  • Select an option

  • Save neurodrone/6694742 to your computer and use it in GitHub Desktop.

Select an option

Save neurodrone/6694742 to your computer and use it in GitHub Desktop.
Quick pack
/*
usage:
pack(unsigned char *buf, const char *fmt, ...);
similar to sprintf():
put all arguments in "..." into the buffer, according to the format
string "fmt", in a way that they can be easily retrieved with unpack()
unpack(const unsigned char *buf, const char *fmt, ...);
similar to sscanf():
retrieves data from the buffer according to "fmt". arguments in "..."
must be pointers to appropriate types.
format specifiers:
b = int8_t B = uint8_t
h = int16_t H = uint16_t
i = int32_t I = uint32_t
l = int64_t L = uint64_t
_ = skip one byte (mainly useful for unpacking)
example:
uint8_t a;
uint16_t b = 1337;
int32_t c = -12345678;
pack(buf, "BBHi", 255, 0, b, c);
unpack(buf, "B_Hi", &a, &b, &c);
assert(a == 255);
assert(b == 1337);
assert(c == -12345678);
*/
#ifndef PACK_H
#define PACK_H
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdint.h>
static size_t pack_bytes(const char *fmt);
static size_t pack(unsigned char *buf, const char *fmt, ...);
static size_t unpack(const unsigned char *buf, const char *fmt, ...);
static void buffer_put(unsigned char *buffer, uint64_t value, size_t bytes);
static uintmax_t buffer_get(const unsigned char *buffer, size_t bytes);
static size_t pack_bytes(const char *fmt)
{
size_t bytes = 0;
char c;
while ((c = *fmt++)) {
switch (c) {
case '_':
case 'b':
case 'B':
bytes += 1;
break;
case 'h':
case 'H':
bytes += 2;
break;
case 'i':
case 'I':
bytes += 4;
break;
case 'l':
case 'L':
bytes += 8;
break;
}
}
return bytes;
}
#define PACK_UNPACK(c, name, CASE) \
static size_t name(c unsigned char *buf, const char *fmt, ...) \
{ \
va_list ap; \
size_t sz, total = 0; \
\
va_start(ap, fmt); \
\
for (; *fmt; fmt++) \
{ \
switch (*fmt) \
{ \
CASE('b', int, int8_t); \
CASE('B', int, uint8_t); \
\
CASE('h', int, int16_t); \
CASE('H', int, uint16_t); \
\
CASE('i', int32_t, int32_t); \
CASE('I', uint32_t, uint32_t); \
\
CASE('l', int64_t, int64_t); \
CASE('L', uint64_t, uint64_t); \
\
case '_': \
sz = 1; \
break; \
\
default: \
va_end(ap); \
return total; \
} \
\
buf += sz; \
total += sz; \
} \
\
va_end(ap); \
\
return total; \
}
#define CASE_PACK(chr, ap_type, type) \
case chr: \
{ \
type val = va_arg(ap, ap_type); \
sz = sizeof (type); \
buffer_put(buf, val, sz); \
} \
break
#define CASE_UNPACK(chr, ap_type, type) \
case chr: \
{ \
type *p = va_arg(ap, type*); \
sz = sizeof (type); \
*p = buffer_get(buf, sz); \
} \
break
/* define pack() */
PACK_UNPACK(, pack, CASE_PACK)
/* define unpack() */
PACK_UNPACK(const, unpack, CASE_UNPACK)
static void buffer_put(unsigned char *buffer, uint64_t value, size_t bytes)
{
while (bytes--) {
buffer[bytes] = value & 0xFF;
value >>= 8;
}
}
static uint64_t buffer_get(const unsigned char *buffer, size_t bytes)
{
uint64_t value = 0;
const unsigned char *end = buffer + bytes;
while (buffer < end) {
value <<= 8;
value += *buffer++;
}
return value;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment