mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
qapi: make string output visitor parse int list
Signed-off-by: Hu Tao <hutao@cn.fujitsu.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> MST: split up patch
This commit is contained in:
parent
659268ffbf
commit
69e255635d
2 changed files with 255 additions and 12 deletions
|
@ -16,32 +16,181 @@
|
|||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include <math.h>
|
||||
#include "qemu/range.h"
|
||||
|
||||
enum ListMode {
|
||||
LM_NONE, /* not traversing a list of repeated options */
|
||||
LM_STARTED, /* start_list() succeeded */
|
||||
|
||||
LM_IN_PROGRESS, /* next_list() has been called.
|
||||
*
|
||||
* Generating the next list link will consume the most
|
||||
* recently parsed QemuOpt instance of the repeated
|
||||
* option.
|
||||
*
|
||||
* Parsing a value into the list link will examine the
|
||||
* next QemuOpt instance of the repeated option, and
|
||||
* possibly enter LM_SIGNED_INTERVAL or
|
||||
* LM_UNSIGNED_INTERVAL.
|
||||
*/
|
||||
|
||||
LM_SIGNED_INTERVAL, /* next_list() has been called.
|
||||
*
|
||||
* Generating the next list link will consume the most
|
||||
* recently stored element from the signed interval,
|
||||
* parsed from the most recent QemuOpt instance of the
|
||||
* repeated option. This may consume QemuOpt itself
|
||||
* and return to LM_IN_PROGRESS.
|
||||
*
|
||||
* Parsing a value into the list link will store the
|
||||
* next element of the signed interval.
|
||||
*/
|
||||
|
||||
LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
|
||||
|
||||
LM_END
|
||||
};
|
||||
|
||||
typedef enum ListMode ListMode;
|
||||
|
||||
struct StringOutputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
bool human;
|
||||
char *string;
|
||||
GString *string;
|
||||
bool head;
|
||||
ListMode list_mode;
|
||||
union {
|
||||
int64_t s;
|
||||
uint64_t u;
|
||||
} range_start, range_end;
|
||||
GList *ranges;
|
||||
};
|
||||
|
||||
static void string_output_set(StringOutputVisitor *sov, char *string)
|
||||
{
|
||||
g_free(sov->string);
|
||||
sov->string = string;
|
||||
if (sov->string) {
|
||||
g_string_free(sov->string, true);
|
||||
}
|
||||
sov->string = g_string_new(string);
|
||||
g_free(string);
|
||||
}
|
||||
|
||||
static void string_output_append(StringOutputVisitor *sov, int64_t a)
|
||||
{
|
||||
Range *r = g_malloc0(sizeof(*r));
|
||||
r->begin = a;
|
||||
r->end = a + 1;
|
||||
sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
|
||||
}
|
||||
|
||||
static void string_output_append_range(StringOutputVisitor *sov,
|
||||
int64_t s, int64_t e)
|
||||
{
|
||||
Range *r = g_malloc0(sizeof(*r));
|
||||
r->begin = s;
|
||||
r->end = e + 1;
|
||||
sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
|
||||
}
|
||||
|
||||
static void format_string(StringOutputVisitor *sov, Range *r, bool next,
|
||||
bool human)
|
||||
{
|
||||
if (r->end - r->begin > 1) {
|
||||
if (human) {
|
||||
g_string_append_printf(sov->string, "%" PRIx64 "-%" PRIx64,
|
||||
r->begin, r->end - 1);
|
||||
|
||||
} else {
|
||||
g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
|
||||
r->begin, r->end - 1);
|
||||
}
|
||||
} else {
|
||||
if (human) {
|
||||
g_string_append_printf(sov->string, "%" PRIx64, r->begin);
|
||||
} else {
|
||||
g_string_append_printf(sov->string, "%" PRId64, r->begin);
|
||||
}
|
||||
}
|
||||
if (next) {
|
||||
g_string_append(sov->string, ",");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_type_int(Visitor *v, int64_t *obj, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
char *out;
|
||||
GList *l;
|
||||
|
||||
switch (sov->list_mode) {
|
||||
case LM_NONE:
|
||||
string_output_append(sov, *obj);
|
||||
break;
|
||||
|
||||
case LM_STARTED:
|
||||
sov->range_start.s = *obj;
|
||||
sov->range_end.s = *obj;
|
||||
sov->list_mode = LM_IN_PROGRESS;
|
||||
return;
|
||||
|
||||
case LM_IN_PROGRESS:
|
||||
if (sov->range_end.s + 1 == *obj) {
|
||||
sov->range_end.s++;
|
||||
} else {
|
||||
if (sov->range_start.s == sov->range_end.s) {
|
||||
string_output_append(sov, sov->range_end.s);
|
||||
} else {
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
}
|
||||
|
||||
sov->range_start.s = *obj;
|
||||
sov->range_end.s = *obj;
|
||||
}
|
||||
return;
|
||||
|
||||
case LM_END:
|
||||
if (sov->range_end.s + 1 == *obj) {
|
||||
sov->range_end.s++;
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
} else {
|
||||
if (sov->range_start.s == sov->range_end.s) {
|
||||
string_output_append(sov, sov->range_end.s);
|
||||
} else {
|
||||
assert(sov->range_start.s < sov->range_end.s);
|
||||
|
||||
string_output_append_range(sov, sov->range_start.s,
|
||||
sov->range_end.s);
|
||||
}
|
||||
string_output_append(sov, *obj);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
l = sov->ranges;
|
||||
while (l) {
|
||||
Range *r = l->data;
|
||||
format_string(sov, r, l->next != NULL, false);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
if (sov->human) {
|
||||
out = g_strdup_printf("%lld (%#llx)", (long long) *obj, (long long) *obj);
|
||||
} else {
|
||||
out = g_strdup_printf("%lld", (long long) *obj);
|
||||
l = sov->ranges;
|
||||
g_string_append(sov->string, " (");
|
||||
while (l) {
|
||||
Range *r = l->data;
|
||||
format_string(sov, r, l->next != NULL, false);
|
||||
l = l->next;
|
||||
}
|
||||
g_string_append(sov->string, ")");
|
||||
}
|
||||
string_output_set(sov, out);
|
||||
}
|
||||
|
||||
static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
|
||||
|
@ -103,9 +252,61 @@ static void print_type_number(Visitor *v, double *obj, const char *name,
|
|||
string_output_set(sov, g_strdup_printf("%f", *obj));
|
||||
}
|
||||
|
||||
static void
|
||||
start_list(Visitor *v, const char *name, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
|
||||
/* we can't traverse a list in a list */
|
||||
assert(sov->list_mode == LM_NONE);
|
||||
sov->list_mode = LM_STARTED;
|
||||
sov->head = true;
|
||||
}
|
||||
|
||||
static GenericList *
|
||||
next_list(Visitor *v, GenericList **list, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
GenericList *ret = NULL;
|
||||
if (*list) {
|
||||
if (sov->head) {
|
||||
ret = *list;
|
||||
} else {
|
||||
ret = (*list)->next;
|
||||
}
|
||||
|
||||
if (sov->head) {
|
||||
if (ret && ret->next == NULL) {
|
||||
sov->list_mode = LM_NONE;
|
||||
}
|
||||
sov->head = false;
|
||||
} else {
|
||||
if (ret && ret->next == NULL) {
|
||||
sov->list_mode = LM_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
end_list(Visitor *v, Error **errp)
|
||||
{
|
||||
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
|
||||
|
||||
assert(sov->list_mode == LM_STARTED ||
|
||||
sov->list_mode == LM_END ||
|
||||
sov->list_mode == LM_NONE ||
|
||||
sov->list_mode == LM_IN_PROGRESS);
|
||||
sov->list_mode = LM_NONE;
|
||||
sov->head = true;
|
||||
|
||||
}
|
||||
|
||||
char *string_output_get_string(StringOutputVisitor *sov)
|
||||
{
|
||||
char *string = sov->string;
|
||||
char *string = g_string_free(sov->string, false);
|
||||
sov->string = NULL;
|
||||
return string;
|
||||
}
|
||||
|
@ -117,7 +318,11 @@ Visitor *string_output_get_visitor(StringOutputVisitor *sov)
|
|||
|
||||
void string_output_visitor_cleanup(StringOutputVisitor *sov)
|
||||
{
|
||||
g_free(sov->string);
|
||||
if (sov->string) {
|
||||
g_string_free(sov->string, true);
|
||||
}
|
||||
|
||||
g_list_free_full(sov->ranges, g_free);
|
||||
g_free(sov);
|
||||
}
|
||||
|
||||
|
@ -127,6 +332,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
|
|||
|
||||
v = g_malloc0(sizeof(*v));
|
||||
|
||||
v->string = g_string_new(NULL);
|
||||
v->human = human;
|
||||
v->visitor.type_enum = output_type_enum;
|
||||
v->visitor.type_int = print_type_int;
|
||||
|
@ -134,6 +340,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)
|
|||
v->visitor.type_bool = print_type_bool;
|
||||
v->visitor.type_str = print_type_str;
|
||||
v->visitor.type_number = print_type_number;
|
||||
v->visitor.start_list = start_list;
|
||||
v->visitor.next_list = next_list;
|
||||
v->visitor.end_list = end_list;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data,
|
|||
static void test_visitor_out_int(TestOutputVisitorData *data,
|
||||
const void *unused)
|
||||
{
|
||||
int64_t value = -42;
|
||||
int64_t value = 42;
|
||||
Error *err = NULL;
|
||||
char *str;
|
||||
|
||||
|
@ -53,10 +53,42 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
|
|||
|
||||
str = string_output_get_string(data->sov);
|
||||
g_assert(str != NULL);
|
||||
g_assert_cmpstr(str, ==, "-42");
|
||||
g_assert_cmpstr(str, ==, "42");
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void test_visitor_out_intList(TestOutputVisitorData *data,
|
||||
const void *unused)
|
||||
{
|
||||
int64_t value[] = {0, 1, 9, 10, 16, 15, 14,
|
||||
3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX};
|
||||
intList *list = NULL, **tmp = &list;
|
||||
int i;
|
||||
Error *errp = NULL;
|
||||
char *str;
|
||||
|
||||
for (i = 0; i < sizeof(value) / sizeof(value[0]); i++) {
|
||||
*tmp = g_malloc0(sizeof(**tmp));
|
||||
(*tmp)->value = value[i];
|
||||
tmp = &(*tmp)->next;
|
||||
}
|
||||
|
||||
visit_type_intList(data->ov, &list, NULL, &errp);
|
||||
g_assert(errp == NULL);
|
||||
|
||||
str = string_output_get_string(data->sov);
|
||||
g_assert(str != NULL);
|
||||
g_assert_cmpstr(str, ==,
|
||||
"0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807");
|
||||
g_free(str);
|
||||
while (list) {
|
||||
intList *tmp2;
|
||||
tmp2 = list->next;
|
||||
g_free(list);
|
||||
list = tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_visitor_out_bool(TestOutputVisitorData *data,
|
||||
const void *unused)
|
||||
{
|
||||
|
@ -182,6 +214,8 @@ int main(int argc, char **argv)
|
|||
&out_visitor_data, test_visitor_out_enum);
|
||||
output_visitor_test_add("/string-visitor/output/enum-errors",
|
||||
&out_visitor_data, test_visitor_out_enum_errors);
|
||||
output_visitor_test_add("/string-visitor/output/intList",
|
||||
&out_visitor_data, test_visitor_out_intList);
|
||||
|
||||
g_test_run();
|
||||
|
||||
|
|
Loading…
Reference in a new issue