From 8b2a0c4ececbd40ef2ac5dcfac7487135dcb3918 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 25 Feb 2018 00:39:36 -0500 Subject: [PATCH] range: Eliminate direct Range member access Users of struct Range mess liberally with its members, which makes refactoring hard. Create a set of methods, and convert all users to call them instead of accessing members. The methods have carefully worded contracts, and use assertions to check them. Backports commit a0efbf16604770b9d805bcf210ec29942321134f from qemu --- qemu/include/qemu/range.h | 87 +++++++++++++++++++++++++++++++- qemu/qapi/string-input-visitor.c | 20 +++----- qemu/util/range.c | 3 +- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/qemu/include/qemu/range.h b/qemu/include/qemu/range.h index 6add664e..1dd1b0e0 100644 --- a/qemu/include/qemu/range.h +++ b/qemu/include/qemu/range.h @@ -38,12 +38,93 @@ struct Range { uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */ }; +static inline void range_invariant(Range *range) +{ + assert((!range->begin && !range->end) /* empty */ + || range->begin <= range->end - 1); /* non-empty */ +} + +/* Compound literal encoding the empty range */ +// Unicorn: MSVC is in the stone-age and not C99/C11 compliant +//#define range_empty ((Range){ .begin = 0, .end = 0 }) + +/* Is @range empty? */ +static inline bool range_is_empty(Range *range) +{ + range_invariant(range); + return !range->begin && !range->end; +} + +/* Does @range contain @val? */ +static inline bool range_contains(Range *range, uint64_t val) +{ + return !range_is_empty(range) + && val >= range->begin && val <= range->end - 1; +} + +/* Initialize @range to the empty range */ +static inline void range_make_empty(Range *range) +{ + range->begin = 0; + range->end = 0; + assert(range_is_empty(range)); +} + +/* + * Initialize @range to span the interval [@lob,@upb]. + * Both bounds are inclusive. + * The interval must not be empty, i.e. @lob must be less than or + * equal @upb. + * The interval must not be [0,UINT64_MAX], because Range can't + * represent that. + */ +static inline void range_set_bounds(Range *range, uint64_t lob, uint64_t upb) +{ + assert(lob <= upb); + range->begin = lob; + range->end = upb + 1; /* may wrap to zero, that's okay */ + assert(!range_is_empty(range)); +} + +/* + * Initialize @range to span the interval [@lob,@upb_plus1). + * The lower bound is inclusive, the upper bound is exclusive. + * Zero @upb_plus1 is special: if @lob is also zero, set @range to the + * empty range. Else, set @range to [@lob,UINT64_MAX]. + */ +static inline void range_set_bounds1(Range *range, + uint64_t lob, uint64_t upb_plus1) +{ + range->begin = lob; + range->end = upb_plus1; + range_invariant(range); +} + +/* Return @range's lower bound. @range must not be empty. */ +static inline uint64_t range_lob(Range *range) +{ + assert(!range_is_empty(range)); + return range->begin; +} + +/* Return @range's upper bound. @range must not be empty. */ +static inline uint64_t range_upb(Range *range) +{ + assert(!range_is_empty(range)); + return range->end - 1; +} + +/* + * Extend @range to the smallest interval that includes @extend_by, too. + * This must not extend @range to cover the interval [0,UINT64_MAX], + * because Range can't represent that. + */ static inline void range_extend(Range *range, Range *extend_by) { - if (!extend_by->begin && !extend_by->end) { + if (range_is_empty(extend_by)) { return; } - if (!range->begin && !range->end) { + if (range_is_empty(range)) { *range = *extend_by; return; } @@ -54,6 +135,8 @@ static inline void range_extend(Range *range, Range *extend_by) if (range->end - 1 < extend_by->end - 1) { range->end = extend_by->end; } + /* Must not extend to { .begin = 0, .end = 0 }: */ + assert(!range_is_empty(range)); } /* Get last byte of a range from offset + length. diff --git a/qemu/qapi/string-input-visitor.c b/qemu/qapi/string-input-visitor.c index a01140a8..dd132459 100644 --- a/qemu/qapi/string-input-visitor.c +++ b/qemu/qapi/string-input-visitor.c @@ -59,8 +59,7 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) if (errno == 0 && endptr > str) { if (*endptr == '\0') { cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = start + 1; + range_set_bounds(cur, start, start); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; str = NULL; @@ -73,16 +72,14 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) end < start + 65536)) { if (*endptr == '\0') { cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = end + 1; + range_set_bounds(cur, start, end); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; str = NULL; } else if (*endptr == ',') { str = endptr + 1; cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = end + 1; + range_set_bounds(cur, start, end); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; } else { @@ -94,8 +91,7 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) } else if (*endptr == ',') { str = endptr + 1; cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = start + 1; + range_set_bounds(cur, start, start); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; } else { @@ -134,7 +130,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size, if (siv->cur_range) { Range *r = siv->cur_range->data; if (r) { - siv->cur = r->begin; + siv->cur = range_lob(r); } *list = g_malloc0(size); } else { @@ -156,7 +152,7 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) return NULL; } - if ((uint64_t)siv->cur < r->begin || (uint64_t)siv->cur >= r->end) { + if (!range_contains(r, siv->cur)) { siv->cur_range = g_list_next(siv->cur_range); if (!siv->cur_range) { return NULL; @@ -165,7 +161,7 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) if (!r) { return NULL; } - siv->cur = r->begin; + siv->cur = range_lob(r); } tail->next = g_malloc0(size); @@ -208,7 +204,7 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, goto error; } - siv->cur = r->begin; + siv->cur = range_lob(r); } *obj = siv->cur; diff --git a/qemu/util/range.c b/qemu/util/range.c index e90c988d..e5f2e711 100644 --- a/qemu/util/range.c +++ b/qemu/util/range.c @@ -46,8 +46,7 @@ GList *range_list_insert(GList *list, Range *data) { GList *l; - /* Range lists require no empty ranges */ - assert(data->begin < data->end || (data->begin && !data->end)); + assert(!range_is_empty(data)); /* Skip all list elements strictly less than data */ for (l = list; l && range_compare(l->data, data) < 0; l = l->next) {