+/*
+ * Compile regex in `pattern`, and try to match `string`. If there's a match,
+ * return true and set `*match_info` to the list of matches. The list of
+ * matches must be freed by the caller. If there's no match, return false and
+ * set `*match_info` to NULL;
+ */
+static
+bool compile_and_match(const char *pattern, const char *string, GMatchInfo **match_info) {
+ bool matches = false;
+ GError *regex_error = NULL;
+ GRegex *regex;
+
+ regex = g_regex_new(pattern, 0, 0, ®ex_error);
+ if (!regex) {
+ goto end;
+ }
+
+ matches = g_regex_match(regex, string, 0, match_info);
+ if (!matches) {
+ /*
+ * g_regex_match allocates `*match_info` even if it returns
+ * FALSE. If there's no match, we have no use for it, so free
+ * it immediatly and don't return it to the caller.
+ */
+ g_match_info_unref(*match_info);
+ *match_info = NULL;
+ }
+
+ g_regex_unref(regex);
+
+end:
+
+ if (regex_error) {
+ g_error_free(regex_error);
+ }
+
+ return matches;
+}
+
+/*
+ * Convert the captured text in match number `match_num` in `match_info`
+ * to an unsigned integer.
+ */
+static
+guint64 match_to_uint(const GMatchInfo *match_info, gint match_num) {
+ gchar *text, *endptr;
+ guint64 result;
+
+ text = g_match_info_fetch(match_info, match_num);
+ BT_ASSERT(text);
+
+ /*
+ * Because the input is carefully sanitized with regexes by the caller,
+ * we assume that g_ascii_strtoull cannot fail.
+ */
+ errno = 0;
+ result = g_ascii_strtoull(text, &endptr, 10);
+ BT_ASSERT(endptr > text);
+ BT_ASSERT(errno == 0);
+
+ g_free(text);
+
+ return result;
+}
+
+/*
+ * When parsing the nanoseconds part, .512 means .512000000, not .000000512.
+ * This function is like match_to_uint, but multiplies the parsed number to get
+ * the expected result.
+ */
+static
+guint64 match_to_uint_ns(const GMatchInfo *match_info, gint match_num) {
+ guint64 nanoseconds;
+ gboolean ret;
+ gint start_pos, end_pos, power;
+ static int pow10[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ };
+
+ nanoseconds = match_to_uint(match_info, match_num);
+
+ /* Multiply by 10 as many times as there are omitted digits. */
+ ret = g_match_info_fetch_pos(match_info, match_num, &start_pos, &end_pos);
+ BT_ASSERT(ret);
+
+ power = 9 - (end_pos - start_pos);
+ BT_ASSERT(power >= 0 && power <= 8);
+
+ nanoseconds *= pow10[power];
+
+ return nanoseconds;
+}
+