Commit 659268ff authored by Hu Tao's avatar Hu Tao Committed by Michael S. Tsirkin
Browse files

qapi: make string input visitor parse int list



Signed-off-by: default avatarHu Tao <hutao@cn.fujitsu.com>
Acked-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Tested-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>

MST: split up patch
parent cac124d1
Loading
Loading
Loading
Loading
+187 −8
Original line number Diff line number Diff line
@@ -15,31 +15,205 @@
#include "qapi/visitor-impl.h"
#include "qapi/qmp/qerror.h"
#include "qemu/option.h"
#include "qemu/queue.h"
#include "qemu/range.h"


struct StringInputVisitor
{
    Visitor visitor;

    bool head;

    GList *ranges;
    GList *cur_range;
    int64_t cur;

    const char *string;
};

static void parse_str(StringInputVisitor *siv, Error **errp)
{
    char *str = (char *) siv->string;
    long long start, end;
    Range *cur;
    char *endptr;

    if (siv->ranges) {
        return;
    }

    errno = 0;
    do {
        start = strtoll(str, &endptr, 0);
        if (errno == 0 && endptr > str && INT64_MIN <= start &&
            start <= INT64_MAX) {
            if (*endptr == '\0') {
                cur = g_malloc0(sizeof(*cur));
                cur->begin = start;
                cur->end = start + 1;
                siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
                                                          range_compare);
                cur = NULL;
                str = NULL;
            } else if (*endptr == '-') {
                str = endptr + 1;
                end = strtoll(str, &endptr, 0);
                if (errno == 0 && endptr > str &&
                    INT64_MIN <= end && end <= INT64_MAX && start <= end &&
                    (start > INT64_MAX - 65536 ||
                     end < start + 65536)) {
                    if (*endptr == '\0') {
                        cur = g_malloc0(sizeof(*cur));
                        cur->begin = start;
                        cur->end = end + 1;
                        siv->ranges =
                            g_list_insert_sorted_merged(siv->ranges,
                                                        cur,
                                                        range_compare);
                        cur = NULL;
                        str = NULL;
                    } else if (*endptr == ',') {
                        str = endptr + 1;
                        cur = g_malloc0(sizeof(*cur));
                        cur->begin = start;
                        cur->end = end + 1;
                        siv->ranges =
                            g_list_insert_sorted_merged(siv->ranges,
                                                        cur,
                                                        range_compare);
                        cur = NULL;
                    } else {
                        goto error;
                    }
                } else {
                    goto error;
                }
            } else if (*endptr == ',') {
                str = endptr + 1;
                cur = g_malloc0(sizeof(*cur));
                cur->begin = start;
                cur->end = start + 1;
                siv->ranges = g_list_insert_sorted_merged(siv->ranges,
                                                          cur,
                                                          range_compare);
                cur = NULL;
            } else {
                goto error;
            }
        } else {
            goto error;
        }
    } while (str);

    return;
error:
    g_list_free_full(siv->ranges, g_free);
    assert(siv->ranges == NULL);
}

static void
start_list(Visitor *v, const char *name, Error **errp)
{
    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);

    parse_str(siv, errp);

    siv->cur_range = g_list_first(siv->ranges);
    if (siv->cur_range) {
        Range *r = siv->cur_range->data;
        if (r) {
            siv->cur = r->begin;
        }
    }
}

static GenericList *
next_list(Visitor *v, GenericList **list, Error **errp)
{
    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
    GenericList **link;
    Range *r;

    if (!siv->ranges || !siv->cur_range) {
        return NULL;
    }

    r = siv->cur_range->data;
    if (!r) {
        return NULL;
    }

    if (siv->cur < r->begin || siv->cur >= r->end) {
        siv->cur_range = g_list_next(siv->cur_range);
        if (!siv->cur_range) {
            return NULL;
        }
        r = siv->cur_range->data;
        if (!r) {
            return NULL;
        }
        siv->cur = r->begin;
    }

    if (siv->head) {
        link = list;
        siv->head = false;
    } else {
        link = &(*list)->next;
    }

    *link = g_malloc0(sizeof **link);
    return *link;
}

