]> git.ipfire.org Git - thirdparty/rrdtool-1.x.git/commitdiff
Add interval handling functions. These will be needed in the future to
authorPeter Stamfest <peter@stamfest.at>
Sun, 24 Aug 2014 13:46:48 +0000 (15:46 +0200)
committerPeter Stamfest <peter@stamfest.at>
Sun, 31 Aug 2014 20:19:22 +0000 (22:19 +0200)
handle pre filling of RRAs.

src/rrd_create.c

index 33fa9c55b6b749aa6c903f48d9e66e672ffa3f36..b2560e5ea2933dfce2a5eaeae8d23e4013549cf9 100644 (file)
@@ -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;