424 lines
12 KiB
C
424 lines
12 KiB
C
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "misc.h"
|
|
#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)
|
|
{
|
|
free(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);
|
|
}
|
|
|
|
static RecordSetOperations BitVectorSetOperations = {
|
|
BitVectorDestroySet, BitVectorIsMemberOfSet, BitVectorIterateSet
|
|
};
|
|
|
|
static 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) calloc(1, 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)
|
|
{
|
|
free(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;
|
|
}
|
|
|
|
static RecordSetOperations IntervalListSetOperations = {
|
|
IntervalListDestroySet, IntervalListIsMemberOfSet, IntervalListIterateSet
|
|
};
|
|
|
|
static 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 = xallocarray(nIntervals, sizeof(RecordSetInterval));
|
|
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)
|
|
malloc(sizeof(IntervalListSet) +
|
|
nIntervals * sizeof(RecordSetInterval));
|
|
if (!prls)
|
|
goto bailout;
|
|
prls->baseSet.ops = &IntervalListSetOperations;
|
|
}
|
|
memcpy(&prls[1], stackIntervals, nIntervals * sizeof(RecordSetInterval));
|
|
prls->nIntervals = nIntervals;
|
|
bailout:
|
|
free(stackIntervals);
|
|
return (RecordSetPtr) prls;
|
|
}
|
|
|
|
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);
|
|
if (((nIntervals > 1) && (maxMember <= 255))
|
|
|| (bmsize < rlsize)) {
|
|
*alignment = bma;
|
|
*ppCreateSet = BitVectorCreateSet;
|
|
return bmsize;
|
|
}
|
|
else {
|
|
*alignment = rla;
|
|
*ppCreateSet = IntervalListCreateSet;
|
|
return rlsize;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/* user-visible functions */
|
|
|
|
int
|
|
RecordSetMemoryRequirements(RecordSetInterval * pIntervals, int nIntervals,
|
|
int *alignment)
|
|
{
|
|
RecordCreateSetProcPtr pCreateSet;
|
|
|
|
return _RecordSetMemoryRequirements(pIntervals, nIntervals, alignment,
|
|
&pCreateSet);
|
|
}
|
|
|
|
RecordSetPtr
|
|
RecordCreateSet(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);
|
|
}
|