static void
end_list(Visitor *v, Error **errp)
{
    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
    siv->head = true;
}

static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
                           Error **errp)
{
    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
    char *endp = (char *) siv->string;
    long long val;

    errno = 0;
    if (siv->string) {
        val = strtoll(siv->string, &endp, 0);
    }
    if (!siv->string || errno || endp == siv->string || *endp) {
    if (!siv->string) {
        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                  "integer");
        return;
    }

    *obj = val;
    parse_str(siv, errp);

    if (!siv->ranges) {
        goto error;
    }

    if (!siv->cur_range) {
        Range *r;

        siv->cur_range = g_list_first(siv->ranges);
        if (!siv->cur_range) {
            goto error;
        }

        r = siv->cur_range->data;
        if (!r) {
            goto error;
        }

        siv->cur = r->begin;
    }

    *obj = siv->cur;
    siv->cur++;
    return;

error:
    error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
              "an int64 value or range");
}

static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
@@ -140,6 +314,7 @@ Visitor *string_input_get_visitor(StringInputVisitor *v)

void string_input_visitor_cleanup(StringInputVisitor *v)
{
    g_list_free_full(v->ranges, g_free);
    g_free(v);
}

@@ -155,8 +330,12 @@ StringInputVisitor *string_input_visitor_new(const char *str)
    v->visitor.type_bool = parse_type_bool;
    v->visitor.type_str = parse_type_str;
    v->visitor.type_number = parse_type_number;
    v->visitor.start_list = start_list;
    v->visitor.next_list = next_list;
    v->visitor.end_list = end_list;
    v->visitor.optional = parse_optional;

    v->string = str;
    v->head = true;
    return v;
}
+36 −0
Original line number Diff line number Diff line
@@ -64,6 +64,35 @@ static void test_visitor_in_int(TestInputVisitorData *data,
    g_assert_cmpint(res, ==, value);
}

static void test_visitor_in_intList(TestInputVisitorData *data,
                                    const void *unused)
{
    int64_t value[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20};
    int16List *res = NULL, *tmp;
    Error *errp = NULL;
    Visitor *v;
    int i = 0;

    v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");

    visit_type_int16List(v, &res, NULL, &errp);
    g_assert(errp == NULL);
    tmp = res;
    while (i < sizeof(value) / sizeof(value[0])) {
        g_assert(tmp);
        g_assert_cmpint(tmp->value, ==, value[i++]);
        tmp = tmp->next;
    }
    g_assert(!tmp);

    tmp = res;
    while (tmp) {
        res = res->next;
        g_free(tmp);
        tmp = res;
    }
}

static void test_visitor_in_bool(TestInputVisitorData *data,
                                 const void *unused)
{
@@ -170,6 +199,7 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
                                 const void *unused)
{
    int64_t ires;
    intList *ilres;
    bool bres;
    double nres;
    char *sres;
@@ -195,6 +225,10 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
        visit_type_int(v, &ires, NULL, NULL);
        visitor_input_teardown(data, NULL);

        v = visitor_input_test_init(data, buf);
        visit_type_intList(v, &ilres, NULL, NULL);
        visitor_input_teardown(data, NULL);

        v = visitor_input_test_init(data, buf);
        visit_type_bool(v, &bres, NULL, NULL);
        visitor_input_teardown(data, NULL);
@@ -231,6 +265,8 @@ int main(int argc, char **argv)

    input_visitor_test_add("/string-visitor/input/int",
                           &in_visitor_data, test_visitor_in_int);
    input_visitor_test_add("/string-visitor/input/intList",
                           &in_visitor_data, test_visitor_in_intList);
    input_visitor_test_add("/string-visitor/input/bool",
                           &in_visitor_data, test_visitor_in_bool);
    input_visitor_test_add("/string-visitor/input/number",