]> sigrok.org Git - libsigrok.git/blobdiff - src/input/csv.c
input/csv: rearrange text to logic data conversion and datafeed
[libsigrok.git] / src / input / csv.c
index b1645e73d9d69a4e01b44f6b615032714fbc966f..c53ca426aa8863301638c6ebf5da086d5f5ee3a6 100644 (file)
@@ -174,6 +174,90 @@ struct context {
        GSList *prev_sr_channels;
 };
 
+/*
+ * Primitive operations to handle sample sets:
+ * - Keep a buffer for datafeed submission, capable of holding many
+ *   samples (reduces call overhead, improves throughput).
+ * - Have a "current sample set" pointer reference one position in that
+ *   large samples buffer.
+ * - Clear the current sample set before text line inspection, then set
+ *   the bits which are found active in the current line of text input.
+ *   Phrase the API such that call sites can be kept simple. Advance to
+ *   the next sample set between lines, flush the larger buffer as needed
+ *   (when it is full, or upon EOF).
+ */
+
+static void clear_logic_samples(struct context *inc)
+{
+       inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
+       memset(inc->sample_buffer, 0, inc->sample_unit_size);
+}
+
+static void set_logic_level(struct context *inc, size_t ch_idx, int on)
+{
+       size_t byte_idx, bit_idx;
+       uint8_t bit_mask;
+
+       if (ch_idx >= inc->num_channels)
+               return;
+       if (!on)
+               return;
+
+       byte_idx = ch_idx / 8;
+       bit_idx = ch_idx % 8;
+       bit_mask = 1 << bit_idx;
+       inc->sample_buffer[byte_idx] |= bit_mask;
+}
+
+static int flush_logic_samples(const struct sr_input *in)
+{
+       struct context *inc;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+       int rc;
+
+       inc = in->priv;
+       if (!inc->datafeed_buf_fill)
+               return SR_OK;
+
+       memset(&packet, 0, sizeof(packet));
+       memset(&logic, 0, sizeof(logic));
+       packet.type = SR_DF_LOGIC;
+       packet.payload = &logic;
+       logic.unitsize = inc->sample_unit_size;
+       logic.length = inc->datafeed_buf_fill;
+       logic.data = inc->datafeed_buffer;
+
+       rc = sr_session_send(in->sdi, &packet);
+       if (rc != SR_OK)
+               return rc;
+
+       inc->datafeed_buf_fill = 0;
+       return SR_OK;
+}
+
+static int queue_logic_samples(const struct sr_input *in)
+{
+       struct context *inc;
+       int rc;
+
+       inc = in->priv;
+
+       inc->datafeed_buf_fill += inc->sample_unit_size;
+       if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
+               rc = flush_logic_samples(in);
+               if (rc != SR_OK)
+                       return rc;
+       }
+       return SR_OK;
+}
+
+/*
+ * Primitive operations for text input: Strip comments off text lines.
+ * Split text lines into columns. Process input text for individual
+ * columns.
+ */
+
 static void strip_comment(char *buf, const GString *prefix)
 {
        char *ptr;
@@ -187,28 +271,39 @@ static void strip_comment(char *buf, const GString *prefix)
        }
 }
 
