Skip to content

Commit e006328

Browse files
committed
range-set: unify the coalescing step and tidy the algebra
With the algebra now in its own module, clean up a few things the move made easy to see. None changes behavior: the unit tests added with the extraction still pass. - sort_and_merge_range_set() and range_set_union() open-coded the same final step: append a range, merging it into the previous one when they overlap or touch, and dropping it when empty. Pull that into range_set_append_coalesce() and call it from both. union stays a linear 2-way merge, and sort_and_merge stays an in-place, allocation-free compaction: range_set_grow() does not reallocate while the output count stays within the original allocation, and the reset output cursor never passes the read index, so appending over the same buffer never clobbers a range not yet read. - Name the temporaries in range_set_map_across_diff(): "untouched" for the ranges the diff did not touch and "shifted" for those after they are shifted to the parent side, so the three-step body reads as its own description. - Drop the dead "if (!rs) return" guard in range_set_check_invariants(), now static with a single, always-valid caller; explain the range_cmp() comment (start is long, and narrowing the difference to int could flip the sign); and const-qualify ranges_overlap(). No functional change intended. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
1 parent b67c472 commit e006328

1 file changed

Lines changed: 48 additions & 41 deletions

File tree

range-set.c

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ static int range_cmp(const void *_r, const void *_s)
5757
const struct range *r = _r;
5858
const struct range *s = _s;
5959

60-
/* this could be simply 'return r.start-s.start', but for the types */
60+
/*
61+
* Not "r->start - s->start": start is long, and narrowing the
62+
* difference to int could overflow and flip the sign.
63+
*/
6164
if (r->start == s->start)
6265
return 0;
6366
if (r->start < s->start)
@@ -72,9 +75,6 @@ static void range_set_check_invariants(struct range_set *rs)
7275
{
7376
unsigned int i;
7477

75-
if (!rs)
76-
return;
77-
7878
if (rs->nr)
7979
assert(rs->ranges[0].start < rs->ranges[0].end);
8080

@@ -84,27 +84,43 @@ static void range_set_check_invariants(struct range_set *rs)
8484
}
8585
}
8686

87+
/*
88+
* Append [start, end) to rs, coalescing it with the last range when they
89+
* overlap or touch, and dropping it when empty. The caller must append in
90+
* nondecreasing start order, so rs stays sorted. This is the shared
91+
* canonicalizing step for building a range set from an already-ordered
92+
* stream, whether into a fresh set or in place over the same buffer.
93+
*/
94+
static void range_set_append_coalesce(struct range_set *rs, long start, long end)
95+
{
96+
if (start == end)
97+
return;
98+
if (!rs->nr || rs->ranges[rs->nr-1].end < start) {
99+
range_set_grow(rs, 1);
100+
rs->ranges[rs->nr].start = start;
101+
rs->ranges[rs->nr].end = end;
102+
rs->nr++;
103+
} else if (rs->ranges[rs->nr-1].end < end) {
104+
rs->ranges[rs->nr-1].end = end;
105+
}
106+
}
107+
87108
void sort_and_merge_range_set(struct range_set *rs)
88109
{
89-
unsigned int i;
90-
unsigned int o = 0; /* output cursor */
110+
unsigned int i, nr = rs->nr;
91111

92-
QSORT(rs->ranges, rs->nr, range_cmp);
112+
QSORT(rs->ranges, nr, range_cmp);
93113

94-
for (i = 0; i < rs->nr; i++) {
95-
if (rs->ranges[i].start == rs->ranges[i].end)
96-
continue;
97-
if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) {
98-
if (rs->ranges[o-1].end < rs->ranges[i].end)
99-
rs->ranges[o-1].end = rs->ranges[i].end;
100-
} else {
101-
rs->ranges[o].start = rs->ranges[i].start;
102-
rs->ranges[o].end = rs->ranges[i].end;
103-
o++;
104-
}
105-
}
106-
assert(o <= rs->nr);
107-
rs->nr = o;
114+
/*
115+
* Compact in place. The output cursor is rs->nr, reset to 0; it
116+
* never overtakes the read index i, and range_set_grow() does not
117+
* reallocate while nr stays within the original count, so appending
118+
* over the same buffer never clobbers an unread range.
119+
*/
120+
rs->nr = 0;
121+
for (i = 0; i < nr; i++)
122+
range_set_append_coalesce(rs, rs->ranges[i].start,
123+
rs->ranges[i].end);
108124

109125
range_set_check_invariants(rs);
110126
}
@@ -115,7 +131,6 @@ void range_set_union(struct range_set *out,
115131
unsigned int i = 0, j = 0;
116132
struct range *ra = a->ranges;
117133
struct range *rb = b->ranges;
118-
/* cannot make an alias of out->ranges: it may change during grow */
119134

120135
assert(out->nr == 0);
121136
while (i < a->nr || j < b->nr) {
@@ -133,16 +148,8 @@ void range_set_union(struct range_set *out,
133148
new_range = &ra[i++];
134149
else /* a exhausted */
135150
new_range = &rb[j++];
136-
if (new_range->start == new_range->end)
137-
; /* empty range */
138-
else if (!out->nr || out->ranges[out->nr-1].end < new_range->start) {
139-
range_set_grow(out, 1);
140-
out->ranges[out->nr].start = new_range->start;
141-
out->ranges[out->nr].end = new_range->end;
142-
out->nr++;
143-
} else if (out->ranges[out->nr-1].end < new_range->end) {
144-
out->ranges[out->nr-1].end = new_range->end;
145-
}
151+
range_set_append_coalesce(out, new_range->start,
152+
new_range->end);
146153
}
147154
}
148155

@@ -153,7 +160,7 @@ void range_set_union(struct range_set *out,
153160
static void range_set_difference(struct range_set *out,
154161
struct range_set *a, struct range_set *b)
155162
{
156-
unsigned int i, j = 0;
163+
unsigned int i, j = 0;
157164
for (i = 0; i < a->nr; i++) {
158165
long start = a->ranges[i].start;
159166
long end = a->ranges[i].end;
@@ -212,7 +219,7 @@ void diff_ranges_release(struct diff_ranges *diff)
212219
* its parent-side counterpart (range_set_map_across_diff).
213220
*/
214221

215-
static int ranges_overlap(struct range *a, struct range *b)
222+
static int ranges_overlap(const struct range *a, const struct range *b)
216223
{
217224
return !(a->end <= b->start || b->end <= a->start);
218225
}
@@ -285,16 +292,16 @@ void range_set_map_across_diff(struct range_set *out,
285292
struct diff_ranges **touched_out)
286293
{
287294
struct diff_ranges *touched = xmalloc(sizeof(*touched));
288-
struct range_set tmp1 = { 0 };
289-
struct range_set tmp2 = { 0 };
295+
struct range_set untouched = { 0 };
296+
struct range_set shifted = { 0 };
290297

291298
diff_ranges_init(touched);
292299
diff_ranges_filter_touched(touched, diff, rs);
293-
range_set_difference(&tmp1, rs, &touched->target);
294-
range_set_shift_diff(&tmp2, &tmp1, diff);
295-
range_set_union(out, &tmp2, &touched->parent);
296-
range_set_release(&tmp1);
297-
range_set_release(&tmp2);
300+
range_set_difference(&untouched, rs, &touched->target);
301+
range_set_shift_diff(&shifted, &untouched, diff);
302+
range_set_union(out, &shifted, &touched->parent);
303+
range_set_release(&untouched);
304+
range_set_release(&shifted);
298305

299306
*touched_out = touched;
300307
}

0 commit comments

Comments
 (0)