/** @file High-level utility functions for decoders. Copyright (C) 2018 Christian Zuckschwerdt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "decoder_util.h" #include #include #include "fatal.h" // create decoder functions r_device *decoder_create(r_device const *dev_template, unsigned user_data_size) { r_device *r_dev = calloc(1, sizeof (*r_dev)); if (!r_dev) { WARN_MALLOC("decoder_create()"); return NULL; // NOTE: returns NULL on alloc failure. } if (dev_template) { *r_dev = *dev_template; // copy } if (user_data_size) { r_dev->decode_ctx = calloc(1, user_data_size); if (!r_dev->decode_ctx) { WARN_MALLOC("decoder_create()"); free(r_dev); return NULL; // NOTE: returns NULL on alloc failure. } } return r_dev; } void *decoder_user_data(r_device *decoder) { return decoder->decode_ctx; } // output functions void decoder_output_log(r_device *decoder, int level, data_t *data) { decoder->log_fn(decoder, level, data); } void decoder_output_data(r_device *decoder, data_t *data) { decoder->output_fn(decoder, data); } // helper static char *bitrow_asprint_code(uint8_t const *bitrow, unsigned bit_len) { if (bit_len > BITBUF_ROWS * BITBUF_COLS * 8) { return NULL; // NOTE: returns NULL on invalid arguments. } size_t size = 8 + bit_len / 4 + 1; // "{nnnnnn}..\0" char *row_code = malloc(size); if (!row_code) { WARN_MALLOC("decoder_output_bitbuffer()"); return NULL; // NOTE: returns NULL on alloc failure. } // a simple bitrow representation int offs = snprintf(row_code, size, "{%u}", bit_len); // print byte-wide for (unsigned col = 0; col < (unsigned)(bit_len + 7) / 8; ++col) { snprintf(&row_code[offs + 2 * col], size - (offs + 2 * col), "%02x", bitrow[col]); } // remove last nibble if needed row_code[offs + 2 * (bit_len + 3) / 8] = '\0'; // print at least one '0' if (bit_len == 0) { snprintf(row_code + offs, size - offs, "0"); } return row_code; } static char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len) { char *row_bits, *p; p = row_bits = malloc(bit_len + bit_len / 4 + 1); // "1..\0" (1 space per nibble) if (!row_bits) { WARN_MALLOC("bitrow_asprint_bits()"); return NULL; // NOTE: returns NULL on alloc failure. } // print bit-wide with a space every nibble for (unsigned i = 0; i < bit_len; ++i) { if (i > 0 && i % 4 == 0) { *p++ = ' '; } if (bitrow[i / 8] & (0x80 >> (i % 8))) { *p++ = '1'; } else { *p++ = '0'; } } *p++ = '\0'; return row_bits; } // variadic output functions int decoder_verbose(r_device *decoder) { return decoder->verbose; } void decoder_log(r_device *decoder, int level, char const *func, char const *msg) { if (decoder->verbose >= level) { // note that decoder levels start at LOG_WARNING level += 4; /* clang-format off */ data_t *data = data_make( "src", "", DATA_STRING, func, "lvl", "", DATA_INT, level, "msg", "", DATA_STRING, msg, NULL); /* clang-format on */ decoder_output_log(decoder, level, data); } } void decoder_logf(r_device *decoder, int level, char const *func, _Printf_format_string_ const char *format, ...) { if (decoder->verbose >= level) { char msg[80]; // fixed length limit va_list ap; va_start(ap, format); int len = vsnprintf(msg, sizeof(msg), format, ap); va_end(ap); if (len >= (int)sizeof(msg)) { decoder_log(decoder, 0, __func__, "Truncating log message"); } decoder_log(decoder, level, func, msg); } } void decoder_log_bitbuffer(r_device *decoder, int level, char const *func, const bitbuffer_t *bitbuffer, char const *msg) { if (decoder->verbose >= level) { // note that decoder levels start at LOG_WARNING level += 4; char *row_codes[BITBUF_ROWS] = {0}; char *row_bits[BITBUF_ROWS] = {0}; unsigned num_rows = bitbuffer->num_rows; for (unsigned i = 0; i < num_rows; i++) { row_codes[i] = bitrow_asprint_code(bitbuffer->bb[i], bitbuffer->bits_per_row[i]); if (decoder->verbose_bits) { row_bits[i] = bitrow_asprint_bits(bitbuffer->bb[i], bitbuffer->bits_per_row[i]); } } /* clang-format off */ data_t *data = data_make( "src", "", DATA_STRING, func, "lvl", "", DATA_INT, level, "msg", "", DATA_STRING, msg, "num_rows", "", DATA_INT, num_rows, "codes", "", DATA_ARRAY, data_array(num_rows, DATA_STRING, row_codes), NULL); /* clang-format on */ if (decoder->verbose_bits) { data = data_ary(data, "bits", "", NULL, data_array(num_rows, DATA_STRING, row_bits)); } decoder_output_log(decoder, level, data); for (unsigned i = 0; i < num_rows; i++) { free(row_codes[i]); free(row_bits[i]); } } } void decoder_logf_bitbuffer(r_device *decoder, int level, char const *func, const bitbuffer_t *bitbuffer, _Printf_format_string_ const char *format, ...) { // TODO: pass to interested outputs if (decoder->verbose >= level) { char msg[80]; // fixed length limit va_list ap; va_start(ap, format); int len = vsnprintf(msg, sizeof(msg), format, ap); va_end(ap); if (len >= (int)sizeof(msg)) { decoder_log(decoder, 0, __func__, "Truncating log message"); } decoder_log_bitbuffer(decoder, level, func, bitbuffer, msg); } } void decoder_log_bitrow(r_device *decoder, int level, char const *func, uint8_t const *bitrow, unsigned bit_len, char const *msg) { if (decoder->verbose >= level) { // note that decoder levels start at LOG_WARNING level += 4; char *row_code; char *row_bits = NULL; row_code = bitrow_asprint_code(bitrow, bit_len); /* clang-format off */ data_t *data = data_make( "src", "", DATA_STRING, func, "lvl", "", DATA_INT, level, "msg", "", DATA_STRING, msg, "codes", "", DATA_STRING, row_code, NULL); /* clang-format on */ if (decoder->verbose_bits) { row_bits = bitrow_asprint_bits(bitrow, bit_len); data = data_str(data, "bits", "", NULL, row_bits); } decoder_output_log(decoder, level, data); free(row_code); free(row_bits); } } void decoder_logf_bitrow(r_device *decoder, int level, char const *func, uint8_t const *bitrow, unsigned bit_len, _Printf_format_string_ const char *format, ...) { if (decoder->verbose >= level) { char msg[80]; // fixed length limit va_list ap; va_start(ap, format); int len = vsnprintf(msg, sizeof(msg), format, ap); va_end(ap); if (len >= (int)sizeof(msg)) { decoder_log(decoder, 0, __func__, "Truncating log message"); } decoder_log_bitrow(decoder, level, func, bitrow, bit_len, msg); } } /* TODO: maybe use as decoder_log function void decoder_output_bitbuffer_array(r_device *decoder, bitbuffer_t const *bitbuffer, char const *msg) { data_t *data; data_t *row_data[BITBUF_ROWS]; char *row_codes[BITBUF_ROWS]; char row_bytes[BITBUF_ROWS * BITBUF_COLS * 2 + 1]; // TODO: this is a lot of stack unsigned i; for (i = 0; i < bitbuffer->num_rows; i++) { row_bytes[0] = '\0'; // print byte-wide for (unsigned col = 0; col < (unsigned)(bitbuffer->bits_per_row[i] + 7) / 8; ++col) { sprintf(&row_bytes[2 * col], "%02x", bitbuffer->bb[i][col]); } // remove last nibble if needed row_bytes[2 * (bitbuffer->bits_per_row[i] + 3) / 8] = '\0'; row_data[i] = data_make( "len", "", DATA_INT, bitbuffer->bits_per_row[i], "data", "", DATA_STRING, row_bytes, NULL); // a simpler representation for csv output row_codes[i] = bitrow_asprint_code(bitbuffer->bb[i], bitbuffer->bits_per_row[i]); } data = data_make( "msg", "", DATA_STRING, msg, "num_rows", "", DATA_INT, bitbuffer->num_rows, "rows", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_DATA, row_data), "codes", "", DATA_ARRAY, data_array(bitbuffer->num_rows, DATA_STRING, row_codes), NULL); decoder_output_data(decoder, data); for (i = 0; i < bitbuffer->num_rows; i++) { free(row_codes[i]); } } */