diff --git a/lladd/io/rangeTracker.h b/lladd/io/rangeTracker.h index 937a04d..816b53a 100644 --- a/lladd/io/rangeTracker.h +++ b/lladd/io/rangeTracker.h @@ -19,12 +19,20 @@ void rangeTrackerDeinit(rangeTracker * rt); /** Add a new range - @return a null terminated array of newly-pinned, quantized ranges + + @return a null terminated array of newly-pinned, quantized ranges. + This array might contain ranges that were already pinned, and/or + ones that overlap (this aspect of the behavior is intentionally + left unspecified). */ range ** rangeTrackerAdd(rangeTracker * rt, const range * r); /** - Remove a range - @return a null terminated array of newly-unpinned, quantized ranges + Remove a range + + @return a null terminated array of unpinned, quantized ranges. + + @see rangeTrackerAdd for a discussion of approximations that may + be applied to rangeTrackerRemove's return value. */ range ** rangeTrackerRemove(rangeTracker * rt, const range * r); diff --git a/src/lladd/io/rangeTracker.c b/src/lladd/io/rangeTracker.c index b2301da..691b909 100644 --- a/src/lladd/io/rangeTracker.c +++ b/src/lladd/io/rangeTracker.c @@ -55,17 +55,22 @@ static void rangeTrackerDelta(rangeTracker * rt, const range * r, int delta) { transition * t = (transition *)RB_ENTRY(lookup)(RB_LULTEQ, &key, rt->ranges); if(t) { + assert(t->delta); + assert(t->pins >= 0); + assert(t->pins + t->delta >= 0); if(t->pos != r->start) { int newpins = t->pins + t->delta; t = malloc(sizeof(transition)); t->pos = r->start; t->delta = delta; t->pins = newpins; + assert(newpins >= 0); RB_ENTRY(search)(t, rt->ranges); // insert curpin = t->pins + t->delta; } else { t->delta += delta; curpin = t->pins + t->delta; + assert(curpin >= 0); if(t->delta == 0) { RB_ENTRY(delete)(t, rt->ranges); key.pos = t->pos; @@ -81,6 +86,7 @@ static void rangeTrackerDelta(rangeTracker * rt, const range * r, int delta) { t->pins = 0; RB_ENTRY(search)(t, rt->ranges); // insert curpin = t->pins + t->delta; + assert(curpin >= 0); } // t is now set; iterate over the tree until we reach a transition @@ -97,6 +103,7 @@ static void rangeTrackerDelta(rangeTracker * rt, const range * r, int delta) { assert(t->delta); assert(t->pins == curpin); curpin = t->pins + t->delta; + assert(curpin >= 0); } if(!t || t->pos != r->stop) { // Need to allocate new transition @@ -104,6 +111,7 @@ static void rangeTrackerDelta(rangeTracker * rt, const range * r, int delta) { t->pos = r->stop; t->delta = 0-delta; t->pins = curpin; + assert(curpin >= 0); RB_ENTRY(search)(t, rt->ranges); // insert } else { // Found existing transition at end of range. @@ -120,8 +128,260 @@ static void rangeTrackerDelta(rangeTracker * rt, const range * r, int delta) { } } -range ** rangeTrackerAdd(rangeTracker * rt, const range * r) { - rangeTrackerDelta(rt, r, 1); +static range ** rangeTrackerToArray(rangeTracker * rt) { + // count ranges. + int range_count = 0; + const transition * t; + int in_range = 0; + + RBLIST * list = RB_ENTRY(openlist) (rt->ranges); + while((t = RB_ENTRY(readlist)(list))) { + if(!(t->pins + t->delta)) { + // end of a range. + in_range = 0; + range_count++; + } else { + in_range = 1; + } + } + RB_ENTRY(closelist)(list); + if(in_range) { + range_count++; + } + + range ** ret = calloc(range_count + 1, sizeof(range *)); + + int next_range = 0; + in_range = 0; + list = RB_ENTRY(openlist) (rt->ranges); + t = RB_ENTRY(readlist)(list); + if(!t) { + assert(range_count == 0); + RB_ENTRY(closelist)(list); + return ret; + } else { + assert(!t->pins); + assert(t->delta); + assert(! ret[next_range] ); + ret[next_range] = malloc(sizeof(range)); + ret[next_range]->start = t->pos; + in_range = 1; + } + while((t = RB_ENTRY(readlist)(list))) { + if(t->pins + t->delta) { + if(!in_range) { + assert(! ret[next_range]); + ret[next_range] = malloc(sizeof(range)); + ret[next_range]->start = t->pos; + in_range = 1; + } + } else { + // end of range. + assert(in_range); + ret[next_range]->stop = t->pos; + in_range = 0; + next_range ++; + } + } + + RB_ENTRY(closelist)(list); + assert(next_range == range_count); + + return ret; +} + + +static inline long roundDown(long x, long quant) { + return (x / quant) * quant; +} +static inline long roundUp(long x, long quant) { + return (((x-1) / quant) + 1) * quant; +} + +/** + @return a set of ranges that are pinned, and that overlap the request range. +*/ +static rangeTracker * pinnedRanges(const rangeTracker * rt, const range * request, rangeTracker * ret, int delta) { + transition key; + const transition * t; + + key.pos = roundDown(request->start, rt->quantization); + + t = rblookup(RB_LUGTEQ, &key, rt->ranges); + + if(!t) { + // No ranges after request->start, so no ranges can overlap. + return ret; + } + long range_start; + + // zero if we just encountered the end of a range. The range runs from + // range_start to putative_range_stop. It is possible that a new range + // begins on the page that putative_range_stop falls on, so we do not + // output the range without looking at the next transition. + int in_range; + // This is only meaningful if in_range = 0. + long putative_range_stop; + + if(t) { + if(roundDown(t->pos, rt->quantization) >= roundUp(request->stop, rt->quantization)) { + if(t->pins) { + // entire range is pinned. + range tmp_r; + tmp_r.start = roundDown(request->start, rt->quantization); + tmp_r.stop = roundUp(request->stop, rt->quantization); + + assert(tmp_r.start >= roundDown(request->start, rt->quantization) && tmp_r.stop <= roundUp(request->stop, rt->quantization)); + rangeTrackerDelta(ret, &tmp_r, delta); + + // printf("0 %s\n", rangeToString(&tmp_r)); + } else { + // none of the range is pinned. + } + return ret; + } + if(t->pins) { + // The beginning of request is a range. + range_start = roundDown(request->start, rt->quantization); + if(0 == t->pins + t->delta) { + in_range = 0; + // even though we're not in range, we need to see if the next + // transition starts a range on the same page before returning a + // new range. + putative_range_stop = roundUp(t->pos, rt->quantization); + } else { + in_range = 1; + } + } else { + // The beginning of the request is not a range. + range_start = roundDown(t->pos, rt->quantization); + in_range = 1; + assert(t->delta); + } + } + while((t = rblookup(RB_LUGREAT, t, rt->ranges))) { + assert(t->delta); + + if(roundUp(t->pos, rt->quantization) >= roundUp(request->stop, rt->quantization)) { + if(in_range) { + // if we're in range, part of the last page must be pinned. + in_range = 0; + putative_range_stop = roundUp(request->stop, rt->quantization); + } else { + // is this transition in the last page? If so, part of the last page must be pinned. + if(t->pos < roundUp(request->stop, rt->quantization)) { + // in_range == 0 + assert(t->pins == 0); + range_start = roundDown(t->pos, rt->quantization); + putative_range_stop = roundUp(request->stop, rt->quantization); + } + } + break; + } + if(t->pins) { + assert(in_range); + + if(!(t->pins + t->delta)) { + putative_range_stop = roundUp(t->pos, rt->quantization); + in_range = 0; + } + + } else { // ! t->pins + assert(!in_range); + + if(putative_range_stop < roundDown(t->pos, rt->quantization)) { + // output a new range + range tmp_r; + tmp_r.start = range_start; + tmp_r.stop = putative_range_stop; + if(tmp_r.start != tmp_r.stop) { + assert(tmp_r.start >= roundDown(request->start, rt->quantization) && tmp_r.stop <= roundUp(request->stop, rt->quantization)); + rangeTrackerDelta(ret, &tmp_r, delta); + // printf("1 %s\n", rangeToString(&tmp_r)); + } + range_start = roundDown(t->pos, rt->quantization); + } else { + // extend old range. + } + in_range = 1; + } + } + assert(!in_range); + range tmp_r; + tmp_r.start = range_start; + tmp_r.stop = putative_range_stop; + if(tmp_r.start != tmp_r.stop) { + assert(tmp_r.start >= roundDown(request->start, rt->quantization) && tmp_r.stop <= roundUp(request->stop, rt->quantization)); + rangeTrackerDelta(ret, &tmp_r, delta); + // printf("2 %s\n", rangeToString(&tmp_r)); + + } + return ret; +} + +range ** rangeTrackerAdd(rangeTracker * rt, const range * rng) { + // printf("pinnedRanges before add %s\n", rangeToString(rng)); + rangeTracker * ret = rangeTrackerInit(rt->quantization); + pinnedRanges(rt, rng, ret, 1); + rangeTrackerDelta(rt, rng, 1); + // printf("pinnedRanges after add\n"); + rangeTracker * ret2 = rangeTrackerInit(rt->quantization); + pinnedRanges(rt, rng, ret2, 1); + + range ** ret_arry = rangeTrackerToArray(ret); + + // remove the first array from the second... + int i = 0; + + while(ret_arry[i]) { + rangeTrackerDelta(ret2, ret_arry[i], -1); + // while we're at it, deinit the first range tracker + rangeTrackerDelta(ret, ret_arry[i], -1); + free(ret_arry[i]); + i++; + } + free(ret_arry); + rangeTrackerDeinit(ret); + + i = 0; + ret_arry = rangeTrackerToArray(ret2); + + while(ret_arry[i]) { + rangeTrackerDelta(ret2, ret_arry[i], -1); + i++; + } + rangeTrackerDeinit(ret2); + + return ret_arry; + + /* // Need to return pinned ranges that overlap r. + + transition key; + const transition * t; + + key.pos = rng->start; + + t = rblookup(RB_LULTEQ, &key, rt->ranges); // could be less than if the new range touches an existing range. + + assert(t); + + range r; + int in_range = 1; + r.start = roundDown(t->pos, rt->quantization); + + while((t = rblookup(RB_LUGREAT, t, rt->ranges))) { + if(!(t->pins + t->delta)) { + assert(in_range); + in_range = 0; + r.stop = roundUp(t->pos, rt->quantization); + // printf("add range: [%lld-%lld]\n", (long long)r.start, (long long)r.stop); + } else if(!in_range) { + assert(t->pins == 0); + in_range = 1; + r.start = roundDown(t->pos, rt->quantization); + } + if(t->pos >= rng->stop) { break; } + } */ } /** @@ -129,8 +389,96 @@ range ** rangeTrackerAdd(rangeTracker * rt, const range * r) { @return a null terminated array of newly-unpinned, quantized ranges */ -range ** rangeTrackerRemove(rangeTracker * rt, const range * r) { - rangeTrackerDelta(rt, r, -1); +range ** rangeTrackerRemove(rangeTracker * rt, const range * rang) { + rangeTracker * ret = rangeTrackerInit(rt->quantization); + // printf("pinnedRanges, before del %s\n", rangeToString(rang)); + pinnedRanges(rt, rang, ret, 1); + rangeTrackerDelta(rt, rang, -1); + // printf("pinnedRanges, after del\n"); + pinnedRanges(rt, rang, ret, -1); + + range ** ret_arry = rangeTrackerToArray(ret); + + int i = 0; + while(ret_arry[i]) { + rangeTrackerDelta(ret, ret_arry[i], -1); + i++; + } + rangeTrackerDeinit(ret); + + return ret_arry; + + /* // Need to return completely unpinned ranges that overlap r. + + range bigger; + bigger.start = roundDown(rang->start, rt->quantization); + bigger.stop = roundUp(rang->stop, rt->quantization); + + transition key; + + key.pos = bigger.start; + + int unpinned_range = 0; + range r; + + const transition * t = RB_ENTRY(lookup)(RB_LUGTEQ, &key, rt->ranges); + + long last_end = bigger.start; + + // special case beginning of range + if(! t) { + t = RB_ENTRY(lookup)(RB_LULESS, &key, rt->ranges); + if(!t || 0 == t->pins + t->delta) { + // printf("A %s\n", rangeToString(&bigger)); + return; + } + } else if(t->pins == 0) { + r.start = bigger.start; + r.stop = roundDown(t->pos, rt->quantization); + if(r.start < r.stop) { + // printf("0 %s\n", rangeToString(&r)); + } + unpinned_range = 1; + assert(0 != t->pins + t->delta); + t = RB_ENTRY(lookup)(RB_LUGREAT, t, rt->ranges); + } + if(t) { + unpinned_range = (0 == t->pins + t->delta); + } + + while(t) { + + if(t->pins == 0) { + // XXX don't care if range bleeds outside of initially pinned range (for now; the difference could be huge in sparse applications) + r.start = roundUp(last_end, rt->quantization); + r.stop = roundDown(t->pos, rt->quantization); + assert(unpinned_range); + if(r.start < r.stop) { + // printf("B %s\n", rangeToString(&r)); + } else { + // printf(".. %s bigger = %s\n", rangeToString(&r), rangeToString(&bigger)); + } + } + // break after processing transition that runs over the edge... + if(t->pos >= bigger.stop) { break; } + + last_end = t->pos; + unpinned_range = (0 == t->pins + t->delta); + t = RB_ENTRY(lookup)(RB_LUGREAT, t, rt->ranges); + } + if(! t && unpinned_range) { + r.start = roundUp(last_end, rt->quantization); + r.stop = bigger.stop; + if(r.start < r.stop) { + // printf("C %s\n", rangeToString(&r)); + } + } + + // plan: (1) Write roundDown, roundUp macros + // enumerate pinned pages before and after operation. (Note that we need to round down and up to get stuff outside the range, but on common pages) + // return before - after, or after - before, depending on whether this is an add or a remove. + */ + } const transition ** rangeTrackerEnumerate(rangeTracker * rt) { @@ -169,3 +517,4 @@ char * transitionToString(const transition * t) { assert(err !=-1); return ret; } + diff --git a/test/lladd/check_rangeTracker.c b/test/lladd/check_rangeTracker.c index 39569c7..dd8efcc 100644 --- a/test/lladd/check_rangeTracker.c +++ b/test/lladd/check_rangeTracker.c @@ -3,6 +3,8 @@ #include #define LOG_NAME "check_rangeTracker.log" +#define QUANTIZATION 7 + #include #include "../check_includes.h" @@ -18,6 +20,12 @@ long myrandom(long x) { return (long)((r/max)); } +void rangeTrackerFreeRet(range ** ret) { + for(int i = 0; ret[i]; i++) { + free(ret[i]); + } + free(ret); +} void printRT(rangeTracker * rt) { const transition ** ts = rangeTrackerEnumerate(rt); int i = 0; @@ -30,7 +38,7 @@ void printRT(rangeTracker * rt) { } START_TEST(rangeTracker_smokeTest) { - rangeTracker * rt = rangeTrackerInit(512); + rangeTracker * rt = rangeTrackerInit(QUANTIZATION); const transition ** ts = rangeTrackerEnumerate(rt); @@ -45,7 +53,8 @@ START_TEST(rangeTracker_smokeTest) { r.start = 10; r.stop = 100; - rangeTrackerAdd(rt, &r); + rangeTrackerFreeRet(rangeTrackerAdd(rt, &r)); + // printRT(rt); @@ -60,10 +69,12 @@ START_TEST(rangeTracker_smokeTest) { ts[1]->pos == 100&& ts[1]->delta == -1 && ts[1]->pins == 1 && !ts[2]); + free(ts); + r.start = 20; r.stop = 80; - rangeTrackerRemove(rt, &r); + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); // printRT(rt); @@ -80,10 +91,11 @@ START_TEST(rangeTracker_smokeTest) { ts[3]->pos == 100&& ts[3]->delta == -1 && ts[3]->pins == 1 && !ts[4]); - rangeTrackerAdd(rt, &r); + rangeTrackerFreeRet(rangeTrackerAdd(rt, &r)); // printRT(rt); + free(ts); ts = rangeTrackerEnumerate(rt); // 10 20 80 90 100 @@ -95,10 +107,10 @@ START_TEST(rangeTracker_smokeTest) { assert(ts[0]->pos == 10 && ts[0]->delta == 1 && ts[0]->pins == 0 && ts[1]->pos == 100&& ts[1]->delta == -1 && ts[1]->pins == 1 && !ts[2]); - rangeTrackerAdd(rt, &r); + rangeTrackerFreeRet(rangeTrackerAdd(rt, &r)); // printRT(rt); - + free(ts); ts = rangeTrackerEnumerate(rt); // 10 20 80 90 100 @@ -111,8 +123,8 @@ START_TEST(rangeTracker_smokeTest) { ts[2]->pos == 80 && ts[2]->delta == -1 && ts[2]->pins == 2 && ts[3]->pos == 100&& ts[3]->delta == -1 && ts[3]->pins == 1 && !ts[4]); - - rangeTrackerRemove(rt, &r); + free(ts); + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); ts = rangeTrackerEnumerate(rt); // 10 20 80 90 100 @@ -123,7 +135,9 @@ START_TEST(rangeTracker_smokeTest) { assert(ts[0]->pos == 10 && ts[0]->delta == 1 && ts[0]->pins == 0 && ts[1]->pos == 100&& ts[1]->delta == -1 && ts[1]->pins == 1 && !ts[2]); - rangeTrackerRemove(rt, &r); + free(ts); + + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); // printRT(rt); @@ -138,13 +152,13 @@ START_TEST(rangeTracker_smokeTest) { ts[1]->pos == 20 && ts[1]->delta == -1 && ts[1]->pins == 1 && ts[2]->pos == 80 && ts[2]->delta == 1 && ts[2]->pins == 0 && ts[3]->pos == 100&& ts[3]->delta == -1 && ts[3]->pins == 1 && !ts[4]); - + free(ts); r.start = 80; r.stop = 90; - rangeTrackerAdd(rt, &r); + rangeTrackerFreeRet(rangeTrackerAdd(rt, &r)); // printRT(rt); @@ -161,10 +175,11 @@ START_TEST(rangeTracker_smokeTest) { ts[2]->pos == 80 && ts[2]->delta == 2 && ts[2]->pins == 0 && ts[3]->pos == 90 && ts[3]->delta == -1 && ts[3]->pins == 2 && ts[4]->pos == 100&& ts[4]->delta == -1 && ts[4]->pins == 1 && !ts[5]); - + free(ts); r.start = 80; r.stop = 100; - rangeTrackerRemove(rt, &r); + + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); // printRT(rt); ts = rangeTrackerEnumerate(rt); @@ -180,10 +195,10 @@ START_TEST(rangeTracker_smokeTest) { ts[2]->pos == 80 && ts[2]->delta == 1 && ts[2]->pins == 0 && ts[3]->pos == 90 && ts[3]->delta == -1 && ts[3]->pins == 1 && !ts[4]); - + free(ts); r.start = 10; r.stop = 20; - rangeTrackerRemove(rt, &r); + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); // printRT(rt); ts = rangeTrackerEnumerate(rt); @@ -196,12 +211,12 @@ START_TEST(rangeTracker_smokeTest) { assert(ts[0]->pos == 80 && ts[0]->delta == 1 && ts[0]->pins == 0 && ts[1]->pos == 90 && ts[1]->delta == -1 && ts[1]->pins == 1 && !ts[2]); - + free(ts); r.start = 80; r.stop = 90; - rangeTrackerRemove(rt, &r); + rangeTrackerFreeRet(rangeTrackerRemove(rt, &r)); // printRT(rt); ts = rangeTrackerEnumerate(rt); @@ -210,16 +225,16 @@ START_TEST(rangeTracker_smokeTest) { // | | | | | // | | | | | // | | | | | - - assert(!ts[0]); + free(ts); + rangeTrackerDeinit(rt); } END_TEST #define RANGE_SIZE 1000 -#define ITERATIONS 1000 -#define RANGE_COUNT 100 +#define ITERATIONS 10000 //1000 +#define RANGE_COUNT 1000 // 100 void randomRange(range * r) { long start = myrandom(RANGE_SIZE-1); long len = 1+myrandom(RANGE_SIZE - start - 1); @@ -261,42 +276,64 @@ START_TEST (rangeTracker_randomTest) { gettimeofday(&time,0); - long seed = time.tv_usec + time.tv_sec * 1000000; + long seed = time.tv_usec + time.tv_sec * 1000000;// 1170727703805787; // printf("\nSeed = %ld\n", seed); srandom(seed); + range ** r_arry; range * ranges = malloc(sizeof(range) * RANGE_COUNT); int * pins = calloc(RANGE_COUNT, sizeof(int)); - rangeTracker * rt = rangeTrackerInit(512); + rangeTracker * rt = rangeTrackerInit(QUANTIZATION); for(long i = 0; i < RANGE_COUNT; i++) { randomRange(&(ranges[i])); } - char * bitmask = calloc(RANGE_SIZE, sizeof(char)); + char * s; for(long i = 0; i < ITERATIONS; i++) { int range = myrandom(RANGE_COUNT); - switch(myrandom(3)) { case 0: { // add range - rangeTrackerAdd(rt, &ranges[range]); + s = rangeToString(&ranges[range]); + printf("pin %s\n", s); + free(s); + r_arry = rangeTrackerAdd(rt, &ranges[range]); + for(int i = 0; r_arry[i]; i++) { + s = rangeToString(r_arry[i]); + printf(" add returned %s\n", s); + free(s); + free(r_arry[i]); + } + free(r_arry); pins[range]++; checkRangeTrackerConsistency(rt); break; } case 1: { // del range if(pins[range]) { - rangeTrackerRemove(rt, &ranges[range]); + s = rangeToString(&ranges[range]); + printf("unpin %s\n", s); + free(s); + r_arry = rangeTrackerRemove(rt, &ranges[range]); + for(int i = 0; r_arry[i]; i++) { + s = rangeToString(r_arry[i]); + printf(" del returned %s\n", s); + free(s); + free(r_arry[i]); + } + free(r_arry); pins[range]--; } checkRangeTrackerConsistency(rt); break; } case 2: { // change range - for(long i = 0; i < RANGE_COUNT; i++) { - if(!pins[i]) { - randomRange(&ranges[i]); + if(!myrandom(100)) { + for(long i = 0; i < RANGE_COUNT; i++) { + if(!pins[i]) { + randomRange(&ranges[i]); + } } } break; @@ -308,13 +345,23 @@ START_TEST (rangeTracker_randomTest) { for(long i = 0; i < RANGE_COUNT; i++) { while(pins[i]) { - rangeTrackerRemove(rt, &ranges[i]); + s = rangeToString(&ranges[i]); + printf("unpin %s\n", s); + free(s); + range ** r_arry = rangeTrackerRemove(rt, &ranges[i]); + for(int i = 0; r_arry[i]; i++) { + s = rangeToString(r_arry[i]); + printf(" del returned %s\n", s); + free(s); + free(r_arry[i]); + } + free(r_arry); pins[i]--; } } - free (bitmask); - + free (ranges); + free (pins); rangeTrackerDeinit(rt); } END_TEST