/* bt.c: BIT TABLES
*
* $Id: //info.ravenbrook.com/project/mps/custom/cet/branch/2016-09-13/job004006/code/bt.c#1 $
* Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license.
*
* READERSHIP
*
* .readership: Any MPS developer
*
* DESIGN
*
* .design: see <design/bt/>
*
* .aver.critical: The function BTIsResRange (and anything it calls)
* is on the critical path <design/critical-path/> because it is
* called by NailboardIsResRange, which is called for every object in
* a nailboarded segment when the segment is scanned or reclaimed; see
* <design/nailboard/#impl.isresrange>.
*/
#include "bt.h"
#include "config.h"
#include "check.h"
#include "mpm.h"
SRCID(bt, "$Id: //info.ravenbrook.com/project/mps/custom/cet/branch/2016-09-13/job004006/code/bt.c#1 $");
/* BTIndexAlignUp, BTIndexAlignDown -- Align bit-table indices
*
* Align bit-table indices up and down to word boundaries
*/
#define BTIndexAlignUp(index) (IndexAlignUp((index), MPS_WORD_WIDTH))
#define BTIndexAlignDown(index) (IndexAlignDown((index), MPS_WORD_WIDTH))
/* BTMask -- generate sub-word masks
*
* Create a mask with only specified bits set
*/
/* Return a word mask of bits set only from base and above */
#define BTMaskLow(base) (~(Word)0 << (base))
/* Return a word mask of bits set only below limit */
#define BTMaskHigh(limit) (~(Word)0 >> (MPS_WORD_WIDTH - (limit)))
/* Return a word mask of bits set only in requested range */
#define BTMask(base,limit) (BTMaskHigh((limit)) & BTMaskLow((base)))
/* BTWordIndex, BTBitIndex -- Decode BT indexes
*
* Return word and bit indexes from index
*/
#define BTWordIndex(index) ((index) >> MPS_WORD_SHIFT)
#define BTBitIndex(index) ((index) & (MPS_WORD_WIDTH - 1))
/* BTIsSmallRange -- test range size
*
* Predicate to determine whether a range is sufficiently small
* that it's not worth trying to separate words and odd bits.
* The choice of what counts as "sufficiently small" is made
* for efficiency reasons. Empirical evidence indicates that
* a good choice is ranges of size 6 or less.
*/
#define BTIsSmallRange(base,limit) ((base) + 6 >= (limit))
/* ACT_ON_RANGE -- macro to act on a base-limit range
*
* Three actions should be provided:
* - single_action(btIndex) - operates on a single bit
* - bits_action(wordIndex, base, limit) -- operates on part-words
* - word_action(wordIndex) -- Operates on full words in range
* WORD_ACTIONs should not use break or continue.
*
* If the range is small enough it will be processed a single
* bit at a time. Larger ranges are processed as words where
* possible, and part-words for boundary bits.
*/
#define ACT_ON_RANGE(base,limit,single_action, \
bits_action,word_action) \
BEGIN \
if (BTIsSmallRange((base), (limit))) { \
/* Small ranges are processed most efficiently bit-by-bit */ \
Index actBit; \
for (actBit = (base); actBit < (limit); ++actBit) { \
single_action(actBit); \
} \
} else { \
Index actInnerBase = BTIndexAlignUp((base)); \
if (actInnerBase > (limit)) { /* no inner range */ \
/* Must have base < limit otherwise caught by small range case */ \
/* And see .aver.critical. */ \
AVER_CRITICAL((base) < (limit)); \
bits_action(BTWordIndex((base)), \
BTBitIndex((base)), \
BTBitIndex((limit))); \
} else { \
Index actInnerLimit = BTIndexAlignDown((limit)); \
Index actWordIndex, actWordBase, actWordLimit; \
\
actWordBase = BTWordIndex(actInnerBase); \
actWordLimit = BTWordIndex(actInnerLimit); \
\
if ((base) < actInnerBase) { \
bits_action(actWordBase-1, \
BTBitIndex((base)), \
MPS_WORD_WIDTH); \
} \
\
for (actWordIndex = actWordBase; actWordIndex < actWordLimit; \
++actWordIndex) { \
word_action(actWordIndex); \
} \
\
if ((limit) > actInnerLimit) { \
bits_action(actWordLimit, 0, BTBitIndex((limit))); \
} \
} \
} \
END
/* ACT_ON_RANGE_HIGH -- macro to act on a base-limit range
*
* in reverse order. Usage as for ACT_ON_RANGE
*/
#define ACT_ON_RANGE_HIGH(base,limit,single_action, \
bits_action,word_action) \
BEGIN \
if (BTIsSmallRange((base), (limit))) { \
/* Small ranges are processed most efficiently bit-by-bit */ \
Index actBit; \
for (actBit = (limit); actBit > (base); --actBit) { \
single_action(actBit - 1); \
} \
} else { \
Index actInnerBase = BTIndexAlignUp((base)); \
if (actInnerBase > (limit)) { /* no inner range */ \
AVER((base) < (limit)); /* caught by small range case */ \
bits_action(BTWordIndex((base)), \
BTBitIndex((base)), \
BTBitIndex((limit))); \
} else { \
Index actInnerLimit = BTIndexAlignDown((limit)); \
Index actWordIndex, actWordBase, actWordLimit; \
\
actWordBase = BTWordIndex(actInnerBase); \
actWordLimit = BTWordIndex(actInnerLimit); \
\
if ((limit) > actInnerLimit) { \
bits_action(actWordLimit, 0, BTBitIndex((limit))); \
} \
\
for (actWordIndex = actWordLimit; actWordIndex > actWordBase; \
--actWordIndex) { \
word_action(actWordIndex-1); \
} \
\
if ((base) < actInnerBase) { \
bits_action(actWordBase-1, \
BTBitIndex((base)), \
MPS_WORD_WIDTH); \
} \
} \
} \
END
/* BTCreate -- allocate a BT from the control pool
*
* See <design/bt/#if.create>
*/
Res BTCreate(BT *btReturn, Arena arena, Count length)
{
Res res;
BT bt;
void *p;
AVER(btReturn != NULL);
AVERT(Arena, arena);
AVER(length > 0);
res = ControlAlloc(&p, arena, BTSize(length));
if (res != ResOK)
return res;
bt = (BT)p;
*btReturn = bt;
return ResOK;
}
/* BTDestroy -- free a BT to the control pool.
*
* See <design/bt/#if.destroy>
*/
void BTDestroy(BT bt, Arena arena, Count length)
{
AVER(bt != NULL);
AVERT(Arena, arena);
AVER(length > 0);
ControlFree(arena, bt, BTSize(length));
}
/* BTCheck -- check the validity of a bit table
*
* There's not much that can be checked at present. This is
* discussed in review.impl.c.bt.4.
*/
Bool BTCheck(BT bt)
{
AVER(bt != NULL);
AVER(AddrIsAligned((Addr)bt, sizeof(Word)));
return TRUE;
}
/* BTSize -- return the size of a BT
*
* See <design/bt/#fun.size>
*/
Size (BTSize)(Count n)
{
/* check that the expression used in rounding up doesn't overflow */
AVER(n+MPS_WORD_WIDTH-1 > n);
return BTSize(n);
}
/* BTGet -- get a bit from a BT
*
* See <design/bt/#fun.get>
*/
Bool (BTGet)(BT t, Index i)
{
AVERT(BT, t);
/* Can't check i */
/* see macro in <code/mpm.h> */
return BTGet(t, i);
}
/* BTSet -- set a bit in a BT
*
* See <design/bt/#fun.set>
*/
void (BTSet)(BT t, Index i)
{
AVERT(BT, t);
/* Can't check i */
/* see macro in <code/mpm.h> */
BTSet(t, i);
}
/* BTRes -- reset a bit in a BT
*
* <design/bt/#fun.res>
*/
void (BTRes)(BT t, Index i)
{
AVERT(BT, t);
/* Can't check i */
/* see macro in <code/mpm.h> */
BTRes(t, i);
}
/* BTSetRange -- set a range of bits in a BT
*
* <design/bt/#fun.set-range>
*/
void BTSetRange(BT t, Index base, Index limit)
{
AVERT(BT, t);
AVER(base < limit);
#define SINGLE_SET_RANGE(i) \
BTSet(t, (i))
#define BITS_SET_RANGE(i,base,limit) \
t[(i)] |= BTMask((base),(limit))
#define WORD_SET_RANGE(i) \
t[(i)] = ~(Word)(0)
ACT_ON_RANGE(base, limit, SINGLE_SET_RANGE,
BITS_SET_RANGE, WORD_SET_RANGE);
}
/* BTIsResRange -- test whether a range of bits is all reset
*
* See <design/bt/#fun.is-reset-range>.
*/
Bool BTIsResRange(BT bt, Index base, Index limit)
{
AVERT_CRITICAL(BT, bt); /* See .aver.critical */
AVER_CRITICAL(base < limit);
/* Can't check range of base or limit */
#define SINGLE_IS_RES_RANGE(i) \
if (BTGet(bt, (i))) return FALSE
#define BITS_IS_RES_RANGE(i,base,limit) \
if ((bt[(i)] & BTMask((base),(limit))) != (Word)0) return FALSE
#define WORD_IS_RES_RANGE(i) \
if (bt[(i)] != (Word)0) return FALSE
ACT_ON_RANGE(base, limit, SINGLE_IS_RES_RANGE,
BITS_IS_RES_RANGE, WORD_IS_RES_RANGE);
return TRUE;
}
/* BTIsSetRange -- test whether a range of bits is all set
*
* See <design/bt/#fun.is-set-range>.
*/
Bool BTIsSetRange(BT bt, Index base, Index limit)
{
AVERT(BT, bt);
AVER(base < limit);
/* Can't check range of base or limit */
#define SINGLE_IS_SET_RANGE(i) \
if (!BTGet(bt, (i))) return FALSE
#define BITS_IS_SET_RANGE(i,base,limit) \
BEGIN \
Word bactMask = BTMask((base),(limit)); \
if ((bt[(i)] & bactMask) != bactMask) \
return FALSE; \
END
#define WORD_IS_SET_RANGE(i) \
if (bt[(i)] != ~(Word)0) return FALSE
ACT_ON_RANGE(base, limit, SINGLE_IS_SET_RANGE,
BITS_IS_SET_RANGE, WORD_IS_SET_RANGE);
return TRUE;
}
/* BTResRange -- reset a range of bits in a BT
*
* <design/bt/#fun.res-range>
*/
void BTResRange(BT t, Index base, Index limit)
{
AVERT(BT, t);
AVER(base < limit);
#define SINGLE_RES_RANGE(i) \
BTRes(t, (i))
#define BITS_RES_RANGE(i,base,limit) \
t[(i)] &= ~(BTMask((base),(limit)))
#define WORD_RES_RANGE(i) t[(i)] = (Word)(0)
ACT_ON_RANGE(base, limit, SINGLE_RES_RANGE,
BITS_RES_RANGE, WORD_RES_RANGE);
}
/* BTFindSet -- find the lowest set bit in a range in a bit table.
*
* Sets foundReturn to false if the range is entirely reset;
* in this case indexReturn is unset. Sets foundReturn to true
* otherwise.
*
* Implemented as a macro for efficiency reasons.
* The macro internally uses the label btFindSetLabel.
* If the macro must be used more than once within a function
* this label must be redefined to avoid a nameclash. E.g.
* #define btFindSetLabel uniqueLabel
* BTFindSet(...)
* #undef btFindSetLabel
*/
#define BTFindSet(foundReturn,indexReturn,bt,base,limit)\
BEGIN \
Bool *bfsFoundReturn = (foundReturn); \
Index *bfsIndexReturn = (indexReturn); \
BT bfsBt = (bt); \
ACT_ON_RANGE((base), (limit), SINGLE_FIND_SET, \
BITS_FIND_SET, WORD_FIND_SET); \
*bfsFoundReturn = FALSE; \
btFindSetLabel:; \
END
#define SINGLE_FIND_SET(i) \
if (BTGet(bfsBt, (i))) { \
*bfsIndexReturn = (i); \
*bfsFoundReturn = TRUE; \
goto btFindSetLabel; \
}
#define BITS_FIND_SET(wi,base,limit) \
BEGIN \
Index bactWi = (wi); \
ACTION_FIND_SET(bactWi, bfsBt[bactWi], (base), (limit)); \
END
#define WORD_FIND_SET(wi) \
BEGIN \
Index wactWi = (wi); \
ACTION_FIND_SET(wactWi, bfsBt[wactWi], 0, MPS_WORD_WIDTH); \
END
#define ACTION_FIND_SET(wi,word,base,limit) \
ACTION_FIND_SET_BIT((wi),(word),(base),(limit),btFindSetLabel)
/* ACTION_FIND_SET_BIT -- Find first set bit in a range
*
* Helper macro to find the low bit in a range of a word.
* Works by first shifting the base of the range to the low
* bits of the word. Then loops performing a binary chop
* over the data looking to see if a bit is set in the lower
* half. If not, it must be in the upper half which is then
* shifted down. The loop completes after using a chop unit
* of a single single bit.
*/
#define ACTION_FIND_SET_BIT(wi,word,base,limit,label) \
BEGIN \
/* no need to mask the low bits which are shifted */ \
Index actionIndex = (base); \
Word actionWord = ((word) & BTMaskHigh((limit))) >> actionIndex; \
Count actionMaskWidth = (MPS_WORD_WIDTH >> 1); \
Word actionMask = ~(Word)0 >> (MPS_WORD_WIDTH-actionMaskWidth); \
if (actionWord != (Word)0) { \
while (actionMaskWidth != (Count)0) { \
if ((actionWord & actionMask) == (Word)0) { \
actionIndex += actionMaskWidth; \
actionWord >>= actionMaskWidth; \
} \
actionMaskWidth >>= 1; \
actionMask >>= actionMaskWidth; \
} \
*bfsIndexReturn = ((wi) << MPS_WORD_SHIFT) | actionIndex; \
*bfsFoundReturn = TRUE; \
goto label; \
} \
END
/* BTFindRes -- find the lowest reset bit in a range in a bit table.
*
* Usage as for BTFindSet
*
* Internally uses the label btFindResLabel
* which must be redefined to avoid a nameclash if the macro is
* used twice in a function scope.
*/
#define BTFindRes(foundReturn,indexReturn,bt,base,limit)\
BEGIN \
Bool *bfsFoundReturn = (foundReturn); \
Index *bfsIndexReturn = (indexReturn); \
BT bfsBt = (bt); \
ACT_ON_RANGE((base), (limit), SINGLE_FIND_RES, \
BITS_FIND_RES, WORD_FIND_RES); \
*bfsFoundReturn = FALSE; \
btFindResLabel:; \
END
#define SINGLE_FIND_RES(i) \
if (!BTGet(bfsBt, (i))) { \
*bfsIndexReturn = (i); \
*bfsFoundReturn = TRUE; \
goto btFindResLabel; \
}
#define BITS_FIND_RES(wi,base,limit) \
BEGIN \
Index bactWi = (wi); \
ACTION_FIND_RES(bactWi,bfsBt[bactWi], (base), (limit)); \
END
#define WORD_FIND_RES(wi) \
BEGIN \
Index wactWi = (wi); \
ACTION_FIND_RES(wactWi, bfsBt[wactWi], 0, MPS_WORD_WIDTH); \
END
#define ACTION_FIND_RES(wi,word,base,limit) \
ACTION_FIND_SET_BIT((wi),~(word),(base),(limit),btFindResLabel)
/* BTFindSetHigh -- find the highest set bit in a range in a bit table.
*
* Usage as for BTFindSet
*
* Internally uses the label btFindSetHighLabel
* which must be redefined to avoid a nameclash if the macro is
* used twice in a function scope.
*/
#define BTFindSetHigh(foundReturn,indexReturn,bt,base,limit)\
BEGIN \
Bool *bfsFoundReturn = (foundReturn); \
Index *bfsIndexReturn = (indexReturn); \
BT bfsBt = (bt); \
ACT_ON_RANGE_HIGH((base), (limit), SINGLE_FIND_SET_HIGH, \
BITS_FIND_SET_HIGH, WORD_FIND_SET_HIGH); \
*bfsFoundReturn = FALSE; \
btFindSetHighLabel:; \
END
#define SINGLE_FIND_SET_HIGH(i) \
if (BTGet(bfsBt, (i))) { \
*bfsIndexReturn = (i); \
*bfsFoundReturn = TRUE; \
goto btFindSetHighLabel; \
}
#define BITS_FIND_SET_HIGH(wi,base,limit) \
BEGIN \
Index bactWi = (wi); \
ACTION_FIND_SET_HIGH(bactWi, bfsBt[bactWi], (base), (limit)); \
END
#define WORD_FIND_SET_HIGH(wi) \
BEGIN \
Index wactWi = (wi); \
ACTION_FIND_SET_HIGH(wactWi, (bfsBt[wactWi]), 0, MPS_WORD_WIDTH); \
END
#define ACTION_FIND_SET_HIGH(wi,word,base,limit) \
ACTION_FIND_SET_BIT_HIGH((wi),(word),(base),(limit),btFindSetHighLabel)
/* ACTION_FIND_SET_BIT_HIGH -- Find highest set bit in a range
*
* Helper macro to find the high bit in a range of a word.
* Essentially a mirror image of ACTION_FIND_SET
*/
#define ACTION_FIND_SET_BIT_HIGH(wi,word,base,limit,label) \
BEGIN \
/* no need to mask the high bits which are shifted */ \
Index actionShift = MPS_WORD_WIDTH - (limit); \
Index actionIndex = MPS_WORD_WIDTH - 1 - actionShift; \
Word actionWord = ((word) & BTMaskLow((base))) << actionShift; \
Count actionMaskWidth = (MPS_WORD_WIDTH >> 1); \
Word actionMask = ~(Word)0 << (MPS_WORD_WIDTH-actionMaskWidth); \
if (actionWord != (Word)0) { \
while (actionMaskWidth != (Count)0) { \
if ((actionWord & actionMask) == (Word)0) { \
actionIndex -= actionMaskWidth; \
actionWord <<= actionMaskWidth; \
} \
actionMaskWidth >>= 1; \
actionMask <<= actionMaskWidth; \
} \
*bfsIndexReturn = ((wi) << MPS_WORD_SHIFT) | actionIndex; \
*bfsFoundReturn = TRUE; \
goto label; \
} \
END
/* BTFindResHigh -- find the highest reset bit in a range
*
* Usage as for BTFindSet
*
* Internally uses the label btFindSetHighLabel
* which must be redefined to avoid a nameclash if the macro is
* used twice in a function scope.
*/
#define BTFindResHigh(foundReturn,indexReturn,bt,base,limit)\
BEGIN \
Bool *bfsFoundReturn = (foundReturn); \
Index *bfsIndexReturn = (indexReturn); \
BT bfsBt = (bt); \
ACT_ON_RANGE_HIGH((base), (limit), SINGLE_FIND_RES_HIGH, \
BITS_FIND_RES_HIGH, WORD_FIND_RES_HIGH); \
*bfsFoundReturn = FALSE; \
btFindResHighLabel:; \
END
#define SINGLE_FIND_RES_HIGH(i) \
if (!BTGet(bfsBt, (i))) { \
*bfsIndexReturn = (i); \
*bfsFoundReturn = TRUE; \
goto btFindResHighLabel; \
}
#define BITS_FIND_RES_HIGH(wi,base,limit) \
BEGIN \
Index bactWi = (wi); \
ACTION_FIND_RES_HIGH(bactWi, bfsBt[bactWi], (base), (limit)); \
END
#define WORD_FIND_RES_HIGH(wi) \
BEGIN \
Index wactWi = (wi); \
ACTION_FIND_RES_HIGH(wactWi, (bfsBt[wactWi]), 0, MPS_WORD_WIDTH); \
END
#define ACTION_FIND_RES_HIGH(wi,word,base,limit) \
ACTION_FIND_SET_BIT_HIGH((wi),~(word),(base),(limit),btFindResHighLabel)
/* BTFindResRange -- find a reset range of bits in a bit table
*
* Starts searching at the low end of the search range.
*
* See <design/bt/#fun.find-res-range>.
*/
static Bool BTFindResRange(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count minLength, Count maxLength)
{
Bool foundRes; /* true if a reset bit is found */
Index resBase; /* base of a candidate reset range */
Index unseenBase; /* base of testing so far */
Index minLimit; /* limit of minimal acceptable range */
Index resLimit; /* limit of search for a candidate range */
AVER(baseReturn != NULL);
AVER(limitReturn != NULL);
AVERT(BT, bt);
AVER(searchBase < searchLimit);
AVER(minLength > 0);
AVER(minLength <= maxLength);
AVER(maxLength <= searchLimit - searchBase);
foundRes = FALSE; /* don't know first reset bit */
minLimit = 0; /* avoid spurious compiler warning */
resBase = searchBase; /* haven't seen anything yet */
unseenBase = searchBase; /* haven't seen anything yet */
resLimit = searchLimit - minLength + 1;
while (resBase < resLimit) {
Index setIndex; /* index of last set bit found */
Bool foundSet = FALSE; /* true if a set bit is found */
/* Find the first reset bit if it's not already known */
if (!foundRes) {
BTFindRes(&foundRes, &resBase, bt, unseenBase, resLimit);
if (!foundRes) {
/* failure */
return FALSE;
}
unseenBase = resBase + 1;
minLimit = resBase + minLength;
}
/* Look to see if there is any set bit in the minimum range */
BTFindSetHigh(&foundSet, &setIndex, bt, unseenBase, minLimit);
if (!foundSet) {
/* Found minimum range. Extend it. */
Index setBase; /* base of search for set bit */
Index setLimit; /* limit search for set bit */
foundSet = FALSE;
setBase = minLimit;
setLimit = resBase + maxLength;
if (setLimit > searchLimit)
setLimit = searchLimit;
if (setLimit > setBase)
BTFindSet(&foundSet, &setIndex, bt, setBase, setLimit);
if (!foundSet)
setIndex = setLimit;
AVER(setIndex - resBase >= minLength);
AVER(setIndex - resBase <= maxLength);
*baseReturn = resBase;
*limitReturn = setIndex;
return TRUE;
} else {
/* Range was too small. Try again */
unseenBase = minLimit;
resBase = setIndex + 1;
if (resBase != minLimit) {
/* Already found the start of next candidate range */
minLimit = resBase + minLength;
/* minLimit might just have gone out of bounds, but in that
* case resBase >= resLimit and so the loop will exit. */
AVER(minLimit <= searchLimit || resBase >= resLimit);
} else {
foundRes = FALSE;
}
}
}
/* failure */
return FALSE;
}
/* BTFindResRangeHigh -- find a reset range of bits in a bit table
*
* Starts searching at the high end of the search range.
*
* See <design/bt/#fun.find-res-range>.
*/
static Bool BTFindResRangeHigh(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count minLength,
Count maxLength)
{
Bool foundRes; /* true if a reset bit is found */
Index resLimit; /* limit of a candidate reset range */
Index resIndex; /* index of highest reset bit found */
Index unseenLimit; /* limit of testing so far */
Index minBase; /* base of minimal acceptable range */
Index resBase; /* base of search for a candidate range */
AVER(baseReturn != NULL);
AVER(limitReturn != NULL);
AVERT(BT, bt);
AVER(searchBase < searchLimit);
AVER(minLength > 0);
AVER(minLength <= maxLength);
AVER(maxLength <= searchLimit - searchBase);
foundRes = FALSE; /* don't know first reset bit */
minBase = 0; /* avoid spurious compiler warning */
resLimit = searchLimit; /* haven't seen anything yet */
unseenLimit = searchLimit; /* haven't seen anything yet */
resBase = searchBase + minLength -1;
while (resLimit > resBase) {
Index setIndex; /* index of first set bit found */
Bool foundSet = FALSE; /* true if a set bit is found */
/* Find the first reset bit if it's not already known */
if (!foundRes) {
/* Look for the limit of a range */
BTFindResHigh(&foundRes, &resIndex, bt, resBase, unseenLimit);
if (!foundRes) {
/* failure */
return FALSE;
}
resLimit = resIndex + 1;
unseenLimit = resIndex;
minBase = resLimit - minLength;
}
/* Look to see if there is any set bit in the minimum range */
BTFindSet(&foundSet, &setIndex, bt, minBase, unseenLimit);
if (!foundSet) {
/* Found minimum range. Extend it. */
Index setBase; /* base of search for set bit */
Index setLimit; /* limit search for set bit */
Index baseIndex; /* base of reset range found */
foundSet = FALSE;
setLimit = minBase;
if ((searchBase + maxLength) > resLimit)
setBase = searchBase;
else
setBase = resLimit - maxLength;
if (setLimit > setBase)
BTFindSetHigh(&foundSet, &setIndex, bt, setBase, setLimit);
if (foundSet)
baseIndex = setIndex+1;
else
baseIndex = setBase;
AVER(resLimit - baseIndex >= minLength);
AVER(resLimit - baseIndex <= maxLength);
*baseReturn = baseIndex;
*limitReturn = resLimit;
return TRUE;
} else {
/* Range was too small. Try again */
unseenLimit = minBase;
resLimit = setIndex;
if (resLimit != minBase) {
/* Already found the start of next candidate range. This wraps
* round if minLength > resLimit (all the variables are
* unsigned so this behaviour is defined), but that means that
* resLimit <= resBase and so the loop will exit. */
AVER(resLimit >= minLength || resLimit <= resBase);
minBase = resLimit - minLength;
} else {
foundRes = FALSE;
}
}
}
/* failure */
return FALSE;
}
/* BTFindLongResRange -- find long range of reset bits in a bit table
*
* See <design/bt/#fun.find-long-res-range>.
*/
Bool BTFindLongResRange(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count length)
{
/* All parameters are checked by BTFindResRange. */
return BTFindResRange(baseReturn, limitReturn,
bt,
searchBase, searchLimit,
length, searchLimit - searchBase);
}
/* BTFindLongResRangeHigh -- find long range of reset bits in a bit table
*
* See <design/bt/#fun.find-long-res-range-high>.
*/
Bool BTFindLongResRangeHigh(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count length)
{
/* All parameters are checked by BTFindResRangeHigh. */
return BTFindResRangeHigh(baseReturn, limitReturn,
bt,
searchBase, searchLimit,
length, searchLimit - searchBase);
}
/* BTFindShortResRange -- find short range of reset bits in a bit table
*
* See <design/bt/#fun.find-short-res-range>.
*/
Bool BTFindShortResRange(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count length)
{
/* All parameters are checked by BTFindResRange. */
return BTFindResRange(baseReturn, limitReturn,
bt,
searchBase, searchLimit,
length, length);
}
/* BTFindShortResRangeHigh -- find short range of reset bits in a bit table
*
* Starts looking from the top of the search range.
*
* See <design/bt/#fun.find-short-res-range-high>.
*/
Bool BTFindShortResRangeHigh(Index *baseReturn, Index *limitReturn,
BT bt,
Index searchBase, Index searchLimit,
Count length)
{
/* All parameters are checked by BTFindResRangeHigh. */
return BTFindResRangeHigh(baseReturn, limitReturn,
bt,
searchBase, searchLimit,
length, length);
}
/* BTRangesSame -- check that a range of bits in two BTs are the same.
*
* See <design/bt/#if.ranges-same>
*/
Bool BTRangesSame(BT comparand, BT comparator, Index base, Index limit)
{
AVERT(BT, comparand);
AVERT(BT, comparator);
AVER(base < limit);
#define SINGLE_RANGES_SAME(i) \
if (BTGet(comparand, (i)) != BTGet(comparator, (i))) \
return FALSE
#define BITS_RANGES_SAME(i,base,limit) \
BEGIN \
Index bactI = (i); \
Word bactMask = BTMask((base),(limit)); \
if ((comparand[bactI] & (bactMask)) != \
(comparator[bactI] & (bactMask))) \
return FALSE; \
END
#define WORD_RANGES_SAME(i) \
BEGIN \
Index wactI = (i); \
if ((comparand[wactI]) != (comparator[wactI])) \
return FALSE; \
END
ACT_ON_RANGE(base, limit, SINGLE_RANGES_SAME,
BITS_RANGES_SAME, WORD_RANGES_SAME);
return TRUE;
}
/* BTCopyInvertRange -- copy a range of bits from one BT to another,
* inverting them as you go.
*
* See <design/bt/#if.copy-invert-range>
*/
void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit)
{
AVERT(BT, fromBT);
AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(base < limit);
#define SINGLE_COPY_INVERT_RANGE(i) \
if (BTGet(fromBT, (i))) \
BTRes(toBT, (i)); \
else \
BTSet(toBT, (i))
#define BITS_COPY_INVERT_RANGE(i,base,limit) \
BEGIN \
Index bactI = (i); \
Word bactMask = BTMask((base),(limit)); \
toBT[bactI] = \
(toBT[bactI] & ~bactMask) | (~fromBT[bactI] & bactMask); \
END
#define WORD_COPY_INVERT_RANGE(i) \
BEGIN \
Index wactI = (i); \
toBT[wactI] = ~fromBT[wactI]; \
END
ACT_ON_RANGE(base, limit, SINGLE_COPY_INVERT_RANGE,
BITS_COPY_INVERT_RANGE, WORD_COPY_INVERT_RANGE);
}
/* BTCopyRange -- copy a range of bits from one BT to another
*
* See <design/bt/#if.copy-range>
*/
void BTCopyRange(BT fromBT, BT toBT, Index base, Index limit)
{
AVERT(BT, fromBT);
AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(base < limit);
#define SINGLE_COPY_RANGE(i) \
if (BTGet(fromBT, (i))) \
BTSet(toBT, (i)); \
else \
BTRes(toBT, (i))
#define BITS_COPY_RANGE(i,base,limit) \
BEGIN \
Index bactI = (i); \
Word bactMask = BTMask((base),(limit)); \
toBT[bactI] = \
(toBT[bactI] & ~bactMask) | (fromBT[bactI] & bactMask); \
END
#define WORD_COPY_RANGE(i) \
BEGIN \
Index wactI = (i); \
toBT[wactI] = fromBT[wactI]; \
END
ACT_ON_RANGE(base, limit, SINGLE_COPY_RANGE,
BITS_COPY_RANGE, WORD_COPY_RANGE);
}
/* BTCopyOffsetRange -- copy a range of bits from one BT to an
* offset range in another BT
*
* .slow: Can't always use ACT_ON_RANGE because word alignment
* may differ for each range. We could try to be smart about
* detecting similar alignment - but we don't.
*
* See <design/bt/#if.copy-offset-range>
*/
void BTCopyOffsetRange(BT fromBT, BT toBT,
Index fromBase, Index fromLimit,
Index toBase, Index toLimit)
{
Index fromBit, toBit;
AVERT(BT, fromBT);
AVERT(BT, toBT);
AVER(fromBT != toBT);
AVER(fromBase < fromLimit);
AVER(toBase < toLimit);
AVER((fromLimit - fromBase) == (toLimit - toBase));
for (fromBit = fromBase, toBit = toBase;
fromBit < fromLimit;
++fromBit, ++toBit) {
if (BTGet(fromBT, fromBit))
BTSet(toBT, toBit);
else
BTRes(toBT, toBit);
}
}
/* BTCountResRange -- count number of reset bits in a range */
Count BTCountResRange(BT bt, Index base, Index limit)
{
Count c = 0;
Index bit;
AVERT(BT, bt);
AVER(base < limit);
for (bit = base; bit < limit; ++bit)
if (!BTGet(bt, bit))
++c;
return c;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2014 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/