summaryrefslogtreecommitdiff
path: root/record/set.c
diff options
context:
space:
mode:
Diffstat (limited to 'record/set.c')
-rw-r--r--record/set.c702
1 files changed, 702 insertions, 0 deletions
diff --git a/record/set.c b/record/set.c
new file mode 100644
index 000000000..dbaac4f54
--- /dev/null
+++ b/record/set.c
@@ -0,0 +1,702 @@
+/* $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.
+
+*/
+
+/*
+
+ 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.
+
+*/
+
+#ifndef TESTING
+#include "misc.h"
+#else
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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(pIntervals, nIntervals)
+ 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(pSet)
+ 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(pSet)
+ RecordSetPtr pSet;
+{
+ xfree(pSet);
+}
+
+static unsigned long
+BitVectorIsMemberOfSet(pSet, pm)
+ 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(pSet, iterbit, bitval)
+ 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(pSet, pIter, pInterval)
+ RecordSetPtr pSet;
+ RecordSetIteratePtr pIter;
+ RecordSetInterval *pInterval;
+{
+ int iterbit = (int)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)(pInterval->last + 1);
+}
+
+RecordSetOperations BitVectorSetOperations = {
+ BitVectorDestroySet, BitVectorIsMemberOfSet, BitVectorIterateSet };
+
+RecordSetOperations BitVectorNoFreeOperations = {
+ NoopDestroySet, BitVectorIsMemberOfSet, BitVectorIterateSet };
+
+static int
+BitVectorSetMemoryRequirements(pIntervals, nIntervals, maxMember, alignment)
+ 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(pIntervals, nIntervals, pMem, memsize)
+ 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(pSet)
+ RecordSetPtr pSet;
+{
+ xfree(pSet);
+}
+
+static unsigned long
+IntervalListIsMemberOfSet(pSet, pm)
+ 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(pSet, pIter, pIntervalReturn)
+ 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(pIntervals, nIntervals, maxMember, alignment)
+ RecordSetInterval *pIntervals;
+ int nIntervals;
+ int maxMember;
+ int *alignment;
+{
+ *alignment = sizeof(unsigned long);
+ return sizeof(IntervalListSet) + nIntervals * sizeof(RecordSetInterval);
+}
+
+static RecordSetPtr
+IntervalListCreateSet(pIntervals, nIntervals, pMem, memsize)
+ 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)(
+#if NeedNestedPrototypes
+ RecordSetInterval *pIntervals,
+ int nIntervals,
+ void *pMem,
+ int memsize
+#endif
+);
+
+static int
+_RecordSetMemoryRequirements(pIntervals, nIntervals, alignment, ppCreateSet)
+ 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 ( ((int)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 */