From 85d3347f01e3d27541e5d2fadf5ab49cc6cf54f9 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 13 Mar 2015 19:38:45 -0700 Subject: [PATCH] Import integer packing library. This simple library is used in a couple projects, better to maintain it in one place. Hook it into the newfangled test suite too. --- .gitignore | 5 ++ Makefile.am | 9 ++++ src/pack.c | 123 +++++++++++++++++++++++++++++++++++++++++++ src/pack.h | 55 ++++++++++++++++++++ src/tap.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ src/tap.h | 31 +++++++++++ t/.gitignore | 2 + t/packtests.c | 48 +++++++++++++++++ t/packtests64.c | 44 ++++++++++++++++ t/packtestu.c | 40 ++++++++++++++ t/packtestu64.c | 37 +++++++++++++ tests/functions.at | 30 +++++++++++ testsuite.at | 3 ++ 13 files changed, 553 insertions(+) create mode 100644 src/pack.c create mode 100644 src/pack.h create mode 100644 src/tap.c create mode 100644 src/tap.h create mode 100644 t/.gitignore create mode 100644 t/packtests.c create mode 100644 t/packtests64.c create mode 100644 t/packtestu.c create mode 100644 t/packtestu64.c create mode 100644 tests/functions.at diff --git a/.gitignore b/.gitignore index 9171f9d..02e9985 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ /autom4te.cache /install-sh /missing +/depcomp +/compile /testsuite.dir /testsuite.log /testsuite @@ -11,4 +13,7 @@ /atconfig Makefile.in Makefile +.dirstamp .deps +*.lo +*.o diff --git a/Makefile.am b/Makefile.am index 21b5f95..6f5e776 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,15 @@ ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -I$(top_srcdir)/src + +check_PROGRAMS = t/packtestu t/packtests t/packtestu64 t/packtests64 + +t_packtestu_SOURCES = t/packtestu.c src/pack.c src/tap.c +t_packtests_SOURCES = t/packtests.c src/pack.c src/tap.c +t_packtestu64_SOURCES = t/packtestu64.c src/pack.c src/tap.c +t_packtests64_SOURCES = t/packtests64.c src/pack.c src/tap.c + EXTRA_DIST = SUFFIXES = diff --git a/src/pack.c b/src/pack.c new file mode 100644 index 0000000..a4dd119 --- /dev/null +++ b/src/pack.c @@ -0,0 +1,123 @@ +/* + * Copyright © 2009 Nick Bowler + * + * Portable binary (de-)serialisation of integral types. + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#include "pack.h" + +/* Unsigned integer packing. */ +#define DEFPACK_BE(bits, type) void pack_ ## bits ## _be ( \ + unsigned char *out, type v \ +) { \ + unsigned i; \ + for (i = 1; i <= bits/8; i++) { \ + out[bits/8 - i] = v % 256; \ + v /= 256; \ + } \ +} + +#define DEFPACK_LE(bits, type) void pack_ ## bits ## _le ( \ + unsigned char *out, type v \ +) { \ + unsigned i; \ + for (i = 0; i < bits/8; i++) { \ + out[i] = v % 256; \ + v /= 256; \ + } \ +} + +DEFPACK_BE(16, unsigned short) +DEFPACK_BE(32, unsigned long) +#if PACK_HAVE_64BIT +DEFPACK_BE(64, unsigned long long) +#endif + +DEFPACK_LE(16, unsigned short) +DEFPACK_LE(32, unsigned long) +#if PACK_HAVE_64BIT +DEFPACK_LE(64, unsigned long long) +#endif + +#define DEFUNPACK_BE(bits, type) type unpack_ ## bits ## _be ( \ + const unsigned char *in \ +) { \ + type v = 0; \ + unsigned i; \ + for (i = 0; i < bits/8; i++) { \ + v *= 256; \ + v += in[i]; \ + } \ + return v; \ +} + +#define DEFUNPACK_LE(bits, type) type unpack_ ## bits ## _le ( \ + const unsigned char *in \ +) { \ + type v = 0; \ + unsigned i; \ + for (i = 1; i <= bits/8; i++) { \ + v *= 256; \ + v += in[bits/8 - i]; \ + } \ + return v; \ +} + +DEFUNPACK_BE(16, unsigned short) +DEFUNPACK_BE(32, unsigned long) +#if PACK_HAVE_64BIT +DEFUNPACK_BE(64, unsigned long long) +#endif + +DEFUNPACK_LE(16, unsigned short) +DEFUNPACK_LE(32, unsigned long) +#if PACK_HAVE_64BIT +DEFUNPACK_LE(64, unsigned long long) +#endif + +/* + * Two's complement signed integer packing. This is unlikely to work on + * systems that don't themselves use two's complement. + */ + +#define DEFUNPACK_SBE(bits, max, type) type unpack_s ## bits ## _be ( \ + const unsigned char *in \ +) { \ + type v = 0; \ + unsigned i; \ + int sign = (in[0] & 0x80) ? 1 : 0; \ + for (i = 0; i < bits/8; i++) { \ + v *= 256; \ + v += in[i] & (i == 0 ? 0x7f : 0xff); \ + } \ + return sign*(-max-1) + v; \ +} + +#define DEFUNPACK_SLE(bits, max, type) type unpack_s ## bits ## _le ( \ + const unsigned char *in \ +) { \ + type v = 0; \ + unsigned i; \ + int sign = (in[bits/8 - 1] & 0x80) ? 1 : 0; \ + for (i = 1; i <= bits/8; i++) { \ + v *= 256; \ + v += in[bits/8 - i] & (i == 1 ? 0x7f : 0xff); \ + } \ + return sign*(-max-1) + v; \ +} + +DEFUNPACK_SBE(16, 32767, short) +DEFUNPACK_SBE(32, 2147483647l, long) +#if PACK_HAVE_64BIT +DEFUNPACK_SBE(64, 9223372036854775807ll, long long) +#endif + +DEFUNPACK_SLE(16, 32767, short) +DEFUNPACK_SLE(32, 2147483647l, long) +#if PACK_HAVE_64BIT +DEFUNPACK_SLE(64, 9223372036854775807ll, long long) +#endif diff --git a/src/pack.h b/src/pack.h new file mode 100644 index 0000000..26c2546 --- /dev/null +++ b/src/pack.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2009 Nick Bowler + * + * Portable binary (de-)serialisation of integral types. + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#ifndef DX_PACK_H_ +#define DX_PACK_H_ + +#include +#if !defined(PACK_HAVE_64BIT) && defined(ULLONG_MAX) && defined(LLONG_MAX) +# define PACK_HAVE_64BIT 1 +#endif + +void pack_16_be(unsigned char *, unsigned short); +void pack_32_be(unsigned char *, unsigned long); +#if PACK_HAVE_64BIT +void pack_64_be(unsigned char *, unsigned long long); +#endif + +void pack_16_le(unsigned char *, unsigned short); +void pack_32_le(unsigned char *, unsigned long); +#if PACK_HAVE_64BIT +void pack_64_le(unsigned char *, unsigned long long); +#endif + +unsigned short unpack_16_be(const unsigned char *); +unsigned long unpack_32_be(const unsigned char *); +#if PACK_HAVE_64BIT +unsigned long long unpack_64_be(const unsigned char *); +#endif + +unsigned short unpack_16_le(const unsigned char *); +unsigned long unpack_32_le(const unsigned char *); +#if PACK_HAVE_64BIT +unsigned long long unpack_64_le(const unsigned char *); +#endif + +short unpack_s16_be(const unsigned char *); +long unpack_s32_be(const unsigned char *); +#if PACK_HAVE_64BIT +long long unpack_s64_be(const unsigned char *); +#endif + +short unpack_s16_le(const unsigned char *); +long unpack_s32_le(const unsigned char *); +#if PACK_HAVE_64BIT +long long unpack_s64_le(const unsigned char *); +#endif + +#endif diff --git a/src/tap.c b/src/tap.c new file mode 100644 index 0000000..4416a9c --- /dev/null +++ b/src/tap.c @@ -0,0 +1,126 @@ +/* + * Copyright © 2015 Nick Bowler + * + * Simple TAP output library for C programs. + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#include +#include +#include + +#include "tap.h" + +static unsigned plan, total, passed, xpassed, failed, xfailed; + +void tap_plan(unsigned expected_tests) +{ + if (plan) + tap_bail_out("tap_plan: tests already planned!"); + if (!expected_tests) + tap_bail_out("tap_plan: no tests to run!"); + + plan = expected_tests; + printf("1..%u\n", plan); +} + +void tap_done(void) +{ + if (!plan) { + tap_plan(total); + } else if (total != plan) { + tap_bail_out("tap_done: planned %u tests, ran %u", plan, total); + } + + assert(total == passed + xpassed + failed + xfailed); + exit(failed || xpassed); +} + +void tap_vbail_out(const char *fmt, va_list ap) +{ + printf("Bail out!%*s", fmt != NULL, ""); + if (fmt) + vprintf(fmt, ap); + putchar('\n'); + exit(99); +} + +void tap_bail_out(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + tap_vbail_out(fmt, ap); + va_end(ap); +} + +void tap_vskip_all(const char *fmt, va_list ap) +{ + if (plan || total) + tap_bail_out("tap_skip_all: already started tests"); + + printf("1..0 # skip%*s", fmt != NULL, ""); + if (fmt) + vprintf(fmt, ap); + putchar('\n'); + exit(77); +} + +void tap_skip_all(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + tap_vskip_all(fmt, ap); + va_end(ap); +} + +void tap_vdiag(const char *fmt, va_list ap) +{ + printf("#%*s", fmt != NULL, ""); + if (fmt) + vprintf(fmt, ap); + putchar('\n'); +} + +void tap_diag(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + tap_vdiag(fmt, ap); + va_end(ap); +} + +int tap_vresult(int ok, const char *fmt, va_list ap) +{ + if (!ok) + printf("not "); + printf("ok %u%*s", ++total, fmt != NULL, ""); + if (fmt) + vprintf(fmt, ap); + putchar('\n'); + + if (!total) + tap_bail_out("cannot handle so many tests"); + + passed += !!ok; + failed += !ok; + + return ok; +} + +int tap_result(int ok, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = tap_vresult(ok, fmt, ap); + va_end(ap); + + return ret; +} diff --git a/src/tap.h b/src/tap.h new file mode 100644 index 0000000..16a4617 --- /dev/null +++ b/src/tap.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2015 Nick Bowler + * + * Simple TAP output library for C programs. + * + * License WTFPL2: Do What The Fuck You Want To Public License, version 2. + * This is free software: you are free to do what the fuck you want to. + * There is NO WARRANTY, to the extent permitted by law. + */ + +#ifndef DX_TAP_H_ +#define DX_TAP_H_ + +#include + +void tap_plan(unsigned expected_tests); +void tap_done(void); + +void tap_vbail_out(const char *fmt, va_list ap); +void tap_bail_out(const char *fmt, ...); + +void tap_vskip_all(const char *fmt, va_list ap); +void tap_skip_all(const char *fmt, ...); + +void tap_vdiag(const char *fmt, va_list ap); +void tap_diag(const char *fmt, ...); + +int tap_vresult(int ok, const char *fmt, va_list ap); +int tap_result(int ok, const char *fmt, ...); + +#endif diff --git a/t/.gitignore b/t/.gitignore new file mode 100644 index 0000000..b36f577 --- /dev/null +++ b/t/.gitignore @@ -0,0 +1,2 @@ +packtest[su] +packtest[su]64 diff --git a/t/packtests.c b/t/packtests.c new file mode 100644 index 0000000..b3a7a8b --- /dev/null +++ b/t/packtests.c @@ -0,0 +1,48 @@ +#include "pack.h" +#include "tap.h" + +static const unsigned char zero[8]; +static const unsigned char minus_one[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const unsigned char min[9] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0x80 +}; + +static const unsigned char test_pattern[8] = { + 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0d, 0xca, 0xfe +}; + +#define test(func, pattern, expected) do { \ + long result__ = (func)(pattern); \ + if (!tap_result(result__ == (expected), "%s(%s)", #func, #expected)) { \ + tap_diag(" expected: %ld", (long)(expected)); \ + tap_diag(" actual: %ld", result__); \ + } \ +} while (0) + +int main(void) +{ + tap_plan(16); + + test(unpack_s16_be, zero, 0); + test(unpack_s16_be, minus_one, -1); + test(unpack_s16_be, test_pattern, -8531); + test(unpack_s16_be, min, -32767-1); + test(unpack_s32_be, zero, 0); + test(unpack_s32_be, minus_one, -1); + test(unpack_s32_be, test_pattern, -559038737); + test(unpack_s32_be, min, -2147483647-1); + + test(unpack_s16_le, zero, 0); + test(unpack_s16_le, minus_one, -1); + test(unpack_s16_le, test_pattern, -21026); + test(unpack_s16_le, min+7, -32767-1); + test(unpack_s32_le, zero, 0); + test(unpack_s32_le, minus_one, -1); + test(unpack_s32_le, test_pattern, -272716322); + test(unpack_s32_le, min+5, -2147483647-1); + + tap_done(); +} diff --git a/t/packtests64.c b/t/packtests64.c new file mode 100644 index 0000000..a8fefe9 --- /dev/null +++ b/t/packtests64.c @@ -0,0 +1,44 @@ +#include "pack.h" +#include "tap.h" + +static const unsigned char zero[8]; +static const unsigned char minus_one[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const unsigned char min[9] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0x80 +}; + +static const unsigned char test_pattern[8] = { + 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0d, 0xca, 0xfe +}; + +#define test(func, pattern, expected) do { \ + long long result__ = (func)(pattern); \ + if (!tap_result(result__ == (expected), "%s(%s)", #func, #expected)) { \ + tap_diag(" expected: %ld", (long long)(expected)); \ + tap_diag(" actual: %ld", result__); \ + } \ +} while (0) + +int main(void) +{ +#if PACK_HAVE_64BIT + tap_plan(8); + + test(unpack_s64_be, zero, 0); + test(unpack_s64_be, minus_one, -1); + test(unpack_s64_be, test_pattern, -2401053088584709378); + test(unpack_s64_be, min, -9223372036854775807-1); + + test(unpack_s64_le, zero, 0); + test(unpack_s64_le, minus_one, -1); + test(unpack_s64_le, test_pattern, -87241914314740258); + test(unpack_s64_le, min+1, -9223372036854775807-1); +#else + tap_skip_all("no 64-bit support"); +#endif + + tap_done(); +} diff --git a/t/packtestu.c b/t/packtestu.c new file mode 100644 index 0000000..5ae3af9 --- /dev/null +++ b/t/packtestu.c @@ -0,0 +1,40 @@ +#include "pack.h" +#include "tap.h" + +static const unsigned char zero[8]; +static const unsigned char minus_one[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const unsigned char test_pattern[8] = { + 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0d, 0xca, 0xfe +}; + +#define test(func, pattern, expected) do { \ + unsigned long result__ = (func)(pattern); \ + if (!tap_result(result__ == (expected), "%s(%s)", #func, #expected)) { \ + tap_diag(" expected: %lu", (unsigned long)(expected)); \ + tap_diag(" actual: %lu", result__); \ + } \ +} while (0) + +int main(void) +{ + tap_plan(12); + + test(unpack_16_be, zero, 0); + test(unpack_16_be, minus_one, 0xffff); + test(unpack_16_be, test_pattern, 0xdead); + test(unpack_32_be, zero, 0); + test(unpack_32_be, minus_one, 0xffffffff); + test(unpack_32_be, test_pattern, 0xdeadbeef); + + test(unpack_16_le, zero, 0); + test(unpack_16_le, minus_one, 0xffff); + test(unpack_16_le, test_pattern, 0xadde); + test(unpack_32_le, zero, 0); + test(unpack_32_le, minus_one, 0xffffffff); + test(unpack_32_le, test_pattern, 0xefbeadde); + + tap_done(); +} diff --git a/t/packtestu64.c b/t/packtestu64.c new file mode 100644 index 0000000..217404f --- /dev/null +++ b/t/packtestu64.c @@ -0,0 +1,37 @@ +#include "pack.h" +#include "tap.h" + +static const unsigned char zero[8]; +static const unsigned char minus_one[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static const unsigned char test_pattern[8] = { + 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0d, 0xca, 0xfe +}; + +#define test(func, pattern, expected) do { \ + unsigned long long result__ = (func)(pattern); \ + if (!tap_result(result__ == (expected), "%s(%s)", #func, #expected)) { \ + tap_diag(" expected: %llu", (unsigned long long)(expected)); \ + tap_diag(" actual: %llu", result__); \ + } \ +} while (0) + +int main(void) +{ +#if PACK_HAVE_64BIT + tap_plan(6); + + test(unpack_64_be, zero, 0); + test(unpack_64_be, minus_one, 0xffffffffffffffff); + test(unpack_64_be, test_pattern, 0xdeadbeeff00dcafe); + test(unpack_64_le, zero, 0); + test(unpack_64_le, minus_one, 0xffffffffffffffff); + test(unpack_64_le, test_pattern, 0xfeca0df0efbeadde); +#else + tap_skip_all("no 64-bit support"); +#endif + + tap_done(); +} diff --git a/tests/functions.at b/tests/functions.at new file mode 100644 index 0000000..115b8cf --- /dev/null +++ b/tests/functions.at @@ -0,0 +1,30 @@ +dnl Copyright © 2015 Nick Bowler +dnl +dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. +dnl This is free software: you are free to do what the fuck you want to. +dnl There is NO WARRANTY, to the extent permitted by law. + +m4_divert_push([PREPARE_TESTS])dnl +test_run_tap () { + "$builddir/t/$1" > "$1.tap" + status=$? + cat "$1.tap" + prove -e cat "$1.tap" + return $status +} +m4_divert_pop([PREPARE_TESTS]) + +m4_define([TEST_TAP], [AT_CHECK([test_run_tap "$1"], [0], [ignore])]) +m4_define([TEST_TAP_SIMPLE], [dnl +AT_SETUP([$1]) +AT_KEYWORDS([$4])dnl +m4_n([$3])dnl +TEST_TAP([$2]) +AT_CLEANUP]) + +AT_BANNER([Binary packing functions]) + +TEST_TAP_SIMPLE([signed unpacking], [packtests], [], [pack]) +TEST_TAP_SIMPLE([unsigned unpacking], [packtestu], [], [pack]) +TEST_TAP_SIMPLE([64-bit signed unpacking], [packtests64], [], [pack]) +TEST_TAP_SIMPLE([64-bit unsigned unpacking], [packtestu64], [], [pack]) diff --git a/testsuite.at b/testsuite.at index aa5abaf..ee9925f 100644 --- a/testsuite.at +++ b/testsuite.at @@ -7,4 +7,7 @@ dnl There is NO WARRANTY, to the extent permitted by law. AT_INIT AT_COLOR_TESTS +AT_TESTED([prove]) + m4_include([tests/macros.at]) +m4_include([tests/functions.at]) -- 2.43.2