+/* TODO Move parse_line() here. */
+
+/**
+ * @brief Parse a text field into multiple bits, binary presentation.
+ *
+ * @param[in] str      The input text, a run of 0/1 digits.
+ * @param[in] inc      The input module's context.
+ *
+ * @retval SR_OK       Success.
+ * @retval SR_ERR      Invalid input data (empty, or format error).
+ *
+ * This routine modifies the logic levels in the current sample set,
+ * based on the text input which consists of binary digits.
+ */
 static int parse_binstr(const char *str, struct context *inc)
 {
        gsize i, j, length;
+       char c;
 
        length = strlen(str);
-
        if (!length) {
                sr_err("Column %zu in line %zu is empty.", inc->single_column,
                        inc->line_number);
                return SR_ERR;
        }
 
-       /* Clear buffer in order to set bits only. */
-       memset(inc->sample_buffer, 0, inc->sample_unit_size);
-
        i = inc->first_channel;
-
        for (j = 0; i < length && j < inc->num_channels; i++, j++) {
-               if (str[length - i - 1] == '1') {
-                       inc->sample_buffer[j / 8] |= (1 << (j % 8));
-               } else if (str[length - i - 1] != '0') {
-                       sr_err("Invalid value '%s' in column %zu in line %zu.",
+               c = str[length - i - 1];
+               if (c == '1') {
+                       set_logic_level(inc, j, 1);
+               } else if (c != '0') {
+                       sr_err("Invalid text '%s' in binary column %zu in line %zu.",
                                str, inc->single_column, inc->line_number);
                        return SR_ERR;
                }
@@ -217,6 +312,18 @@ static int parse_binstr(const char *str, struct context *inc)
        return SR_OK;
 }
 
+/**
+ * @brief Parse a text field into multiple bits, hexadecimal presentation.
+ *
+ * @param[in] str      The input text, a run of hex digits.
+ * @param[in] inc      The input module's context.
+ *
+ * @retval SR_OK       Success.
+ * @retval SR_ERR      Invalid input data (empty, or format error).
+ *
+ * This routine modifies the logic levels in the current sample set,
+ * based on the text input which consists of hexadecimal digits.
+ */
 static int parse_hexstr(const char *str, struct context *inc)
 {
        gsize i, j, k, length;
@@ -224,43 +331,44 @@ static int parse_hexstr(const char *str, struct context *inc)
        char c;
 
        length = strlen(str);
-
        if (!length) {
                sr_err("Column %zu in line %zu is empty.", inc->single_column,
                        inc->line_number);
                return SR_ERR;
        }
 
-       /* Clear buffer in order to set bits only. */
-       memset(inc->sample_buffer, 0, inc->sample_unit_size);
-
        /* Calculate the position of the first hexadecimal digit. */
        i = inc->first_channel / 4;
-
        for (j = 0; i < length && j < inc->num_channels; i++) {
                c = str[length - i - 1];
-
                if (!g_ascii_isxdigit(c)) {
-                       sr_err("Invalid value '%s' in column %zu in line %zu.",
+                       sr_err("Invalid text '%s' in hex column %zu in line %zu.",
                                str, inc->single_column, inc->line_number);
                        return SR_ERR;
                }
 
                value = g_ascii_xdigit_value(c);
-
                k = (inc->first_channel + j) % 4;
-
-               for (; j < inc->num_channels && k < 4; k++) {
-                       if (value & (1 << k))
-                               inc->sample_buffer[j / 8] |= (1 << (j % 8));
-
-                       j++;
+               for (; j < inc->num_channels && k < 4; j++, k++) {
+                       set_logic_level(inc, j, value & (1 << k));
                }
        }
 
        return SR_OK;
 }
 
+/**
+ * @brief Parse a text field into multiple bits, octal presentation.
+ *
+ * @param[in] str      The input text, a run of oct digits.
+ * @param[in] inc      The input module's context.
+ *
+ * @retval SR_OK       Success.
+ * @retval SR_ERR      Invalid input data (empty, or format error).
+ *
+ * This routine modifies the logic levels in the current sample set,
+ * based on the text input which consists of octal digits.
+ */
 static int parse_octstr(const char *str, struct context *inc)
 {
        gsize i, j, k, length;
@@ -268,43 +376,46 @@ static int parse_octstr(const char *str, struct context *inc)
        char c;
 
        length = strlen(str);
-
        if (!length) {
                sr_err("Column %zu in line %zu is empty.", inc->single_column,
                        inc->line_number);
                return SR_ERR;
        }
 
-       /* Clear buffer in order to set bits only. */
-       memset(inc->sample_buffer, 0, inc->sample_unit_size);
-
        /* Calculate the position of the first octal digit. */
        i = inc->first_channel / 3;
-
        for (j = 0; i < length && j < inc->num_channels; i++) {
                c = str[length - i - 1];
-
                if (c < '0' || c > '7') {
-                       sr_err("Invalid value '%s' in column %zu in line %zu.",
+                       sr_err("Invalid text '%s' in oct column %zu in line %zu.",
                                str, inc->single_column, inc->line_number);
                        return SR_ERR;
                }
 
                value = g_ascii_xdigit_value(c);
-
                k = (inc->first_channel + j) % 3;
-
-               for (; j < inc->num_channels && k < 3; k++) {
-                       if (value & (1 << k))
-                               inc->sample_buffer[j / 8] |= (1 << (j % 8));
-
-                       j++;
+               for (; j < inc->num_channels && k < 3; j++, k++) {
+                       set_logic_level(inc, j, value & (1 << k));
                }
        }
 
        return SR_OK;
 }
 
+static int parse_single_column(const char *column, struct context *inc)
+{
+       switch (inc->format) {
+       case FORMAT_BIN:
+               return parse_binstr(column, inc);
+       case FORMAT_HEX:
+               return parse_hexstr(column, inc);
+       case FORMAT_OCT:
+               return parse_octstr(column, inc);
+       }
+
+       return SR_ERR;
+}
+
 /**
  * @brief Splits a text line into a set of columns.
  *
@@ -313,6 +424,12 @@ static int parse_octstr(const char *str, struct context *inc)
  * @param[in] max_cols The maximum column count, negative to get all of them.
  *
  * @returns An array of strings, representing the columns' text.
+ *
+ * This routine splits a text line on previously determined separators.
+ * A previously determined set of columns gets isolated (starting at a
+ * first position and spanning a given number of columns). A negative
+ * value for the maximum number of columns results in no restriction on
+ * the result set's length (the first columns still get trimmed off).
  */
 static char **parse_line(char *buf, struct context *inc, ssize_t max_cols)
 {
@@ -320,66 +437,73 @@ static char **parse_line(char *buf, struct context *inc, ssize_t max_cols)
        GSList *list, *l;
        char **columns;
        char *column;
-       gsize n, k;
+       gsize seen, taken;
 
-       n = 0;
-       k = 0;
+       seen = 0;
+       taken = 0;
        list = NULL;
 
        remainder = buf;
        str = strstr(remainder, inc->delimiter->str);
-
        while (str && max_cols) {
-               if (n >= inc->first_column) {
+               if (seen >= inc->first_column) {
                        column = g_strndup(remainder, str - remainder);
                        list = g_slist_prepend(list, g_strstrip(column));
 
                        max_cols--;
-                       k++;
+                       taken++;
                }
 
                remainder = str + inc->delimiter->len;
                str = strstr(remainder, inc->delimiter->str);
-               n++;
+               seen++;
        }
 
-       if (buf[0] && max_cols && n >= inc->first_column) {
+       if (buf[0] && max_cols && seen >= inc->first_column) {
                column = g_strdup(remainder);
                list = g_slist_prepend(list, g_strstrip(column));
-               k++;
+               taken++;
        }
 
-       if (!(columns = g_try_new(char *, k + 1)))
+       if (!(columns = g_try_new(char *, taken + 1)))
                return NULL;
-
-       columns[k--] = NULL;
-
+       columns[taken--] = NULL;
        for (l = list; l; l = l->next)
-               columns[k--] = l->data;
+               columns[taken--] = l->data;
 
        g_slist_free(list);
 
        return columns;
 }
 
+/**
+ * @brief Picks logic levels from multiple binary colomns, one channel per column.
+ *
+ * @param[in] columns  The text fields which are kept in the columns.
+ * @param[in] inc      The input module's context.
+ *
+ * @retval SR_OK       Success.
+ * @retval SR_ERR      Insufficient input, or syntax errors.
+ *
+ * This routine exclusively handles binary input where one logic channel
+ * occupies one column each. All channels are expected to reside in one
+ * consequtive run of columns.
+ */
 static int parse_multi_columns(char **columns, struct context *inc)
 {
        gsize i;
        char *column;
 
-       /* Clear buffer in order to set bits only. */
-       memset(inc->sample_buffer, 0, inc->sample_unit_size);
-
        for (i = 0; i < inc->num_channels; i++) {
                column = columns[i];
-               if (column[0] == '1') {
-                       inc->sample_buffer[i / 8] |= (1 << (i % 8));
+               if (strcmp(column, "1") == 0) {
+                       set_logic_level(inc, i, 1);
                } else if (!strlen(column)) {
                        sr_err("Column %zu in line %zu is empty.",
                                inc->first_channel + i, inc->line_number);
                        return SR_ERR;
-               } else if (column[0] != '0') {
-                       sr_err("Invalid value '%s' in column %zu in line %zu.",
+               } else if (strcmp(column, "0") != 0) {
+                       sr_err("Invalid text '%s' in bit column %zu in line %zu.",
                                column, inc->first_channel + i,
                                inc->line_number);
                        return SR_ERR;
@@ -389,71 +513,6 @@ static int parse_multi_columns(char **columns, struct context *inc)
        return SR_OK;
 }
 
-static int parse_single_column(const char *column, struct context *inc)
-{
-       int res;
-
-       res = SR_ERR;
-
-       switch (inc->format) {
-       case FORMAT_BIN:
-               res = parse_binstr(column, inc);
-               break;
-       case FORMAT_HEX:
-               res = parse_hexstr(column, inc);
-               break;
-       case FORMAT_OCT:
-               res = parse_octstr(column, inc);
-               break;
-       }
-
-       return res;
-}
-
-static int flush_samples(const struct sr_input *in)
-{
-       struct context *inc;
-       struct sr_datafeed_packet packet;
-       struct sr_datafeed_logic logic;
-       int rc;
-
-       inc = in->priv;
-       if (!inc->datafeed_buf_fill)
-               return SR_OK;
-
-       memset(&packet, 0, sizeof(packet));
-       memset(&logic, 0, sizeof(logic));
-       packet.type = SR_DF_LOGIC;
-       packet.payload = &logic;
-       logic.unitsize = inc->sample_unit_size;
-       logic.length = inc->datafeed_buf_fill;
-       logic.data = inc->datafeed_buffer;
-
-       rc = sr_session_send(in->sdi, &packet);
-       if (rc != SR_OK)
-               return rc;
-
-       inc->datafeed_buf_fill = 0;
-       return SR_OK;
-}
-
-static int queue_samples(const struct sr_input *in)
-{
-       struct context *inc;
-       int rc;
-
-       inc = in->priv;
-
-       inc->datafeed_buf_fill += inc->sample_unit_size;
-       if (inc->datafeed_buf_fill == inc->datafeed_buf_size) {
-               rc = flush_samples(in);
-               if (rc != SR_OK)
-                       return rc;
-       }
-       inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
-       return SR_OK;
-}
-
 static int init(struct sr_input *in, GHashTable *options)
 {
        struct context *inc;
@@ -678,15 +737,14 @@ static int initial_parse(const struct sr_input *in, GString *buf)
         * Calculate the minimum buffer size to store the set of samples
         * of all channels (unit size). Determine a larger buffer size
         * for datafeed submission that is a multiple of the unit size.
-        * Allocate the larger buffer, and have the "sample buffer" point
-        * to a location within that large buffer.
+        * Allocate the larger buffer, the "sample buffer" will point
+        * to a location within that large buffer later.
         */
        inc->sample_unit_size = (inc->num_channels + 7) / 8;
        inc->datafeed_buf_size = CHUNK_SIZE;
        inc->datafeed_buf_size *= inc->sample_unit_size;
        inc->datafeed_buffer = g_malloc(inc->datafeed_buf_size);
        inc->datafeed_buf_fill = 0;
-       inc->sample_buffer = &inc->datafeed_buffer[inc->datafeed_buf_fill];
 
 out:
        if (columns)
@@ -866,6 +924,8 @@ static int process_buffer(struct sr_input *in, gboolean is_eof)
                        return SR_ERR;
                }
 
+               clear_logic_samples(inc);
+
                if (inc->multi_column_mode)
                        ret = parse_multi_columns(columns, inc);
                else
@@ -876,8 +936,8 @@ static int process_buffer(struct sr_input *in, gboolean is_eof)
                        return SR_ERR;
                }
 
-               /* Send sample data to the session bus. */
-               ret = queue_samples(in);
+               /* Send sample data to the session bus (buffered). */
+               ret = queue_logic_samples(in);
                if (ret != SR_OK) {
                        sr_err("Sending samples failed.");
                        g_strfreev(columns);
@@ -931,7 +991,7 @@ static int end(struct sr_input *in)
        if (ret != SR_OK)
                return ret;
 
-       ret = flush_samples(in);
+       ret = flush_logic_samples(in);
        if (ret != SR_OK)
                return ret;