#include <common.h>
#include <debug.h>
#include <rangesort.h>
#define PARALLEL_ARRAY_SIZE (5)
struct range_list_t {
range_t *array;
#ifdef DEBUG
int is_sorted;
#endif
int array_length;
int num_ranges;
};
range_list_t* init_range_list(void) {
range_list_t *ranges = (range_list_t *)MALLOC(sizeof(range_list_t));
ranges->array = (range_t *)MALLOC(PARALLEL_ARRAY_SIZE*sizeof(range_t));
ranges->array_length = PARALLEL_ARRAY_SIZE;
ranges->num_ranges = 0;
#ifdef DEBUG
ranges->is_sorted = 0;
#endif
return ranges;
}
void destroy_range_list(range_list_t *ranges) {
int idx;
for (idx = 0; idx < ranges->num_ranges; idx++) {
if (ranges->array[idx].user_dtor) {
ASSERT(ranges->array[idx].user);
ranges->array[idx].user_dtor(ranges->array[idx].user);
}
}
FREE(ranges->array);
FREE(ranges);
}
static inline int CONTAINS(range_t *container, range_t *contained) {
return container->start <= contained->start && contained->length &&
(container->start + container->length >
contained->start + contained->length);
}
static inline int IN_RANGE(range_t *range, GElf_Off point) {
return
range->start <= point &&
point < (range->start + range->length);
}
static inline int INTERSECT(range_t *left, range_t *right) {
return
(IN_RANGE(left, right->start) &&
IN_RANGE(right, left->start + left->length)) ||
(IN_RANGE(right, left->start) &&
IN_RANGE(left, right->start + right->length));
}
static int range_cmp_for_search(const void *l, const void *r) {
range_t *left = (range_t *)l, *right = (range_t *)r;
if (INTERSECT(left, right) ||
CONTAINS(left, right) ||
CONTAINS(right, left)) {
return 0;
}
/* elfcopy.c checks that the start of a section begins at or
after end of the previous section in the sorted list. So we need
to be careful about empty sections. */
if (left->start != right->start)
return left->start - right->start;
else {
ASSERT(left->length == 0 || right->length == 0);
return left->length - right->length;
}
}
static inline void run_checks(const void *l, const void *r) {
range_t *left = (range_t *)l, *right = (range_t *)r;
if (CONTAINS(left, right)) {
if (left->err_fn)
left->err_fn(ERROR_CONTAINS, left, right);
FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
left->start, left->start + left->length,
right->start, right->start + right->length);
}
if (CONTAINS(right, left)) {
if (right->err_fn)
right->err_fn(ERROR_CONTAINS, left, right);
FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
right->start, right->start + right->length,
left->start, left->start + left->length);
}
if (INTERSECT(left, right)) {
if (left->err_fn)
left->err_fn(ERROR_OVERLAPS, left, right);
FAILIF(1, "Range sorting error: [%lld, %lld)and [%lld, %lld) intersect!\n",
left->start, left->start + left->length,
right->start, right->start + right->length);
}
}
static int range_cmp(const void *l, const void *r) {
run_checks(l, r);
range_t *left = (range_t *)l, *right = (range_t *)r;
/* elfcopy.c checks that the start of a section begins at or
after end of the previous section in the sorted list. So we need
to be careful about empty sections. */
if (left->start != right->start)
return left->start - right->start;
else {
ASSERT(left->length == 0 || right->length == 0);
return left->length - right->length;
}
}
void add_unique_range_nosort(
range_list_t *ranges,
GElf_Off start,
GElf_Off length,
void *user,
void (*err_fn)(range_error_t, range_t *, range_t *),
void (*user_dtor)(void * ))
{
if (ranges->num_ranges == ranges->array_length) {
ranges->array_length += PARALLEL_ARRAY_SIZE;
ranges->array = REALLOC(ranges->array,
ranges->array_length*sizeof(range_t));
}
ranges->array[ranges->num_ranges].start = start;
ranges->array[ranges->num_ranges].length = length;
ranges->array[ranges->num_ranges].user = user;
ranges->array[ranges->num_ranges].err_fn = err_fn;
ranges->array[ranges->num_ranges].user_dtor = user_dtor;
ranges->num_ranges++;
}
range_list_t *sort_ranges(range_list_t *ranges) {
if (ranges->num_ranges > 1)
qsort(ranges->array, ranges->num_ranges, sizeof(range_t), range_cmp);
ranges->is_sorted = 1;
return ranges;
}
range_t *find_range(range_list_t *ranges, GElf_Off value) {
#if 1
int i;
for (i = 0; i < ranges->num_ranges; i++) {
if (ranges->array[i].start <= value &&
value < ranges->array[i].start + ranges->array[i].length)
return ranges->array + i;
}
return NULL;
#else
ASSERT(ranges->is_sorted); /* The range list must be sorted */
range_t lookup;
lookup.start = value;
lookup.length = 0;
return
(range_t *)bsearch(&lookup,
ranges->array, ranges->num_ranges, sizeof(range_t),
range_cmp_for_search);
#endif
}
int get_num_ranges(const range_list_t *ranges)
{
return ranges->num_ranges;
}
range_t *get_sorted_ranges(const range_list_t *ranges, int *num_ranges) {
ASSERT(ranges->is_sorted); /* The range list must be sorted */
if (num_ranges) {
*num_ranges = ranges->num_ranges;
}
return ranges->array;
}
GElf_Off get_last_address(const range_list_t *ranges) {
ASSERT(ranges->num_ranges);
return
ranges->array[ranges->num_ranges-1].start +
ranges->array[ranges->num_ranges-1].length;
}
static void handle_range_error(range_error_t err,
range_t *left, range_t *right) {
switch (err) {
case ERROR_CONTAINS:
ERROR("ERROR: section (%lld, %lld bytes) contains "
"section (%lld, %lld bytes)\n",
left->start, left->length,
right->start, right->length);
break;
case ERROR_OVERLAPS:
ERROR("ERROR: Section (%lld, %lld bytes) intersects "
"section (%lld, %lld bytes)\n",
left->start, left->length,
right->start, right->length);
break;
default:
ASSERT(!"Unknown range error code!");
}
FAILIF(1, "Range error.\n");
}
static void destroy_contiguous_range_info(void *user) {
contiguous_range_info_t *info = (contiguous_range_info_t *)user;
FREE(info->ranges);
FREE(info);
}
static void handle_contiguous_range_error(range_error_t err,
range_t *left,
range_t *right)
{
contiguous_range_info_t *left_data =
(contiguous_range_info_t *)left->user;
ASSERT(left_data);
contiguous_range_info_t *right_data =
(contiguous_range_info_t *)right->user;
ASSERT(right_data);
PRINT("Contiguous-range overlap error. Printing contained ranges:\n");
int cnt;
PRINT("\tLeft ranges:\n");
for (cnt = 0; cnt < left_data->num_ranges; cnt++) {
PRINT("\t\t[%lld, %lld)\n",
left_data->ranges[cnt].start,
left_data->ranges[cnt].start + left_data->ranges[cnt].length);
}
PRINT("\tRight ranges:\n");
for (cnt = 0; cnt < right_data->num_ranges; cnt++) {
PRINT("\t\t[%lld, %lld)\n",
right_data->ranges[cnt].start,
right_data->ranges[cnt].start + right_data->ranges[cnt].length);
}
handle_range_error(err, left, right);
}
range_list_t* get_contiguous_ranges(const range_list_t *input)
{
ASSERT(input);
FAILIF(!input->is_sorted,
"get_contiguous_ranges(): input range list is not sorted!\n");
range_list_t* ret = init_range_list();
int num_ranges;
range_t *ranges = get_sorted_ranges(input, &num_ranges);
int end_idx = 0;
while (end_idx < num_ranges) {
int start_idx = end_idx++;
int old_end_idx = start_idx;
int total_length = ranges[start_idx].length;
while (end_idx < num_ranges) {
if (ranges[old_end_idx].start + ranges[old_end_idx].length !=
ranges[end_idx].start)
break;
old_end_idx = end_idx++;
total_length += ranges[old_end_idx].length;
}
contiguous_range_info_t *user =
(contiguous_range_info_t *)MALLOC(sizeof(contiguous_range_info_t));
user->num_ranges = end_idx - start_idx;
user->ranges = (range_t *)MALLOC(user->num_ranges * sizeof(range_t));
int i;
for (i = 0; i < end_idx - start_idx; i++)
user->ranges[i] = ranges[start_idx + i];
add_unique_range_nosort(ret,
ranges[start_idx].start,
total_length,
user,
handle_contiguous_range_error,
destroy_contiguous_range_info);
}
return ret;
}
range_list_t* subtract_ranges(const range_list_t *r, const range_list_t *s)
{
ASSERT(r); ASSERT(r->is_sorted);
ASSERT(s); ASSERT(s->is_sorted);
range_list_t *result = init_range_list();
int r_num_ranges, r_idx;
range_t *r_ranges = get_sorted_ranges(r, &r_num_ranges);
ASSERT(r_ranges);
int s_num_ranges, s_idx;
range_t *s_ranges = get_sorted_ranges(s, &s_num_ranges);
ASSERT(s_ranges);
s_idx = 0;
for (r_idx = 0; r_idx < r_num_ranges; r_idx++) {
GElf_Off last_start = r_ranges[r_idx].start;
for (; s_idx < s_num_ranges; s_idx++) {
if (CONTAINS(&r_ranges[r_idx], &s_ranges[s_idx])) {
if (last_start ==
r_ranges[r_idx].start + r_ranges[r_idx].length) {
break;
}
if (last_start == s_ranges[s_idx].start) {
last_start += s_ranges[s_idx].length;
continue;
}
INFO("Adding subtracted range [%lld, %lld)\n",
last_start,
s_ranges[s_idx].start);
add_unique_range_nosort(
result,
last_start,
s_ranges[s_idx].start - last_start,
NULL,
NULL,
NULL);
last_start = s_ranges[s_idx].start + s_ranges[s_idx].length;
} else {
ASSERT(!INTERSECT(&r_ranges[r_idx], &s_ranges[s_idx]));
break;
}
} /* while (s_idx < s_num_ranges) */
} /* for (r_idx = 0; r_idx < r_num_ranges; r_idx++) */
return result;
}