From: Peter Stamfest Date: Sun, 24 Aug 2014 13:46:48 +0000 (+0200) Subject: Add interval handling functions. These will be needed in the future to X-Git-Tag: v1.5.0-rc1~42^2~39 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b9b5c0e349e4aeccb0208bbe3192326be489a80b;p=thirdparty%2Frrdtool-1.x.git Add interval handling functions. These will be needed in the future to handle pre filling of RRAs. --- diff --git a/src/rrd_create.c b/src/rrd_create.c index 33fa9c55..b2560e5e 100644 --- a/src/rrd_create.c +++ b/src/rrd_create.c @@ -1169,7 +1169,7 @@ int write_fh( return (0); } /* int write_file */ -static long overlap(long start1, long end1, long start2, long end2) { +static long overlap(time_t start1, time_t end1, time_t start2, time_t end2) { if (start1 >= end1) return 0; if (start2 >= end2) return 0; @@ -1179,6 +1179,18 @@ static long overlap(long start1, long end1, long start2, long end2) { return min(end1, end2) - max(start1, start2); } +static long is_interval_within_interval(time_t start1, time_t end1, + time_t start2, time_t end2) { + if (end1 - start1 > end2 - start2) return 0; + + long o = overlap(start1, end1, start2, end2); + return o == end1-start1; +} + +static int is_time_within_interval(time_t time, time_t start, time_t end) { + return (time >= start && time <= end); +} + static void debug_dump_rra(const rrd_t *rrd, int rra_index, int ds_index) { long total_cnt = 0; for (int zz = 0 ; zz < rra_index ; zz++) { @@ -1197,6 +1209,234 @@ static void debug_dump_rra(const rrd_t *rrd, int rra_index, int ds_index) { } } +typedef struct { + int covered; /* != 0 -> covered, == 0 -> not covered */ + /* start and end are inclusive times */ + time_t start; + time_t end; +} coverage_t; + +static inline void set_interval(coverage_t *c, int covered, time_t start, time_t end) { + c->covered = covered; + c->start = start; + c->end = end; +} + +static void dump_coverage_array(const coverage_t *current_coverage, const int *coverage_array_size) { + for (int i = 0 ; i < *coverage_array_size ; i++) { + fprintf(stderr, "%d covered=%d start=%ld end=%ld\n", i, current_coverage[i].covered, + current_coverage[i].start, current_coverage[i].end); + } +} + +static coverage_t *add_coverage(coverage_t *current_coverage, int *coverage_array_size, + time_t start, time_t end, + int *newly_covered_interval) +{ + int i; + fprintf(stderr, "ADDING %ld %ld\n", start, end); + + if (coverage_array_size == NULL) return NULL; + if (current_coverage == NULL) return NULL; + + /* + * Never extend beyond the ends of current coverage information. We do + * this by forcibly trimming the newly added interval to the start of + * the first and the end of the last interval. + */ + if (start < current_coverage->start) { + start = current_coverage->start; + } + if (end > (current_coverage + (*coverage_array_size - 1))->end) { + end = (current_coverage + (*coverage_array_size - 1))->end; + } + + *newly_covered_interval = 0; + + for (i = 0 ; i < *coverage_array_size ; i++) { + coverage_t *cc = current_coverage + i; + coverage_t *next; + + time_t org_start = cc->start; + time_t org_end = cc->end; + fprintf(stderr, "check %ld %ld against %d/%d (%ld %ld)\n", start, end, i, *coverage_array_size, org_start, org_end); + + if (is_interval_within_interval(start, end, org_start, org_end)) { + /* + * Case (A): newly added interval is fully contained within the current one. + */ + fprintf(stderr, "(A)\n"); + fprintf(stderr, "OVERLAP %ld %ld %ld %ld\n", start, end, org_start, org_end); + if (cc->covered) { + // no new data .. already fully covered, just return + break; + } + /* NOT covered by the interval, but new interval is fully contained within the current one */ + + + /* special case: is the newly covered interval EXACTLY the same as the current? + * If yes: just turn the current interval into a covered one. + * Also make sure to only report a newly covered interval if it wasn't covered before + * (NOTE: this is actually redundant, as we reach this point only for "uncovered" intervals). + * Any required collapsing of intervals will be done during the cleanup pass. + */ + + if (start == org_start && end == org_end) { + *newly_covered_interval += cc->covered ? 0 : end - start + 1; + cc->covered = 1; + break; + } + + // split interval... + if (org_start == start) { + // insert before current interval + + current_coverage = realloc(current_coverage, sizeof(coverage_t) * (*coverage_array_size + 1)); + cc = current_coverage + i; + + memmove(cc + 1, + cc, (*coverage_array_size - i) * sizeof(coverage_t)); + + set_interval(cc , 1, org_start, end ); + set_interval(cc + 1, 0, end + 1 , org_end); + + (*coverage_array_size)++; + *newly_covered_interval += end - start + 1; + break; + } + if (org_end == end) { + // insert after current interval + + current_coverage = realloc(current_coverage, sizeof(coverage_t) * (*coverage_array_size + 1)); + cc = current_coverage + i; + + memmove(cc + 1, + cc, (*coverage_array_size - i) * sizeof(coverage_t)); + + set_interval(cc , 0, org_start, start - 1); + set_interval(cc + 1, 1, start , org_end ); + + (*coverage_array_size)++; + *newly_covered_interval += end - start + 1; + break; + } + + // split into three intervals: uncovered/covered/uncovered .. add TWO new array elements + + current_coverage = realloc(current_coverage, sizeof(coverage_t) * (*coverage_array_size + 2)); + cc = current_coverage + i; + + memmove(cc + 2, + cc, (*coverage_array_size - i) * sizeof(coverage_t)); + + set_interval(cc , 0, org_start, start - 1); + set_interval(cc + 1, 1, start , end ); + set_interval(cc + 2, 0, end + 1 , org_end ); + + (*coverage_array_size) += 2; + *newly_covered_interval += end - start + 1; + break; + } + + /* + * Case (B); + * + * does the new interval fully cover the current interval? + * This might happen more than once! + * + * Note that if this case happens, case (A) above will NEVER happen... + */ + if (is_interval_within_interval(org_start, org_end, start, end)) { + fprintf(stderr, "(B)\n"); + if (! cc->covered) { + /* just turn the current interval into a covered one. Report + * the range as newly covered */ + cc->covered = 1; + *newly_covered_interval += cc->end - cc->start + 1; + } + } + + /* + * Case (C): The newly added interval starts within the current one, but + * it does not end within. + * + * We handle this by handling the implications for the current interval and then + * adjusting the new interval start period for the next iteration. That way, we will + * finally hit cases (A) or (B) and we will never see a situation where the + * new interval ends within the current on but does not start within + * (which would have become case (D)). + */ + + if (is_time_within_interval(start, org_start, org_end)) { + fprintf(stderr, "(C)\n"); + /* If the current interval is a covered one, we do nothing but + * to adjust the start interval for the next iteration. + */ + if (cc->covered) { + fprintf(stderr, "(C1)\n"); + start = org_end + 1; + continue; + } + + /* if the current interval is not covered... */ + + if (cc->start == start) { + fprintf(stderr, "(C2)\n"); + /* ... and the new interval starts with the current one, we just turn it into a + * covered one and adjust the start... */ + cc->covered = 1; + start = org_end + 1; + } else { + fprintf(stderr, "(C3) %d\n", *coverage_array_size + 1); + /* ... and the new interval does NOT start with the current one, we have to split the interval .. */ + + current_coverage = realloc(current_coverage, sizeof(coverage_t) * (*coverage_array_size + 1)); + cc = current_coverage + i; + memmove(cc + 1, cc, sizeof(coverage_t) * ((*coverage_array_size) - i)); + (*coverage_array_size)++; + + next = cc + 1; + set_interval(cc , 0, org_start, start - 1); + set_interval(cc + 1, 1, start , org_end ); + + *newly_covered_interval += next->end - next->start + 1; + start = org_end + 1; + } + } + } + + /* cleanup pass: collapse same type-intervals bordering each other.... */ + i = 0; + while (i < *coverage_array_size - 1) { + coverage_t *cc = current_coverage + i; + coverage_t *next = cc + 1; + + if (cc->covered == next->covered) { + fprintf(stderr, "Collapse %ld %ld %ld %ld\n", cc->start, cc->end, next->start, next->end); + cc->end = next->end; + + memmove(next, next + 1, sizeof(coverage_t) * (*coverage_array_size - i - 1)); + (*coverage_array_size)--; + fprintf(stderr, "%d intervals left\n", *coverage_array_size); + + // re-iterate with i unchanged !! + continue; + } + + i++; + } + + return current_coverage; +} + +static long total_coverage(const coverage_t *coverage, const int *array_size) { + long total = 0; + for (int i = 0 ; i < *array_size ; i++) { + if (coverage[i].covered) total += coverage[i].end - coverage[i].start + 1; + } + return total; +} + static int rrd_prefill_data(rrd_t *rrd, const GList *sources) { int rc = -1;