]> sigrok.org Git - libsigrok.git/blobdiff - src/strutil.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / strutil.c
index be07662f3ec03c95eb8e0afc8b0695ba8afcc82e..2136bd4936c2fde5bf303bbf491e938af5d4acb1 100644 (file)
@@ -1667,4 +1667,209 @@ SR_API void sr_free_probe_names(char **names)
        g_strfreev(names);
 }
 
+/**
+ * Trim leading and trailing whitespace off text.
+ *
+ * @param[in] s The input text.
+ *
+ * @return Start of trimmed input text.
+ *
+ * Manipulates the caller's input text in place.
+ *
+ * @since 0.6.0
+ */
+SR_API char *sr_text_trim_spaces(char *s)
+{
+       char *p;
+
+       if (!s || !*s)
+               return s;
+
+       p = s + strlen(s);
+       while (p > s && isspace((int)p[-1]))
+               *(--p) = '\0';
+       while (isspace((int)*s))
+               s++;
+
+       return s;
+}
+
+/**
+ * Check for another complete text line, trim, return consumed char count.
+ *
+ * @param[in] s The input text, current read position.
+ * @param[in] l The input text, remaining available characters.
+ * @param[out] next Position after the current text line.
+ * @param[out] taken Count of consumed chars in current text line.
+ *
+ * @return Start of trimmed and NUL terminated text line.
+ *   Or #NULL when no text line was found.
+ *
+ * Checks for the availability of another text line of input data.
+ * Manipulates the caller's input text in place.
+ *
+ * The end-of-line condition is the LF character ('\n'). Which covers
+ * LF-only as well as CR/LF input data. CR-only and LF/CR are considered
+ * unpopular and are not supported. LF/CR may appear to work at the
+ * caller's when leading whitespace gets trimmed (line boundaries will
+ * be incorrect, but content may get processed as expected). Support for
+ * all of the above combinations breaks the detection of empty lines (or
+ * becomes unmaintainably complex).
+ *
+ * The input buffer must be end-of-line terminated, lack of EOL results
+ * in failure to detect the text line. This is motivated by accumulating
+ * input in chunks, and the desire to not process incomplete lines before
+ * their reception has completed. Callers should enforce EOL if their
+ * source of input provides an EOF condition and is unreliable in terms
+ * of text line termination.
+ *
+ * When another text line is available, it gets NUL terminated and
+ * space gets trimmed of both ends. The start position of the trimmed
+ * text line is returned. Optionally the number of consumed characters
+ * is returned to the caller. Optionally 'next' points to after the
+ * returned text line, or #NULL when no other text is available in the
+ * input buffer.
+ *
+ * The 'taken' value is not preset by this routine, only gets updated.
+ * This is convenient for callers which expect to find multiple text
+ * lines in a received chunk, before finally discarding processed data
+ * from the input buffer (which can involve expensive memory move
+ * operations, and may be desirable to defer as much as possible).
+ *
+ * @since 0.6.0
+ */
+SR_API char *sr_text_next_line(char *s, size_t l, char **next, size_t *taken)
+{
+       char *p;
+
+       if (next)
+               *next = NULL;
+       if (!l)
+               l = strlen(s);
+
+       /* Immediate reject incomplete input data. */
+       if (!s || !*s || !l)
+               return NULL;
+
+       /* Search for the next line termination. NUL terminate. */
+       p = g_strstr_len(s, l, "\n");
+       if (!p)
+               return NULL;
+       *p++ = '\0';
+       if (taken)
+               *taken += p - s;
+       l -= p - s;
+       if (next)
+               *next = l ? p : NULL;
+
+       /* Trim NUL terminated text line at both ends. */
+       s = sr_text_trim_spaces(s);
+       return s;
+}
+
+/**
+ * Isolates another space separated word in a text line.
+ *
+ * @param[in] s The input text, current read position.
+ * @param[out] next The position after the current word.
+ *
+ * @return The start of the current word. Or #NULL if there is none.
+ *
+ * Advances over leading whitespace. Isolates (NUL terminates) the next
+ * whitespace separated word. Optionally returns the position after the
+ * current word. Manipulates the caller's input text in place.
+ *
+ * @since 0.6.0
+ */
+SR_API char *sr_text_next_word(char *s, char **next)
+{
+       char *word, *p;
+
+       word = s;
+       if (next)
+               *next = NULL;
+
+       /* Immediately reject incomplete input data. */
+       if (!word || !*word)
+               return NULL;
+
+       /* Advance over optional leading whitespace. */
+       while (isspace((int)*word))
+               word++;
+       if (!*word)
+               return NULL;
+
+       /*
+        * Advance until whitespace or end of text. Quick return when
+        * end of input is seen. Otherwise advance over whitespace and
+        * return the position of trailing text.
+        */
+       p = word;
+       while (*p && !isspace((int)*p))
+               p++;
+       if (!*p)
+               return word;
+       *p++ = '\0';
+       while (isspace((int)*p))
+               p++;
+       if (!*p)
+               return word;
+       if (next)
+               *next = p;
+       return word;
+}
+
+/**
+ * Get the number of necessary bits to hold a given value. Also gets
+ * the next power-of-two value at or above the caller provided value.
+ *
+ * @param[in] value The value that must get stored.
+ * @param[out] bits The required number of bits.
+ * @param[out] power The corresponding power-of-two.
+ *
+ * @return SR_OK upon success, SR_ERR* otherwise.
+ *
+ * TODO Move this routine to a more appropriate location, it is not
+ * strictly string related.
+ *
+ * @since 0.6.0
+ */
+SR_API int sr_next_power_of_two(size_t value, size_t *bits, size_t *power)
+{
+       size_t need_bits;
+       size_t check_mask;
+
+       if (bits)
+               *bits = 0;
+       if (power)
+               *power = 0;
+
+       /*
+        * Handle the special case of input value 0 (needs 1 bit
+        * and results in "power of two" value 1) here. It is not
+        * covered by the generic logic below.
+        */
+       if (!value) {
+               if (bits)
+                       *bits = 1;
+               if (power)
+                       *power = 1;
+               return SR_OK;
+       }
+
+       need_bits = 0;
+       check_mask = 0;
+       do {
+               need_bits++;
+               check_mask <<= 1;
+               check_mask |= 1UL << 0;
+       } while (value & ~check_mask);
+
+       if (bits)
+               *bits = need_bits;
+       if (power)
+               *power = ++check_mask;
+       return SR_OK;
+}
+
 /** @} */