/* * Copyright (C) 2020-2021 Tomasz Kramkowski * SPDX-License-Identifier: MIT */ // @BUILD_CC host #include #include #include #include #include #include #include struct { enum endian { BIG, LITTLE } e; char *prefix; } endian[] = { { BIG, "" }, { BIG, ">" }, { LITTLE, "<" }, }; struct fmtinfo { char fmt; char *ptype; char *cfmt; char *type; intmax_t min; uintmax_t max; size_t size; } fmtinfo[] = { { 'b', "SCHAR", "d", "signed char", INTMAX_C( -128), UINTMAX_C( 127), 1 }, { 'B', "UCHAR", "u", "unsigned char", INTMAX_C( 0), UINTMAX_C( 255), 1 }, { 'h', "SHORT", "d", "signed short", INTMAX_C( -32768), UINTMAX_C( 32767), 2 }, { 'H', "USHORT", "u", "unsigned short", INTMAX_C( 0), UINTMAX_C( 65535), 2 }, { 'i', "INT", "d", "signed int", INTMAX_C( -32768), UINTMAX_C( 32767), 2 }, { 'I', "UINT", "u", "unsigned int", INTMAX_C( 0), UINTMAX_C( 65535), 2 }, { 'l', "LONG", "ld", "signed long", INTMAX_C( -2147483648), UINTMAX_C( 2147483647), 4 }, { 'L', "ULONG", "lu", "unsigned long", INTMAX_C( 0), UINTMAX_C( 4294967295), 4 }, { 'q', "LLONG", "lld", "signed long long", -INTMAX_C(9223372036854775807) - 1, UINTMAX_C( 9223372036854775807), 8 }, { 'Q', "ULLONG", "llu", "unsigned long long", INTMAX_C( 0), UINTMAX_C(18446744073709551615), 8 }, }; static char cchar(char c) { if (c == '\0') return c; if (isalnum(c)) return c; return '_'; } static const char *cname(const char *s) { static char ret[100]; for (int i = 0; i < 99; i++) { ret[i] = cchar(s[i]); if (s[i] == '\0') break; } return ret; } static const char *u2bytes(enum endian e, int n, uintmax_t v) { static char b[100]; char *p = b; assert(n > 0 && n <= 8); for (int i = 0; i < n; i++) { p += snprintf(p, b + sizeof(b) - p, "%s0x%x", i == 0 ? "" : ", ", (int)((v >> (e == LITTLE ? i : n - i - 1) * 8) & 0xff)); assert(p < b + sizeof(b)); } return b; } static uintmax_t i2u(int n, intmax_t v) { if (v >= 0) return v; return (UINTMAX_MAX >> (sizeof (uintmax_t) * CHAR_BIT - n * 8)) + v + 1; } static const char *i2bytes(enum endian e, int n, intmax_t v) { return u2bytes(e, n, i2u(n, v)); } static void unpack_signed(FILE *out, const struct fmtinfo *fi, enum endian e, intmax_t testval, int arraysize) { char sizeprefix[11] = ""; const char *data; if (arraysize >= 1) snprintf(sizeprefix, sizeof sizeprefix, "%d", arraysize); else arraysize = 1; fprintf(out, "\tCHECK_UNPACK(DATA("); data = i2bytes(endian[e].e, fi->size, testval); for (int i = 0; i < arraysize; i++) fprintf(out, "%s%s", i == 0 ? "" : ", ", data); fprintf(out, "), \"%s%s%c\", v);\n", endian[e].prefix, sizeprefix, fi->fmt); for (int i = 0; i < arraysize; i++) fprintf(out, "\tCHECK_EQUAL(PRIdMAX, (intmax_t)v[%d], -INTMAX_C(%" PRIdMAX ")-1);\n", i, -(testval + 1)); } static void unpack_unsigned(FILE *out, const struct fmtinfo *fi, enum endian e, uintmax_t testval, int arraysize) { char sizeprefix[11] = ""; const char *data; if (arraysize >= 1) snprintf(sizeprefix, sizeof sizeprefix, "%d", arraysize); else arraysize = 1; fprintf(out, "\tCHECK_UNPACK(DATA("); data = u2bytes(endian[e].e, fi->size, testval); for (int i = 0; i < arraysize; i++) fprintf(out, "%s%s", i == 0 ? "" : ", ", data); fprintf(out, "), \"%s%s%c\", v);\n", endian[e].prefix, sizeprefix, fi->fmt); for (int i = 0; i < arraysize; i++) fprintf(out, "\tCHECK_EQUAL(PRIdMAX, (uintmax_t)v[%d], UINTMAX_C(%" PRIuMAX "));\n", i, testval); } static void unpack_gen(FILE *out, const struct fmtinfo *fi, int arraysize) { int realsize = arraysize == 0 ? 1 : arraysize; bool sign; sign = islower(fi->fmt); fprintf(out, "TEST(unpack_simple%d_%s, \"unpack simple", arraysize, cname(fi->type)); if (arraysize != 0) fprintf(out, " array[%d]", arraysize); fprintf(out, " %s\")\n", fi->type); fprintf(out, "{\n"); fprintf(out, "\t%s v[%d] = { ", fi->type, realsize ); for (int i = 0; i < realsize; i++) fprintf(out, "%s__LINE__ + %d", i == 0 ? "" : ", ", i); fprintf(out, " };\n"); for (size_t e = 0; e < sizeof endian / sizeof endian[0]; e++) { for (int i = sign ? -1 : 0; i <= 1; i++) unpack_signed(out, fi, e, i, arraysize); if (sign) unpack_signed(out, fi, e, fi->min, arraysize); unpack_unsigned(out, fi, e, fi->max, arraysize); } fprintf(out, "\treturn true;\n"); fprintf(out, "}\n"); } static void unpack_struct_signed(FILE *out, const struct fmtinfo *fi, enum endian e, intmax_t testval, int arraysize) { char sizeprefix[11] = ""; const char *data; if (arraysize >= 1) snprintf(sizeprefix, sizeof sizeprefix, "%d", arraysize); else arraysize = 1; fprintf(out, "\tCHECK_UNPACK_STRUCT(DATA("); data = i2bytes(e, fi->size, testval); for (int i = 0; i < arraysize; i++) fprintf(out, "%s%s", i == 0 ? "" : ", ", data); fprintf(out, "), &args, &v);\n"); for (int i = 0; i < arraysize; i++) fprintf(out, "\tCHECK_EQUAL(\"%s\", v.field[%d], (%s)-INTMAX_C(%" PRIdMAX ")-1);\n", fi->cfmt, i, fi->type, -(testval + 1)); } static void unpack_struct_unsigned(FILE *out, const struct fmtinfo *fi, enum endian e, uintmax_t testval, int arraysize) { char sizeprefix[11] = ""; const char *data; if (arraysize >= 1) snprintf(sizeprefix, sizeof sizeprefix, "%d", arraysize); else arraysize = 1; fprintf(out, "\tCHECK_UNPACK_STRUCT(DATA("); data = u2bytes(e, fi->size, testval); for (int i = 0; i < arraysize; i++) fprintf(out, "%s%s", i == 0 ? "" : ", ", data); fprintf(out, "), &args, &v);\n"); for (int i = 0; i < arraysize; i++) fprintf(out, "\tCHECK_EQUAL(\"%s\", v.field[%d], (%s)UINTMAX_C(%" PRIuMAX "));\n", \ fi->cfmt, i, fi->type, testval); } static void unpack_struct_gen(FILE *out, const struct fmtinfo *fi, int arraysize) { int realsize = arraysize == 0 ? 1 : arraysize; bool sign; sign = islower(fi->fmt); fprintf(out, "TEST(unpack_struct_simple%d_%s, \"unpack_struct simple", \ arraysize, cname(fi->type)); if (arraysize != 0) fprintf(out, " array[%d]", arraysize); fprintf(out, " %s\")\n", fi->type); fprintf(out, "{\n"); fprintf(out, "\tstruct dest {\n"); fprintf(out, "\t\t%s field[%d];\n", fi->type, realsize); fprintf(out, "\t} v = {{ "); for (int i = 0; i < realsize; i++) fprintf(out, "%s(__LINE__ + %d) %% UINTMAX_C(%" PRIuMAX ")", i == 0 ? "" : ", ", i, fi->max); fprintf(out, " }};\n"); fprintf(out, "\tstruct pack_args args = {\n"); fprintf(out, "\t\t.fields = (struct pack_field []){\n"); fprintf(out, "\t\t\t{ PACK_TYPE_%s, %d, offsetof(struct dest, field) }\n", fi->ptype, realsize); fprintf(out, "\t\t},\n"); fprintf(out, "\t\t.num_fields = 1,\n"); fprintf(out, "\t};\n"); for (enum endian e = BIG; e <= LITTLE; e++) { fprintf(out, "\targs.endian = PACK_ENDIAN_%s;\n", e == BIG ? "BIG" : "LITTLE"); for (int i = sign ? -1 : 0; i <= 1; i++) unpack_struct_signed(out, fi, e, i, arraysize); if (sign) unpack_struct_signed(out, fi, e, fi->min, arraysize); unpack_struct_unsigned(out, fi, e, fi->max, arraysize); } fprintf(out, "\treturn true;\n"); fprintf(out, "}\n"); } int main(void) { FILE *out = stdout; for (size_t i = 0; i < sizeof fmtinfo / sizeof fmtinfo[0]; i++) { unpack_gen(out, &fmtinfo[i], 0); for (int j = 2; j < 4; j++) unpack_gen(out, &fmtinfo[i], j); } for (size_t i = 0; i < sizeof fmtinfo / sizeof fmtinfo[0]; i++) { unpack_struct_gen(out, &fmtinfo[i], 0); for (int j = 2; j < 4; j++) unpack_struct_gen(out, &fmtinfo[i], j); } }