caad8b724b
(note this only fixed up the function definitions for the static functions which can not cause any abi incompatibility)
677 lines
17 KiB
C
677 lines
17 KiB
C
/* $Xorg: set.c,v 1.4 2001/02/09 02:05:27 xorgcvs Exp $ */
|
|
|
|
/*
|
|
|
|
Copyright 1995, 1998 The Open Group
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from The Open Group.
|
|
|
|
*/
|
|
/* $XFree86: xc/programs/Xserver/record/set.c,v 1.7 2001/12/14 20:00:37 dawes Exp $ */
|
|
|
|
/*
|
|
|
|
See the header set.h for a description of the set ADT.
|
|
|
|
Implementation Strategy
|
|
|
|
A bit vector is an obvious choice to represent the set, but may take
|
|
too much memory, depending on the numerically largest member in the
|
|
set. One expected common case is for the client to ask for *all*
|
|
protocol. This means it would ask for minor opcodes 0 through 65535.
|
|
Representing this as a bit vector takes 8K -- and there may be
|
|
multiple minor opcode intervals, as many as one per major (extension)
|
|
opcode). In such cases, a list-of-intervals representation would be
|
|
preferable to reduce memory consumption. Both representations will be
|
|
implemented, and RecordCreateSet will decide heuristically which one
|
|
to use based on the set members.
|
|
|
|
Note: When compiling for use in the server, do not use -DTESTING.
|
|
When compiling for stand-alone testing of the set ADT, use -DTESTING.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#ifndef TESTING
|
|
#include "misc.h"
|
|
#else
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
/* stuff that you normally get from the X Server's environment */
|
|
|
|
typedef int Bool;
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
typedef unsigned short CARD16;
|
|
|
|
#define xalloc malloc
|
|
#define xfree free
|
|
#define ALLOCATE_LOCAL malloc
|
|
#define DEALLOCATE_LOCAL free
|
|
|
|
void *Xcalloc(size)
|
|
int size;
|
|
{
|
|
void *p = malloc(size);
|
|
if (p) memset(p, 0, size);
|
|
return p;
|
|
}
|
|
|
|
#ifndef max
|
|
#define max(_a, _b) ( ((_a) > (_b)) ? (_a) : (_b) )
|
|
#endif
|
|
|
|
#endif /* TESTING */
|
|
|
|
#include "set.h"
|
|
|
|
static int
|
|
maxMemberInInterval(RecordSetInterval *pIntervals, int nIntervals)
|
|
{
|
|
int i;
|
|
int maxMember = -1;
|
|
for (i = 0; i < nIntervals; i++)
|
|
{
|
|
if (maxMember < (int)pIntervals[i].last)
|
|
maxMember = pIntervals[i].last;
|
|
}
|
|
return maxMember;
|
|
}
|
|
|
|
static void
|
|
NoopDestroySet(RecordSetPtr pSet)
|
|
{
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/* set operations for bit vector representation */
|
|
|
|
typedef struct {
|
|
RecordSetRec baseSet;
|
|
int maxMember;
|
|
/* followed by the bit vector itself */
|
|
} BitVectorSet, *BitVectorSetPtr;
|
|
|
|
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
|
|
|
static void
|
|
BitVectorDestroySet(RecordSetPtr pSet)
|
|
{
|
|
xfree(pSet);
|
|
}
|
|
|
|
static unsigned long
|
|
BitVectorIsMemberOfSet(RecordSetPtr pSet, int pm)
|
|
{
|
|
BitVectorSetPtr pbvs = (BitVectorSetPtr)pSet;
|
|
unsigned long *pbitvec;
|
|
|
|
if ((int)pm > pbvs->maxMember) return FALSE;
|
|
pbitvec = (unsigned long *)(&pbvs[1]);
|
|
return (pbitvec[pm / BITS_PER_LONG] & ((unsigned long)1 << (pm % BITS_PER_LONG)));
|
|
}
|
|
|
|
|
|
static int
|
|
BitVectorFindBit(RecordSetPtr pSet, int iterbit, Bool bitval)
|
|
{
|
|
BitVectorSetPtr pbvs = (BitVectorSetPtr)pSet;
|
|
unsigned long *pbitvec = (unsigned long *)(&pbvs[1]);
|
|
int startlong;
|
|
int startbit;
|
|
int walkbit;
|
|
int maxMember;
|
|
unsigned long skipval;
|
|
unsigned long bits;
|
|
unsigned long usefulbits;
|
|
|
|
startlong = iterbit / BITS_PER_LONG;
|
|
pbitvec += startlong;
|
|
startbit = startlong * BITS_PER_LONG;
|
|
skipval = bitval ? 0L : ~0L;
|
|
maxMember = pbvs->maxMember;
|
|
|
|
|
|
if (startbit > maxMember) return -1;
|
|
bits = *pbitvec;
|
|
usefulbits = ~(((unsigned long)1 << (iterbit - startbit)) - 1);
|
|
if ( (bits & usefulbits) == (skipval & usefulbits) )
|
|
{
|
|
pbitvec++;
|
|
startbit += BITS_PER_LONG;
|
|
|
|
while (startbit <= maxMember && *pbitvec == skipval)
|
|
{
|
|
pbitvec++;
|
|
startbit += BITS_PER_LONG;
|
|
}
|
|
if (startbit > maxMember) return -1;
|
|
}
|
|
|
|
walkbit = (startbit < iterbit) ? iterbit - startbit : 0;
|
|
|
|
bits = *pbitvec;
|
|
while (walkbit < BITS_PER_LONG && ((!(bits & ((unsigned long)1 << walkbit))) == bitval))
|
|
walkbit++;
|
|
|
|
return startbit + walkbit;
|
|
}
|
|
|
|
|
|
static RecordSetIteratePtr
|
|
BitVectorIterateSet(RecordSetPtr pSet, RecordSetIteratePtr pIter,
|
|
RecordSetInterval *pInterval)
|
|
{
|
|
int iterbit = (int)(long)pIter;
|
|
int b;
|
|
|
|
b = BitVectorFindBit(pSet, iterbit, TRUE);
|
|
if (b == -1) return (RecordSetIteratePtr)0;
|
|
pInterval->first = b;
|
|
|
|
b = BitVectorFindBit(pSet, b, FALSE);
|
|
pInterval->last = (b < 0) ? ((BitVectorSetPtr)pSet)->maxMember : b - 1;
|
|
return (RecordSetIteratePtr)(long)(pInterval->last + 1);
|
|
}
|
|
|
|
RecordSetOperations BitVectorSetOperations = {
|
|
BitVectorDestroySet, BitVectorIsMemberOfSet, BitVectorIterateSet };
|
|
|
|
RecordSetOperations BitVectorNoFreeOperations = {
|
|
NoopDestroySet, BitVectorIsMemberOfSet, BitVectorIterateSet };
|
|
|
|
static int
|
|
BitVectorSetMemoryRequirements(RecordSetInterval *pIntervals, int nIntervals,
|
|
int maxMember, int *alignment)
|
|
{
|
|
int nlongs;
|
|
|
|
*alignment = sizeof(unsigned long);
|
|
nlongs = (maxMember + BITS_PER_LONG) / BITS_PER_LONG;
|
|
return (sizeof(BitVectorSet) + nlongs * sizeof(unsigned long));
|
|
}
|
|
|
|
static RecordSetPtr
|
|
BitVectorCreateSet(RecordSetInterval *pIntervals, int nIntervals,
|
|
void *pMem, int memsize)
|
|
{
|
|
BitVectorSetPtr pbvs;
|
|
int i, j;
|
|
unsigned long *pbitvec;
|
|
|
|
/* allocate all storage needed by this set in one chunk */
|
|
|
|
if (pMem)
|
|
{
|
|
memset(pMem, 0, memsize);
|
|
pbvs = (BitVectorSetPtr)pMem;
|
|
pbvs->baseSet.ops = &BitVectorNoFreeOperations;
|
|
}
|
|
else
|
|
{
|
|
pbvs = (BitVectorSetPtr)Xcalloc(memsize);
|
|
if (!pbvs) return NULL;
|
|
pbvs->baseSet.ops = &BitVectorSetOperations;
|
|
}
|
|
|
|
pbvs->maxMember = maxMemberInInterval(pIntervals, nIntervals);
|
|
|
|
/* fill in the set */
|
|
|
|
pbitvec = (unsigned long *)(&pbvs[1]);
|
|
for (i = 0; i < nIntervals; i++)
|
|
{
|
|
for (j = pIntervals[i].first; j <= (int)pIntervals[i].last; j++)
|
|
{
|
|
pbitvec[j/BITS_PER_LONG] |= ((unsigned long)1 << (j % BITS_PER_LONG));
|
|
}
|
|
}
|
|
return (RecordSetPtr)pbvs;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
/* set operations for interval list representation */
|
|
|
|
typedef struct {
|
|
RecordSetRec baseSet;
|
|
int nIntervals;
|
|
/* followed by the intervals (RecordSetInterval) */
|
|
} IntervalListSet, *IntervalListSetPtr;
|
|
|
|
static void
|
|
IntervalListDestroySet(RecordSetPtr pSet)
|
|
{
|
|
xfree(pSet);
|
|
}
|
|
|
|
static unsigned long
|
|
IntervalListIsMemberOfSet(RecordSetPtr pSet, int pm)
|
|
{
|
|
IntervalListSetPtr prls = (IntervalListSetPtr)pSet;
|
|
RecordSetInterval *pInterval = (RecordSetInterval *)(&prls[1]);
|
|
int hi, lo, probe;
|
|
|
|
/* binary search */
|
|
lo = 0; hi = prls->nIntervals - 1;
|
|
while (lo <= hi)
|
|
{
|
|
probe = (hi + lo) / 2;
|
|
if (pm >= pInterval[probe].first && pm <= pInterval[probe].last) return 1;
|
|
else if (pm < pInterval[probe].first) hi = probe - 1;
|
|
else lo = probe + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static RecordSetIteratePtr
|
|
IntervalListIterateSet(RecordSetPtr pSet, RecordSetIteratePtr pIter,
|
|
RecordSetInterval *pIntervalReturn)
|
|
{
|
|
RecordSetInterval *pInterval = (RecordSetInterval *)pIter;
|
|
IntervalListSetPtr prls = (IntervalListSetPtr)pSet;
|
|
|
|
if (pInterval == NULL)
|
|
{
|
|
pInterval = (RecordSetInterval *)(&prls[1]);
|
|
}
|
|
|
|
if ( (pInterval - (RecordSetInterval *)(&prls[1])) < prls->nIntervals )
|
|
{
|
|
*pIntervalReturn = *pInterval;
|
|
return (RecordSetIteratePtr)(++pInterval);
|
|
}
|
|
else
|
|
return (RecordSetIteratePtr)NULL;
|
|
}
|
|
|
|
RecordSetOperations IntervalListSetOperations = {
|
|
IntervalListDestroySet, IntervalListIsMemberOfSet, IntervalListIterateSet };
|
|
|
|
RecordSetOperations IntervalListNoFreeOperations = {
|
|
NoopDestroySet, IntervalListIsMemberOfSet, IntervalListIterateSet };
|
|
|
|
static int
|
|
IntervalListMemoryRequirements(RecordSetInterval *pIntervals, int nIntervals,
|
|
int maxMember, int *alignment)
|
|
{
|
|
*alignment = sizeof(unsigned long);
|
|
return sizeof(IntervalListSet) + nIntervals * sizeof(RecordSetInterval);
|
|
}
|
|
|
|
static RecordSetPtr
|
|
IntervalListCreateSet(RecordSetInterval *pIntervals, int nIntervals,
|
|
void *pMem, int memsize)
|
|
{
|
|
IntervalListSetPtr prls;
|
|
int i, j, k;
|
|
RecordSetInterval *stackIntervals = NULL;
|
|
CARD16 first;
|
|
|
|
if (nIntervals > 0)
|
|
{
|
|
stackIntervals = (RecordSetInterval *)ALLOCATE_LOCAL(
|
|
sizeof(RecordSetInterval) * nIntervals);
|
|
if (!stackIntervals) return NULL;
|
|
|
|
/* sort intervals, store in stackIntervals (insertion sort) */
|
|
|
|
for (i = 0; i < nIntervals; i++)
|
|
{
|
|
first = pIntervals[i].first;
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
if (first < stackIntervals[j].first)
|
|
break;
|
|
}
|
|
for (k = i; k > j; k--)
|
|
{
|
|
stackIntervals[k] = stackIntervals[k-1];
|
|
}
|
|
stackIntervals[j] = pIntervals[i];
|
|
}
|
|
|
|
/* merge abutting/overlapping intervals */
|
|
|
|
for (i = 0; i < nIntervals - 1; )
|
|
{
|
|
if ( (stackIntervals[i].last + (unsigned int)1) <
|
|
stackIntervals[i + 1].first)
|
|
{
|
|
i++; /* disjoint intervals */
|
|
}
|
|
else
|
|
{
|
|
stackIntervals[i].last = max(stackIntervals[i].last,
|
|
stackIntervals[i + 1].last);
|
|
nIntervals--;
|
|
for (j = i + 1; j < nIntervals; j++)
|
|
stackIntervals[j] = stackIntervals[j + 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* allocate and fill in set structure */
|
|
|
|
if (pMem)
|
|
{
|
|
prls = (IntervalListSetPtr)pMem;
|
|
prls->baseSet.ops = &IntervalListNoFreeOperations;
|
|
}
|
|
else
|
|
{
|
|
prls = (IntervalListSetPtr)
|
|
xalloc(sizeof(IntervalListSet) + nIntervals * sizeof(RecordSetInterval));
|
|
if (!prls) goto bailout;
|
|
prls->baseSet.ops = &IntervalListSetOperations;
|
|
}
|
|
memcpy(&prls[1], stackIntervals, nIntervals * sizeof(RecordSetInterval));
|
|
prls->nIntervals = nIntervals;
|
|
bailout:
|
|
if (stackIntervals) DEALLOCATE_LOCAL(stackIntervals);
|
|
return (RecordSetPtr)prls;
|
|
}
|
|
|
|
#ifdef TESTING
|
|
typedef enum {
|
|
BitVectorImplementation, IntervalListImplementation} RecordSetImplementation;
|
|
|
|
RecordSetImplementation _RecordSetImpl;
|
|
|
|
static void
|
|
_RecordForceSetImplementation(setimpl)
|
|
RecordSetImplementation setimpl;
|
|
{
|
|
_RecordSetImpl = setimpl;
|
|
}
|
|
#endif
|
|
|
|
typedef RecordSetPtr (*RecordCreateSetProcPtr)(
|
|
RecordSetInterval *pIntervals,
|
|
int nIntervals,
|
|
void *pMem,
|
|
int memsize
|
|
);
|
|
|
|
static int
|
|
_RecordSetMemoryRequirements(RecordSetInterval *pIntervals, int nIntervals,
|
|
int *alignment,
|
|
RecordCreateSetProcPtr *ppCreateSet)
|
|
{
|
|
int bmsize, rlsize, bma, rla;
|
|
int maxMember;
|
|
|
|
/* find maximum member of set so we know how big to make the bit vector */
|
|
maxMember = maxMemberInInterval(pIntervals, nIntervals);
|
|
|
|
bmsize = BitVectorSetMemoryRequirements(pIntervals, nIntervals, maxMember,
|
|
&bma);
|
|
rlsize = IntervalListMemoryRequirements(pIntervals, nIntervals, maxMember,
|
|
&rla);
|
|
#ifdef TESTING
|
|
if (_RecordSetImpl == BitVectorImplementation)
|
|
#else
|
|
if ( ( (nIntervals > 1) && (maxMember <= 255) )
|
|
|| (bmsize < rlsize) )
|
|
#endif
|
|
{
|
|
*alignment = bma;
|
|
*ppCreateSet = BitVectorCreateSet;
|
|
return bmsize;
|
|
}
|
|
else
|
|
{
|
|
*alignment = rla;
|
|
*ppCreateSet = IntervalListCreateSet;
|
|
return rlsize;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/* user-visible functions */
|
|
|
|
int
|
|
RecordSetMemoryRequirements(pIntervals, nIntervals, alignment)
|
|
RecordSetInterval *pIntervals;
|
|
int nIntervals;
|
|
int *alignment;
|
|
{
|
|
RecordCreateSetProcPtr pCreateSet;
|
|
return _RecordSetMemoryRequirements(pIntervals, nIntervals, alignment,
|
|
&pCreateSet);
|
|
}
|
|
|
|
RecordSetPtr
|
|
RecordCreateSet(pIntervals, nIntervals, pMem, memsize)
|
|
RecordSetInterval *pIntervals;
|
|
int nIntervals;
|
|
void *pMem;
|
|
int memsize;
|
|
{
|
|
RecordCreateSetProcPtr pCreateSet;
|
|
int alignment;
|
|
int size;
|
|
|
|
size = _RecordSetMemoryRequirements(pIntervals, nIntervals, &alignment,
|
|
&pCreateSet);
|
|
if (pMem)
|
|
{
|
|
if ( ((long)pMem & (alignment-1) ) || memsize < size)
|
|
return NULL;
|
|
}
|
|
return (*pCreateSet)(pIntervals, nIntervals, pMem, size);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef TESTING
|
|
|
|
/*
|
|
|
|
Test Strategy
|
|
|
|
Having two set representations is convenient for testing because we
|
|
can play them against each other. The test code will be able to
|
|
specify which implementation to use. This breaks the encapsulation,
|
|
but that seems acceptable for testing. The crux of the test loop
|
|
looks like this:
|
|
|
|
loop:
|
|
generate random list of Intervals
|
|
|
|
create set A using bit vector implementation
|
|
create set B using Interval list implementation
|
|
|
|
for each possible set member
|
|
if set A and set B disagree on whether this is a member error;
|
|
|
|
iterate over both sets, comparing the intervals returned by each.
|
|
if intervals or number of intervals are different error;
|
|
|
|
iterate over intervals of set A
|
|
for i = interval.first to interval.last
|
|
if i is not a member of set B error;
|
|
|
|
iterate over intervals of set B
|
|
for i = interval.first to interval.last
|
|
if i is not a member of set A error;
|
|
|
|
destroy sets A, B
|
|
|
|
*/
|
|
|
|
int GenerateRandomIntervals(pIntervals, maxintervals)
|
|
RecordSetInterval *pIntervals;
|
|
int maxintervals;
|
|
{
|
|
int i, nIntervals;
|
|
|
|
nIntervals = rand() % maxintervals;
|
|
|
|
for (i = 0; i < nIntervals; i++)
|
|
{
|
|
pIntervals[i].first = rand();
|
|
pIntervals[i].last = pIntervals[i].first + rand();
|
|
}
|
|
return nIntervals;
|
|
}
|
|
|
|
#define MAXINTERVALS 100
|
|
|
|
int main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
RecordSetPtr bs, rs;
|
|
RecordSetInterval br, rr;
|
|
RecordSetIteratePtr bi, ri;
|
|
CARD16 i;
|
|
int testcount;
|
|
RecordSetInterval intervals[MAXINTERVALS];
|
|
int nIntervals;
|
|
int bsize, rsize;
|
|
int balign, ralign;
|
|
int pad;
|
|
|
|
for (testcount = 0; 1; testcount++)
|
|
{
|
|
nIntervals = GenerateRandomIntervals(intervals, MAXINTERVALS);
|
|
printf("%d nIntervals %d\n", testcount, nIntervals);
|
|
|
|
if (testcount & 1)
|
|
{
|
|
_RecordForceSetImplementation(BitVectorImplementation);
|
|
bsize = RecordSetMemoryRequirements(intervals, nIntervals, &balign);
|
|
_RecordForceSetImplementation(IntervalListImplementation);
|
|
rsize = RecordSetMemoryRequirements(intervals, nIntervals, &ralign);
|
|
pad = (ralign - (bsize & (ralign - 1))) & (ralign - 1);
|
|
bs = (RecordSetPtr)xalloc(bsize + pad + rsize );
|
|
if (!bs)
|
|
{
|
|
fprintf(stderr, "%d: failed to alloc memory for sets\n",
|
|
testcount);
|
|
continue;
|
|
}
|
|
rs = (RecordSetPtr)(((char *)bs) + bsize + pad);
|
|
}
|
|
else
|
|
{
|
|
bs = rs = NULL;
|
|
bsize = rsize = 0;
|
|
}
|
|
|
|
_RecordForceSetImplementation(BitVectorImplementation);
|
|
bs = RecordCreateSet(intervals, nIntervals, bs, bsize);
|
|
_RecordForceSetImplementation(IntervalListImplementation);
|
|
rs = RecordCreateSet(intervals, nIntervals, rs, rsize);
|
|
|
|
if (!bs || !rs)
|
|
{
|
|
fprintf(stderr, "%d: failed to create sets\n", testcount);
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < 65535; i++)
|
|
{
|
|
unsigned long b, r;
|
|
|
|
b = RecordIsMemberOfSet(bs, i);
|
|
r = RecordIsMemberOfSet(rs, i);
|
|
if ( (b && !r) || (!b && r) )
|
|
{
|
|
fprintf(stderr, "%d: isMemberOfSet %d\n",
|
|
testcount, (int)i);
|
|
}
|
|
}
|
|
|
|
bi = RecordIterateSet(bs, NULL, &br);
|
|
ri = RecordIterateSet(rs, NULL, &rr);
|
|
|
|
while (bi && ri)
|
|
{
|
|
if ( (rr.first != br.first) || (rr.last != br.last) )
|
|
{
|
|
fprintf(stderr, "%d: iterateSet interval value mismatch\n",
|
|
testcount);
|
|
}
|
|
bi = RecordIterateSet(bs, bi, &br);
|
|
ri = RecordIterateSet(rs, ri, &rr);
|
|
}
|
|
if (bi != ri)
|
|
{
|
|
fprintf(stderr, "%d: iterateSet interval count mismatch\n",
|
|
testcount);
|
|
}
|
|
|
|
|
|
bi = NULL;
|
|
while (bi = RecordIterateSet(bs, bi, &br))
|
|
{
|
|
for (i = br.first; i <= br.last; i++)
|
|
{
|
|
if (!RecordIsMemberOfSet(rs, i))
|
|
{
|
|
fprintf(stderr, "%d: iterateSet b / isMemberOfSet r %d\n",
|
|
testcount, (int)i);
|
|
}
|
|
}
|
|
}
|
|
|
|
ri = NULL;
|
|
while (ri = RecordIterateSet(rs, ri, &rr))
|
|
{
|
|
for (i = rr.first; i <= rr.last; i++)
|
|
{
|
|
if (!RecordIsMemberOfSet(bs, i) )
|
|
{
|
|
fprintf(stderr, "%d: iterateSet r / isMemberOfSet b %d\n",
|
|
testcount, (int)i);
|
|
}
|
|
}
|
|
}
|
|
|
|
RecordDestroySet(bs);
|
|
RecordDestroySet(rs);
|
|
|
|
if (testcount & 1)
|
|
{
|
|
xfree(bs);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* TESTING */
|