summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Xext/Makefile.am2
-rw-r--r--Xext/hashtable.c291
-rw-r--r--Xext/hashtable.h137
-rw-r--r--Xext/xres.c829
-rw-r--r--composite/compext.c25
-rw-r--r--configure.ac2
-rw-r--r--dix/resource.c383
-rw-r--r--include/protocol-versions.h2
-rw-r--r--include/resource.h58
-rw-r--r--render/picture.c24
-rw-r--r--test/Makefile.am3
-rw-r--r--test/hashtabletest.c162
12 files changed, 1882 insertions, 36 deletions
diff --git a/Xext/Makefile.am b/Xext/Makefile.am
index cb432e00e..5929a3e49 100644
--- a/Xext/Makefile.am
+++ b/Xext/Makefile.am
@@ -50,7 +50,7 @@ MODULE_SRCS += $(XV_SRCS)
endif
# XResource extension: lets clients get data about per-client resource usage
-RES_SRCS = xres.c
+RES_SRCS = hashtable.c xres.c
if RES
MODULE_SRCS += $(RES_SRCS)
endif
diff --git a/Xext/hashtable.c b/Xext/hashtable.c
new file mode 100644
index 000000000..2adf92e56
--- /dev/null
+++ b/Xext/hashtable.c
@@ -0,0 +1,291 @@
+#include <stdlib.h>
+#include "misc.h"
+#include "hashtable.h"
+
+/* HashResourceID */
+#include "resource.h"
+
+#define INITHASHSIZE 6
+#define MAXHASHSIZE 11
+
+struct HashTableRec {
+ int keySize;
+ int dataSize;
+
+ int elements; /* number of elements inserted */
+ int bucketBits; /* number of buckets is 1 << bucketBits */
+ struct xorg_list *buckets; /* array of bucket list heads */
+
+ HashFunc hash;
+ HashCompareFunc compare;
+
+ pointer cdata;
+};
+
+typedef struct {
+ struct xorg_list l;
+ void *key;
+ void *data;
+} BucketRec, *BucketPtr;
+
+HashTable
+ht_create(int keySize,
+ int dataSize,
+ HashFunc hash,
+ HashCompareFunc compare,
+ pointer cdata)
+{
+ int c;
+ int numBuckets;
+ HashTable ht = malloc(sizeof(struct HashTableRec));
+
+ if (!ht) {
+ return NULL;
+ }
+
+ ht->keySize = keySize;
+ ht->dataSize = dataSize;
+ ht->hash = hash;
+ ht->compare = compare;
+ ht->elements = 0;
+ ht->bucketBits = INITHASHSIZE;
+ numBuckets = 1 << ht->bucketBits;
+ ht->buckets = malloc(numBuckets * sizeof(*ht->buckets));
+ ht->cdata = cdata;
+
+ if (ht->buckets) {
+ for (c = 0; c < numBuckets; ++c) {
+ xorg_list_init(&ht->buckets[c]);
+ }
+ return ht;
+ } else {
+ free(ht);
+ return NULL;
+ }
+}
+
+void
+ht_destroy(HashTable ht)
+{
+ int c;
+ BucketPtr it, tmp;
+ int numBuckets = 1 << ht->bucketBits;
+ for (c = 0; c < numBuckets; ++c) {
+ xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
+ xorg_list_del(&it->l);
+ free(it);
+ }
+ }
+ free(ht->buckets);
+}
+
+static Bool
+double_size(HashTable ht)
+{
+ struct xorg_list *newBuckets;
+ int numBuckets = 1 << ht->bucketBits;
+ int newBucketBits = ht->bucketBits + 1;
+ int newNumBuckets = 1 << newBucketBits;
+ int c;
+
+ newBuckets = malloc(newNumBuckets * sizeof(*ht->buckets));
+ if (newBuckets) {
+ for (c = 0; c < newNumBuckets; ++c) {
+ xorg_list_init(&newBuckets[c]);
+ }
+
+ for (c = 0; c < numBuckets; ++c) {
+ BucketPtr it, tmp;
+ xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
+ struct xorg_list *newBucket =
+ &newBuckets[ht->hash(ht->cdata, it->key, newBucketBits)];
+ xorg_list_del(&it->l);
+ xorg_list_add(&it->l, newBucket);
+ }
+ }
+ free(ht->buckets);
+
+ ht->buckets = newBuckets;
+ ht->bucketBits = newBucketBits;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+pointer
+ht_add(HashTable ht, pointer key)
+{
+ unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
+ struct xorg_list *bucket = &ht->buckets[index];
+ BucketRec *elem = calloc(1, sizeof(BucketRec));
+ if (!elem) {
+ goto outOfMemory;
+ }
+ elem->key = malloc(ht->keySize);
+ if (!elem->key) {
+ goto outOfMemory;
+ }
+ /* we avoid signaling an out-of-memory error if dataSize is 0 */
+ elem->data = calloc(1, ht->dataSize);
+ if (ht->dataSize && !elem->data) {
+ goto outOfMemory;
+ }
+ xorg_list_add(&elem->l, bucket);
+ ++ht->elements;
+
+ memcpy(elem->key, key, ht->keySize);
+
+ if (ht->elements > 4 * (1 << ht->bucketBits) &&
+ ht->bucketBits < MAXHASHSIZE) {
+ if (!double_size(ht)) {
+ --ht->elements;
+ xorg_list_del(&elem->l);
+ goto outOfMemory;
+ }
+ }
+
+ /* if memory allocation has failed due to dataSize being 0, return
+ a "dummy" pointer pointing at the of the key */
+ return elem->data ? elem->data : ((char*) elem->key + ht->keySize);
+
+ outOfMemory:
+ if (elem) {
+ free(elem->key);
+ free(elem->data);
+ free(elem);
+ }
+
+ return NULL;
+}
+
+void
+ht_remove(HashTable ht, pointer key)
+{
+ unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
+ struct xorg_list *bucket = &ht->buckets[index];
+ BucketPtr it;
+
+ xorg_list_for_each_entry(it, bucket, l) {
+ if (ht->compare(ht->cdata, key, it->key) == 0) {
+ xorg_list_del(&it->l);
+ --ht->elements;
+ free(it->key);
+ free(it->data);
+ free(it);
+ return;
+ }
+ }
+}
+
+pointer
+ht_find(HashTable ht, pointer key)
+{
+ unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
+ struct xorg_list *bucket = &ht->buckets[index];
+ BucketPtr it;
+
+ xorg_list_for_each_entry(it, bucket, l) {
+ if (ht->compare(ht->cdata, key, it->key) == 0) {
+ return it->data ? it->data : ((char*) it->key + ht->keySize);
+ }
+ }
+
+ return NULL;
+}
+
+void
+ht_dump_distribution(HashTable ht)
+{
+ int c;
+ int numBuckets = 1 << ht->bucketBits;
+ for (c = 0; c < numBuckets; ++c) {
+ BucketPtr it;
+ int n = 0;
+
+ xorg_list_for_each_entry(it, &ht->buckets[c], l) {
+ ++n;
+ }
+ printf("%d: %d\n", c, n);
+ }
+}
+
+/* Picked the function from http://burtleburtle.net/bob/hash/doobs.html by
+ Bob Jenkins, which is released in public domain */
+static CARD32
+one_at_a_time_hash(const void *data, int len)
+{
+ CARD32 hash;
+ int i;
+ const char *key = data;
+ for (hash=0, i=0; i<len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+unsigned
+ht_generic_hash(void *cdata, const void *ptr, int numBits)
+{
+ HtGenericHashSetupPtr setup = cdata;
+ return one_at_a_time_hash(ptr, setup->keySize) & ~((~0) << numBits);
+}
+
+int
+ht_generic_compare(void *cdata, const void *l, const void *r)
+{
+ HtGenericHashSetupPtr setup = cdata;
+ return memcmp(l, r, setup->keySize);
+}
+
+unsigned
+ht_resourceid_hash(void * cdata, const void * data, int numBits)
+{
+ const XID* idPtr = data;
+ XID id = *idPtr & RESOURCE_ID_MASK;
+ (void) cdata;
+ return HashResourceID(id, numBits);
+}
+
+int
+ht_resourceid_compare(void* cdata, const void* a, const void* b)
+{
+ const XID* xa = a;
+ const XID* xb = b;
+ (void) cdata;
+ return
+ *xa < *xb ? -1 :
+ *xa > *xb ? 1 :
+ 0;
+}
+
+void
+ht_dump_contents(HashTable ht,
+ void (*print_key)(void *opaque, void *key),
+ void (*print_value)(void *opaque, void *value),
+ void* opaque)
+{
+ int c;
+ int numBuckets = 1 << ht->bucketBits;
+ for (c = 0; c < numBuckets; ++c) {
+ BucketPtr it;
+ int n = 0;
+
+ printf("%d: ", c);
+ xorg_list_for_each_entry(it, &ht->buckets[c], l) {
+ if (n > 0) {
+ printf(", ");
+ }
+ print_key(opaque, it->key);
+ printf("->");
+ print_value(opaque, it->data);
+ ++n;
+ }
+ printf("\n");
+ }
+}
diff --git a/Xext/hashtable.h b/Xext/hashtable.h
new file mode 100644
index 000000000..5d1598425
--- /dev/null
+++ b/Xext/hashtable.h
@@ -0,0 +1,137 @@
+#ifndef HASHTABLE_H
+#define HASHTABLE_H 1
+
+#include <dix-config.h>
+#include <X11/Xfuncproto.h>
+#include <X11/Xdefs.h>
+#include "list.h"
+
+/** @brief A hashing function.
+
+ @param[in/out] cdata Opaque data that can be passed to HtInit that will
+ eventually end up here
+ @param[in] ptr The data to be hashed. The size of the data, if
+ needed, can be configured via a record that can be
+ passed via cdata.
+ @param[in] numBits The number of bits this hash needs to have in the
+ resulting hash
+
+ @return A numBits-bit hash of the data
+*/
+typedef unsigned (*HashFunc)(void * cdata, const void * ptr, int numBits);
+
+/** @brief A comparison function for hashed keys.
+
+ @param[in/out] cdata Opaque data that ca be passed to Htinit that will
+ eventually end up here
+ @param[in] l The left side data to be compared
+ @param[in] r The right side data to be compared
+
+ @return -1 if l < r, 0 if l == r, 1 if l > r
+*/
+typedef int (*HashCompareFunc)(void * cdata, const void * l, const void * r);
+
+struct HashTableRec;
+
+typedef struct HashTableRec *HashTable;
+
+/** @brief A configuration for HtGenericHash */
+typedef struct {
+ int keySize;
+} HtGenericHashSetupRec, *HtGenericHashSetupPtr;
+
+/** @brief ht_create initalizes a hash table for a certain hash table
+ configuration
+
+ @param[out] ht The hash table structure to initialize
+ @param[in] keySize The key size in bytes
+ @param[in] dataSize The data size in bytes
+ @param[in] hash The hash function to use for hashing keys
+ @param[in] compare The comparison function for hashing keys
+ @param[in] cdata Opaque data that will be passed to hash and
+ comparison functions
+*/
+extern _X_EXPORT HashTable ht_create(int keySize,
+ int dataSize,
+ HashFunc hash,
+ HashCompareFunc compare,
+ pointer cdata);
+/** @brief HtDestruct deinitializes the structure. It does not free the
+ memory allocated to HashTableRec
+*/
+extern _X_EXPORT void ht_destroy(HashTable ht);
+
+/** @brief Adds a new key to the hash table. The key will be copied
+ and a pointer to the value will be returned. The data will
+ be initialized with zeroes.
+
+ @param[in/out] ht The hash table
+ @param[key] key The key. The contents of the key will be copied.
+
+ @return On error NULL is returned, otherwise a pointer to the data
+ associated with the newly inserted key.
+
+ @note If dataSize is 0, a pointer to the end of the key may be returned
+ to avoid returning NULL. Obviously the data pointed cannot be
+ modified, as implied by dataSize being 0.
+*/
+extern _X_EXPORT pointer ht_add(HashTable ht, pointer key);
+
+/** @brief Removes a key from the hash table along with its
+ associated data, which will be free'd.
+*/
+extern _X_EXPORT void ht_remove(HashTable ht, pointer key);
+
+/** @brief Finds the associated data of a key from the hash table.
+
+ @return If the key cannot be found, the function returns NULL.
+ Otherwise it returns a pointer to the data associated
+ with the key.
+
+ @note If dataSize == 0, this function may return NULL
+ even if the key has been inserted! If dataSize == NULL,
+ use HtMember instead to determine if a key has been
+ inserted.
+*/
+extern _X_EXPORT pointer ht_find(HashTable ht, pointer key);
+
+/** @brief A generic hash function */
+extern _X_EXPORT unsigned ht_generic_hash(void *cdata,
+ const void *ptr,
+ int numBits);
+
+/** @brief A generic comparison function. It compares data byte-wise. */
+extern _X_EXPORT int ht_generic_compare(void *cdata,
+ const void *l,
+ const void *r);
+
+/** @brief A debugging function that dumps the distribution of the
+ hash table: for each bucket, list the number of elements
+ contained within. */
+extern _X_EXPORT void ht_dump_distribution(HashTable ht);
+
+/** @brief A debugging function that dumps the contents of the hash
+ table: for each bucket, list the elements contained
+ within. */
+extern _X_EXPORT void ht_dump_contents(HashTable ht,
+ void (*print_key)(void *opaque, void *key),
+ void (*print_value)(void *opaque, void *value),
+ void* opaque);
+
+/** @brief A hashing function to be used for hashing resource IDs when
+ used with HashTables. It makes no use of cdata, so that can
+ be NULL. It uses HashXID underneath, and should HashXID be
+ unable to hash the value, it switches into using the generic
+ hash function. */
+extern _X_EXPORT unsigned ht_resourceid_hash(void *cdata,
+ const void * data,
+ int numBits);
+
+/** @brief A comparison function to be used for comparing resource
+ IDs when used with HashTables. It makes no use of cdata,
+ so that can be NULL. */
+extern _X_EXPORT int ht_resourceid_compare(void *cdata,
+ const void *a,
+ const void *b);
+
+#endif // HASHTABLE_H
diff --git a/Xext/xres.c b/Xext/xres.c
index 9d89b6550..ecef0c032 100644
--- a/Xext/xres.c
+++ b/Xext/xres.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <X11/X.h>
#include <X11/Xproto.h>
+#include <assert.h>
#include "misc.h"
#include "os.h"
#include "dixstruct.h"
@@ -22,6 +23,169 @@
#include "gcstruct.h"
#include "modinit.h"
#include "protocol-versions.h"
+#include "client.h"
+#include "list.h"
+#include "misc.h"
+#include <string.h>
+#include "hashtable.h"
+#include "picturestr.h"
+#include "compint.h"
+
+/** @brief Holds fragments of responses for ConstructClientIds.
+ *
+ * note: there is no consideration for data alignment */
+typedef struct {
+ struct xorg_list l;
+ int bytes;
+ /* data follows */
+} FragmentList;
+
+#define FRAGMENT_DATA(ptr) ((void*) ((char*) (ptr) + sizeof(FragmentList)))
+
+/** @brief Holds structure for the generated response to
+ ProcXResQueryClientIds; used by ConstructClientId* -functions */
+typedef struct {
+ int numIds;
+ int resultBytes;
+ struct xorg_list response;
+ int sentClientMasks[MAXCLIENTS];
+} ConstructClientIdCtx;
+
+/** @brief Holds the structure for information required to
+ generate the response to XResQueryResourceBytes. In addition
+ to response it contains information on the query as well,
+ as well as some volatile information required by a few
+ functions that cannot take that information directly
+ via a parameter, as they are called via already-existing
+ higher order functions. */
+typedef struct {
+ ClientPtr sendClient;
+ int numSizes;
+ int resultBytes;
+ struct xorg_list response;
+ int status;
+ long numSpecs;
+ xXResResourceIdSpec *specs;
+ HashTable visitedResources;
+
+ /* Used by AddSubResourceSizeSpec when AddResourceSizeValue is
+ handling crossreferences */
+ HashTable visitedSubResources;
+
+ /* used when ConstructResourceBytesCtx is passed to
+ AddResourceSizeValue2 via FindClientResourcesByType */
+ RESTYPE resType;
+
+ /* used when ConstructResourceBytesCtx is passed to
+ AddResourceSizeValueByResource from ConstructResourceBytesByResource */
+ xXResResourceIdSpec *curSpec;
+
+ /** Used when iterating through a single resource's subresources
+
+ @see AddSubResourceSizeSpec */
+ xXResResourceSizeValue *sizeValue;
+} ConstructResourceBytesCtx;
+
+/** @brief Allocate and add a sequence of bytes at the end of a fragment list.
+ Call DestroyFragments to release the list.
+
+ @param frags A pointer to head of an initialized linked list
+ @param bytes Number of bytes to allocate
+ @return Returns a pointer to the allocated non-zeroed region
+ that is to be filled by the caller. On error (out of memory)
+ returns NULL and makes no changes to the list.
+*/
+static void *
+AddFragment(struct xorg_list *frags, int bytes)
+{
+ FragmentList *f = malloc(sizeof(FragmentList) + bytes);
+ if (!f) {
+ return NULL;
+ } else {
+ f->bytes = bytes;
+ xorg_list_add(&f->l, frags->prev);
+ return (char*) f + sizeof(*f);
+ }
+}
+
+/** @brief Sends all fragments in the list to the client. Does not
+ free anything.
+
+ @param client The client to send the fragments to
+ @param frags The head of the list of fragments
+*/
+static void
+WriteFragmentsToClient(ClientPtr client, struct xorg_list *frags)
+{
+ FragmentList *it;
+ xorg_list_for_each_entry(it, frags, l) {
+ WriteToClient(client, it->bytes, (char*) it + sizeof(*it));
+ }
+}
+
+/** @brief Frees a list of fragments. Does not free() root node.
+
+ @param frags The head of the list of fragments
+*/
+static void
+DestroyFragments(struct xorg_list *frags)
+{
+ FragmentList *it, *tmp;
+ xorg_list_for_each_entry_safe(it, tmp, frags, l) {
+ xorg_list_del(&it->l);
+ free(it);
+ }
+}
+
+/** @brief Constructs a context record for ConstructClientId* functions
+ to use */
+static void
+InitConstructClientIdCtx(ConstructClientIdCtx *ctx)
+{
+ ctx->numIds = 0;
+ ctx->resultBytes = 0;
+ xorg_list_init(&ctx->response);
+ memset(ctx->sentClientMasks, 0, sizeof(ctx->sentClientMasks));
+}
+
+/** @brief Destroys a context record, releases all memory (except the storage
+ for *ctx itself) */
+static void
+DestroyConstructClientIdCtx(ConstructClientIdCtx *ctx)
+{
+ DestroyFragments(&ctx->response);
+}
+
+static Bool
+InitConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx,
+ ClientPtr sendClient,
+ long numSpecs,
+ xXResResourceIdSpec *specs)
+{
+ ctx->sendClient = sendClient;
+ ctx->numSizes = 0;
+ ctx->resultBytes = 0;
+ xorg_list_init(&ctx->response);
+ ctx->status = Success;
+ ctx->numSpecs = numSpecs;
+ ctx->specs = specs;
+ ctx->visitedResources = ht_create(sizeof(XID), 0,
+ ht_resourceid_hash, ht_resourceid_compare,
+ NULL);
+
+ if (!ctx->visitedResources) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+static void
+DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx)
+{
+ DestroyFragments(&ctx->response);
+ ht_destroy(ctx->visitedResources);
+}
static int
ProcXResQueryVersion(ClientPtr client)
@@ -195,6 +359,17 @@ ResGetApproxPixmapBytes(PixmapPtr pix)
}
static void
+ResFindResourcePixmaps(pointer value, XID id, RESTYPE type, pointer cdata)
+{
+ SizeType sizeFunc = GetResourceTypeSizeFunc(type);
+ ResourceSizeRec size = { 0, 0, 0 };
+ unsigned long *bytes = cdata;
+
+ sizeFunc(value, id, &size);
+ *bytes += size.pixmapRefSize;
+}
+
+static void
ResFindPixmaps(pointer value, XID id, pointer cdata)
{
unsigned long *bytes = (unsigned long *) cdata;
@@ -229,6 +404,22 @@ ResFindGCPixmaps(pointer value, XID id, pointer cdata)
*bytes += ResGetApproxPixmapBytes(pGC->tile.pixmap);
}
+static void
+ResFindPicturePixmaps(pointer value, XID id, pointer cdata)
+{
+#ifdef RENDER
+ ResFindResourcePixmaps(value, id, PictureType, cdata);
+#endif
+}
+
+static void
+ResFindCompositeClientWindowPixmaps (pointer value, XID id, pointer cdata)
+{
+#ifdef COMPOSITE
+ ResFindResourcePixmaps(value, id, CompositeClientWindowType, cdata);
+#endif
+}
+
static int
ProcXResQueryClientPixmapBytes(ClientPtr client)
{
@@ -263,8 +454,18 @@ ProcXResQueryClientPixmapBytes(ClientPtr client)
FindClientResourcesByType(clients[clientID], RT_GC,
ResFindGCPixmaps, (pointer) (&bytes));
+#ifdef RENDER
+ /* Render extension picture pixmaps. */
+ FindClientResourcesByType(clients[clientID], PictureType,
+ ResFindPicturePixmaps,
+ (pointer)(&bytes));
+#endif
+
#ifdef COMPOSITE
- /* FIXME: include composite pixmaps too */
+ /* Composite extension client window pixmaps. */
+ FindClientResourcesByType(clients[clientID], CompositeClientWindowType,
+ ResFindCompositeClientWindowPixmaps,
+ (pointer)(&bytes));
#endif
rep.type = X_Reply;
@@ -288,6 +489,584 @@ ProcXResQueryClientPixmapBytes(ClientPtr client)
return Success;
}
+/** @brief Finds out if a client's information need to be put into the
+ response; marks client having been handled, if that is the case.
+
+ @param client The client to send information about
+ @param mask The request mask (0 to send everything, otherwise a
+ bitmask of X_XRes*Mask)
+ @param ctx The context record that tells which clients and id types
+ have been already handled
+ @param sendMask Which id type are we now considering. One of X_XRes*Mask.
+
+ @return Returns TRUE if the client information needs to be on the
+ response, otherwise FALSE.
+*/
+static Bool
+WillConstructMask(ClientPtr client, CARD32 mask,
+ ConstructClientIdCtx *ctx, int sendMask)
+{
+ if ((!mask || (mask & sendMask))
+ && !(ctx->sentClientMasks[client->index] & sendMask)) {
+ ctx->sentClientMasks[client->index] |= sendMask;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/** @brief Constructs a response about a single client, based on a certain
+ client id spec
+
+ @param sendClient Which client wishes to receive this answer. Used for
+ byte endianess.
+ @param client Which client are we considering.
+ @param mask The client id spec mask indicating which information
+ we want about this client.
+ @param ctx The context record containing the constructed response
+ and information on which clients and masks have been
+ already handled.
+
+ @return Return TRUE if everything went OK, otherwise FALSE which indicates
+ a memory allocation problem.
+*/
+static Bool
+ConstructClientIdValue(ClientPtr sendClient, ClientPtr client, CARD32 mask,
+ ConstructClientIdCtx *ctx)
+{
+ xXResClientIdValue rep;
+
+ rep.spec.client = client->clientAsMask;
+ if (client->swapped) {
+ swapl (&rep.spec.client);
+ }
+
+ if (WillConstructMask(client, mask, ctx, X_XResClientXIDMask)) {
+ void *ptr = AddFragment(&ctx->response, sizeof(rep));
+ if (!ptr) {
+ return FALSE;
+ }
+
+ rep.spec.mask = X_XResClientXIDMask;
+ rep.length = 0;
+ if (sendClient->swapped) {
+ swapl (&rep.spec.mask);
+ /* swapl (&rep.length, n); - not required for rep.length = 0 */
+ }
+
+ memcpy(ptr, &rep, sizeof(rep));
+
+ ctx->resultBytes += sizeof(rep);
+ ++ctx->numIds;
+ }
+ if (WillConstructMask(client, mask, ctx, X_XResLocalClientPIDMask)) {
+ pid_t pid = GetClientPid(client);
+
+ if (pid != -1) {
+ void *ptr = AddFragment(&ctx->response,
+ sizeof(rep) + sizeof(CARD32));
+ CARD32 *value = (void*) ((char*) ptr + sizeof(rep));
+
+ if (!ptr) {
+ return FALSE;
+ }
+
+ rep.spec.mask = X_XResLocalClientPIDMask;
+ rep.length = 4;
+
+ if (sendClient->swapped) {
+ swapl (&rep.spec.mask);
+ swapl (&rep.length);
+ }
+
+ if (sendClient->swapped) {
+ swapl (value);
+ }
+ memcpy(ptr, &rep, sizeof(rep));
+ *value = pid;
+
+ ctx->resultBytes += sizeof(rep) + sizeof(CARD32);
+ ++ctx->numIds;
+ }
+ }
+
+ /* memory allocation errors earlier may return with FALSE */
+ return TRUE;
+}
+
+/** @brief Constructs a response about all clients, based on a client id specs
+
+ @param client Which client which we are constructing the response for.
+ @param numSpecs Number of client id specs in specs
+ @param specs Client id specs
+
+ @return Return Success if everything went OK, otherwise a Bad* (currently
+ BadAlloc or BadValue)
+*/
+static int
+ConstructClientIds(ClientPtr client,
+ int numSpecs, xXResClientIdSpec* specs,
+ ConstructClientIdCtx *ctx)
+{
+ int specIdx;
+
+ for (specIdx = 0; specIdx < numSpecs; ++specIdx) {
+ if (specs[specIdx].client == 0) {
+ int c;
+ for (c = 0; c < currentMaxClients; ++c) {
+ if (clients[c]) {
+ if (!ConstructClientIdValue(client, clients[c],
+ specs[specIdx].mask, ctx)) {
+ return BadAlloc;
+ }
+ }
+ }
+ } else {
+ int clientID = CLIENT_ID(specs[specIdx].client);
+
+ if ((clientID < currentMaxClients) && clients[clientID]) {
+ if (!ConstructClientIdValue(client, clients[clientID],
+ specs[specIdx].mask, ctx)) {
+ return BadAlloc;
+ }
+ }
+ }
+ }
+
+ /* memory allocation errors earlier may return with BadAlloc */
+ return Success;
+}
+
+/** @brief Response to XResQueryClientIds request introduced in XResProto v1.2
+
+ @param client Which client which we are constructing the response for.
+
+ @return Returns the value returned from ConstructClientIds with the same
+ semantics
+*/
+static int
+ProcXResQueryClientIds (ClientPtr client)
+{
+ REQUEST(xXResQueryClientIdsReq);
+
+ xXResQueryClientIdsReply rep;
+ xXResClientIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff));
+ int rc;
+ ConstructClientIdCtx ctx;
+
+ InitConstructClientIdCtx(&ctx);
+
+ REQUEST_AT_LEAST_SIZE(xXResQueryClientIdsReq);
+ REQUEST_FIXED_SIZE(xXResQueryClientIdsReq,
+ stuff->numSpecs * sizeof(specs[0]));
+
+ rc = ConstructClientIds(client, stuff->numSpecs, specs, &ctx);
+
+ if (rc == Success) {
+ rep.type = X_Reply;
+ rep.sequenceNumber = client->sequence;
+
+ assert((ctx.resultBytes & 3) == 0);
+ rep.length = bytes_to_int32(ctx.resultBytes);
+ rep.numIds = ctx.numIds;
+
+ if (client->swapped) {
+ swaps (&rep.sequenceNumber);
+ swapl (&rep.length);
+ swapl (&rep.numIds);
+ }
+
+ WriteToClient(client,sizeof(rep),(char*)&rep);
+ WriteFragmentsToClient(client, &ctx.response);
+ }
+
+ DestroyConstructClientIdCtx(&ctx);
+
+ return rc;
+}
+
+/** @brief Swaps xXResResourceIdSpec endianess */
+static void
+SwapXResResourceIdSpec(xXResResourceIdSpec *spec)
+{
+ swapl(&spec->resource);
+ swapl(&spec->type);
+}
+
+/** @brief Swaps xXResResourceSizeSpec endianess */
+static void
+SwapXResResourceSizeSpec(xXResResourceSizeSpec *size)
+{
+ SwapXResResourceIdSpec(&size->spec);
+ swapl(&size->bytes);
+ swapl(&size->refCount);
+ swapl(&size->useCount);
+}
+
+/** @brief Swaps xXResResourceSizeValue endianess */
+static void
+SwapXResResourceSizeValue(xXResResourceSizeValue *rep)
+{
+ SwapXResResourceSizeSpec(&rep->size);
+ swapl(&rep->numCrossReferences);
+}
+
+/** @brief Swaps the response bytes */
+static void
+SwapXResQueryResourceBytes(struct xorg_list *response)
+{
+ struct xorg_list *it = response->next;
+ int c;
+
+ while (it != response) {
+ xXResResourceSizeValue *value = FRAGMENT_DATA(it);
+ it = it->next;
+ for (c = 0; c < value->numCrossReferences; ++c) {
+ xXResResourceSizeSpec *spec = FRAGMENT_DATA(it);
+ SwapXResResourceSizeSpec(spec);
+ it = it->next;
+ }
+ SwapXResResourceSizeValue(value);
+ }
+}
+
+/** @brief Adds xXResResourceSizeSpec describing a resource's size into
+ the buffer contained in the context. The resource is considered
+ to be a subresource.
+
+ @see AddResourceSizeValue
+
+ @param[in] value The X resource object on which to add information
+ about to the buffer
+ @param[in] id The ID of the X resource
+ @param[in] type The type of the X resource
+ @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
+ Void pointer type is used here to satisfy the type
+ FindRes
+*/
+static void
+AddSubResourceSizeSpec(pointer value,
+ XID id,
+ RESTYPE type,
+ pointer cdata)
+{
+ ConstructResourceBytesCtx *ctx = cdata;
+
+ if (ctx->status == Success) {
+ xXResResourceSizeSpec **prevCrossRef =
+ ht_find(ctx->visitedSubResources, &value);
+ if (!prevCrossRef) {
+ Bool ok = TRUE;
+ xXResResourceSizeSpec *crossRef =
+ AddFragment(&ctx->response, sizeof(xXResResourceSizeSpec));
+ ok = ok && crossRef != NULL;
+ if (ok) {
+ xXResResourceSizeSpec **p;
+ p = ht_add(ctx->visitedSubResources, &value);
+ if (!p) {
+ ok = FALSE;
+ } else {
+ *p = crossRef;
+ }
+ }
+ if (!ok) {
+ ctx->status = BadAlloc;
+ } else {
+ SizeType sizeFunc = GetResourceTypeSizeFunc(type);
+ ResourceSizeRec size = { 0, 0, 0 };
+ sizeFunc(value, id, &size);
+
+ crossRef->spec.resource = id;
+ crossRef->spec.type = type;
+ crossRef->bytes = size.resourceSize;
+ crossRef->refCount = size.refCnt;
+ crossRef->useCount = 1;
+
+ ++ctx->sizeValue->numCrossReferences;
+
+ ctx->resultBytes += sizeof(*crossRef);
+ }
+ } else {
+ /* if we have visited the subresource earlier (from current parent
+ resource), just increase its use count by one */
+ ++(*prevCrossRef)->useCount;
+ }
+ }
+}
+
+/** @brief Adds xXResResourceSizeValue describing a resource's size into
+ the buffer contained in the context. In addition, the
+ subresources are iterated and added as xXResResourceSizeSpec's
+ by using AddSubResourceSizeSpec
+
+ @see AddSubResourceSizeSpec
+
+ @param[in] value The X resource object on which to add information
+ about to the buffer
+ @param[in] id The ID of the X resource
+ @param[in] type The type of the X resource
+ @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
+ Void pointer type is used here to satisfy the type
+ FindRes
+*/
+static void
+AddResourceSizeValue(pointer ptr, XID id, RESTYPE type, pointer cdata)
+{
+ ConstructResourceBytesCtx *ctx = cdata;
+ if (ctx->status == Success &&
+ !ht_find(ctx->visitedResources, &id)) {
+ Bool ok = TRUE;
+ HashTable ht;
+ HtGenericHashSetupRec htSetup = {
+ .keySize = sizeof(void*)
+ };
+
+ /* it doesn't matter that we don't undo the work done here
+ * immediately. All but ht_init will be undone at the end
+ * of the request and there can happen no failure after
+ * ht_init, so we don't need to clean it up here in any
+ * special way */
+
+ xXResResourceSizeValue *value =
+ AddFragment(&ctx->response, sizeof(xXResResourceSizeValue));
+ if (!value) {
+ ok = FALSE;
+ }
+ ok = ok && ht_add(ctx->visitedResources, &id);
+ if (ok) {
+ ht = ht_create(htSetup.keySize,
+ sizeof(xXResResourceSizeSpec*),
+ ht_generic_hash, ht_generic_compare,
+ &htSetup);
+ ok = ok && ht;
+ }
+
+ if (!ok) {
+ ctx->status = BadAlloc;
+ } else {
+ SizeType sizeFunc = GetResourceTypeSizeFunc(type);
+ ResourceSizeRec size = { 0, 0, 0 };
+
+ sizeFunc(ptr, id, &size);
+
+ value->size.spec.resource = id;
+ value->size.spec.type = type;
+ value->size.bytes = size.resourceSize;
+ value->size.refCount = size.refCnt;
+ value->size.useCount = 1;
+ value->numCrossReferences = 0;
+
+ ctx->sizeValue = value;
+ ctx->visitedSubResources = ht;
+ FindSubResources(ptr, type, AddSubResourceSizeSpec, ctx);
+ ctx->visitedSubResources = NULL;
+ ctx->sizeValue = NULL;
+
+ ctx->resultBytes += sizeof(*value);
+ ++ctx->numSizes;
+
+ ht_destroy(ht);
+ }
+ }
+}
+
+/** @brief A variant of AddResourceSizeValue that passes the resource type
+ through the context object to satisfy the type FindResType
+
+ @see AddResourceSizeValue
+
+ @param[in] ptr The resource
+ @param[in] id The resource ID
+ @param[in/out] cdata The context object that contains the resource type
+*/
+static void
+AddResourceSizeValueWithResType(pointer ptr, XID id, pointer cdata)
+{
+ ConstructResourceBytesCtx *ctx = cdata;
+ AddResourceSizeValue(ptr, id, ctx->resType, cdata);
+}
+
+/** @brief Adds the information of a resource into the buffer if it matches
+ the match condition.
+
+ @see AddResourceSizeValue
+
+ @param[in] ptr The resource
+ @param[in] id The resource ID
+ @param[in] type The resource type
+ @param[in/out] cdata The context object as a void pointer to satisfy the
+ type FindAllRes
+*/
+static void
+AddResourceSizeValueByResource(pointer ptr, XID id, RESTYPE type, pointer cdata)
+{
+ ConstructResourceBytesCtx *ctx = cdata;
+ xXResResourceIdSpec *spec = ctx->curSpec;
+
+ if ((!spec->type || spec->type == type) &&
+ (!spec->resource || spec->resource == id)) {
+ AddResourceSizeValue(ptr, id, type, ctx);
+ }
+}
+
+/** @brief Add all resources of the client into the result buffer
+ disregarding all those specifications that specify the
+ resource by its ID. Those are handled by
+ ConstructResourceBytesByResource
+
+ @see ConstructResourceBytesByResource
+
+ @param[in] aboutClient Which client is being considered
+ @param[in/out] ctx The context that contains the resource id
+ specifications as well as the result buffer
+*/
+static void
+ConstructClientResourceBytes(ClientPtr aboutClient,
+ ConstructResourceBytesCtx *ctx)
+{
+ int specIdx;
+ for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
+ xXResResourceIdSpec* spec = ctx->specs + specIdx;
+ if (spec->resource) {
+ /* these specs are handled elsewhere */
+ } else if (spec->type) {
+ ctx->resType = spec->type;
+ FindClientResourcesByType(aboutClient, spec->type,
+ AddResourceSizeValueWithResType, ctx);
+ } else {
+ FindAllClientResources(aboutClient, AddResourceSizeValue, ctx);
+ }
+ }
+}
+
+/** @brief Add the sizes of all such resources that can are specified by
+ their ID in the resource id specification. The scan can
+ by limited to a client with the aboutClient parameter
+
+ @see ConstructResourceBytesByResource
+
+ @param[in] aboutClient Which client is being considered. This may be None
+ to mean all clients.
+ @param[in/out] ctx The context that contains the resource id
+ specifications as well as the result buffer. In
+ addition this function uses the curSpec field to
+ keep a pointer to the current resource id
+ specification in it, which can be used by
+ AddResourceSizeValueByResource .
+*/
+static void
+ConstructResourceBytesByResource(XID aboutClient, ConstructResourceBytesCtx *ctx)
+{
+ int specIdx;
+ for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
+ xXResResourceIdSpec *spec = ctx->specs + specIdx;
+ if (spec->resource) {
+ int cid = CLIENT_ID(spec->resource);
+ if (cid < currentMaxClients &&
+ (aboutClient == None || cid == aboutClient)) {
+ ClientPtr client = clients[cid];
+ if (client) {
+ ctx->curSpec = spec;
+ FindAllClientResources(client,
+ AddResourceSizeValueByResource,
+ ctx);
+ }
+ }
+ }
+ }
+}
+
+/** @brief Build the resource size response for the given client
+ (or all if not specified) per the parameters set up
+ in the context object.
+
+ @param[in] aboutClient Which client to consider or None for all clients
+ @param[in/out] ctx The context object that contains the request as well
+ as the response buffer.
+*/
+static int
+ConstructResourceBytes(XID aboutClient,
+ ConstructResourceBytesCtx *ctx)
+{
+ if (aboutClient) {
+ int clientIdx = CLIENT_ID(aboutClient);
+ ClientPtr client = NullClient;
+
+ if ((clientIdx >= currentMaxClients) || !clients[clientIdx]) {
+ ctx->sendClient->errorValue = aboutClient;
+ return BadValue;
+ }
+
+ client = clients[clientIdx];
+
+ ConstructClientResourceBytes(client, ctx);
+ ConstructResourceBytesByResource(aboutClient, ctx);
+ } else {
+ int clientIdx;
+
+ ConstructClientResourceBytes(NULL, ctx);
+
+ for (clientIdx = 0; clientIdx < currentMaxClients; ++clientIdx) {
+ ClientPtr client = clients[clientIdx];
+
+ if (client) {
+ ConstructClientResourceBytes(client, ctx);
+ }
+ }
+
+ ConstructResourceBytesByResource(None, ctx);
+ }
+
+
+ return ctx->status;
+}
+
+/** @brief Implements the XResQueryResourceBytes of XResProto v1.2 */
+static int
+ProcXResQueryResourceBytes (ClientPtr client)
+{
+ REQUEST(xXResQueryResourceBytesReq);
+
+ xXResQueryResourceBytesReply rep;
+ int rc;
+ ConstructResourceBytesCtx ctx;
+
+ REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
+ REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
+ stuff->numSpecs * sizeof(ctx.specs[0]));
+
+ if (!InitConstructResourceBytesCtx(&ctx, client,
+ stuff->numSpecs,
+ (void*) ((char*) stuff +
+ sz_xXResQueryResourceBytesReq))) {
+ return BadAlloc;
+ }
+
+ rc = ConstructResourceBytes(stuff->client, &ctx);
+
+ if (rc == Success) {
+ rep.type = X_Reply;
+ rep.sequenceNumber = client->sequence;
+ rep.numSizes = ctx.numSizes;
+ rep.length = bytes_to_int32(ctx.resultBytes);
+
+ if (client->swapped) {
+ swaps (&rep.sequenceNumber);
+ swapl (&rep.length);
+ swapl (&rep.numSizes);
+
+ SwapXResQueryResourceBytes(&ctx.response);
+ }
+
+ WriteToClient(client,sizeof(rep),(char*)&rep);
+ WriteFragmentsToClient(client, &ctx.response);
+ }
+
+ DestroyConstructResourceBytesCtx(&ctx);
+
+ return rc;
+}
+
static int
ProcResDispatch(ClientPtr client)
{
@@ -301,8 +1080,11 @@ ProcResDispatch(ClientPtr client)
return ProcXResQueryClientResources(client);
case X_XResQueryClientPixmapBytes:
return ProcXResQueryClientPixmapBytes(client);
- default:
- break;
+ case X_XResQueryClientIds:
+ return ProcXResQueryClientIds(client);
+ case X_XResQueryResourceBytes:
+ return ProcXResQueryResourceBytes(client);
+ default: break;
}
return BadRequest;
@@ -335,7 +1117,39 @@ SProcXResQueryClientPixmapBytes(ClientPtr client)
}
static int
-SProcResDispatch(ClientPtr client)
+SProcXResQueryClientIds (ClientPtr client)
+{
+ REQUEST(xXResQueryClientIdsReq);
+
+ REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq);
+ swapl(&stuff->numSpecs);
+ return ProcXResQueryClientIds(client);
+}
+
+/** @brief Implements the XResQueryResourceBytes of XResProto v1.2.
+ This variant byteswaps request contents before issuing the
+ rest of the work to ProcXResQueryResourceBytes */
+static int
+SProcXResQueryResourceBytes (ClientPtr client)
+{
+ REQUEST(xXResQueryResourceBytesReq);
+ int c;
+ xXResResourceIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff));
+
+ swapl(&stuff->numSpecs);
+ REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
+ REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
+ stuff->numSpecs * sizeof(specs[0]));
+
+ for (c = 0; c < stuff->numSpecs; ++c) {
+ SwapXResResourceIdSpec(specs + c);
+ }
+
+ return ProcXResQueryResourceBytes(client);
+}
+
+static int
+SProcResDispatch (ClientPtr client)
{
REQUEST(xReq);
swaps(&stuff->length);
@@ -349,8 +1163,11 @@ SProcResDispatch(ClientPtr client)
return SProcXResQueryClientResources(client);
case X_XResQueryClientPixmapBytes:
return SProcXResQueryClientPixmapBytes(client);
- default:
- break;
+ case X_XResQueryClientIds:
+ return SProcXResQueryClientIds(client);
+ case X_XResQueryResourceBytes:
+ return SProcXResQueryResourceBytes(client);
+ default: break;
}
return BadRequest;
diff --git a/composite/compext.c b/composite/compext.c
index 940eed1fc..1d4d8bf97 100644
--- a/composite/compext.c
+++ b/composite/compext.c
@@ -497,6 +497,28 @@ SProcCompositeDispatch(ClientPtr client)
return BadRequest;
}
+/** @see GetDefaultBytes */
+static void
+GetCompositeClientWindowBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ WindowPtr window = value;
+
+ /* Currently only pixmap bytes are reported to clients. */
+ size->resourceSize = 0;
+
+ /* Calculate pixmap reference sizes. */
+ size->pixmapRefSize = 0;
+ if (window->redirectDraw != RedirectDrawNone)
+ {
+ SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
+ ResourceSizeRec pixmapSize = { 0, 0 };
+ ScreenPtr screen = window->drawable.pScreen;
+ PixmapPtr pixmap = screen->GetWindowPixmap(window);
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+}
+
void
CompositeExtensionInit(void)
{
@@ -529,6 +551,9 @@ CompositeExtensionInit(void)
if (!CompositeClientWindowType)
return;
+ SetResourceTypeSizeFunc(CompositeClientWindowType,
+ GetCompositeClientWindowBytes);
+
CompositeClientSubwindowsType = CreateNewResourceType
(FreeCompositeClientSubwindows, "CompositeClientSubwindows");
if (!CompositeClientSubwindowsType)
diff --git a/configure.ac b/configure.ac
index 65d29f206..ea1d286a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -759,7 +759,7 @@ VIDEOPROTO="videoproto"
COMPOSITEPROTO="compositeproto >= 0.4"
RECORDPROTO="recordproto >= 1.13.99.1"
SCRNSAVERPROTO="scrnsaverproto >= 1.1"
-RESOURCEPROTO="resourceproto"
+RESOURCEPROTO="resourceproto >= 1.2.0"
DRIPROTO="xf86driproto >= 2.1.0"
DRI2PROTO="dri2proto >= 2.6"
XINERAMAPROTO="xineramaproto"
diff --git a/dix/resource.c b/dix/resource.c
index 89d077642..2aafa343c 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -141,6 +141,7 @@ Equipment Corporation.
#include "xace.h"
#include <assert.h>
#include "registry.h"
+#include "gcstruct.h"
#ifdef XSERVER_DTRACE
#include <sys/types.h>
@@ -182,50 +183,313 @@ RESTYPE TypeMask;
struct ResourceType {
DeleteType deleteFunc;
+ SizeType sizeFunc;
+ FindTypeSubResources findSubResFunc;
int errorValue;
};
+/**
+ * Used by all resources that don't specify a function to calculate
+ * resource size. Currently this is used for all resources with
+ * insignificant memory usage.
+ *
+ * @see GetResourceTypeSizeFunc, SetResourceTypeSizeFunc
+ *
+ * @param[in] value Pointer to resource object.
+ *
+ * @param[in] id Resource ID for the object.
+ *
+ * @param[out] size Fill all fields to zero to indicate that size of
+ * resource can't be determined.
+ */
+static void
+GetDefaultBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ size->resourceSize = 0;
+ size->pixmapRefSize = 0;
+ size->refCnt = 1;
+}
+
+/**
+ * Used by all resources that don't specify a function to iterate
+ * through subresources. Currently this is used for all resources with
+ * insignificant memory usage.
+ *
+ * @see FindSubResources, SetResourceTypeFindSubResFunc
+ *
+ * @param[in] value Pointer to resource object.
+ *
+ * @param[in] func Function to call for each subresource.
+
+ * @param[out] cdata Pointer to opaque data.
+ */
+static void
+DefaultFindSubRes(pointer value, FindAllRes func, pointer cdata)
+{
+ /* do nothing */
+}
+
+/**
+ * Calculate drawable size in bytes. Reference counting is not taken
+ * into account.
+ *
+ * @param[in] drawable Pointer to a drawable.
+ *
+ * @return Estimate of total memory usage for the drawable.
+ */
+static unsigned long
+GetDrawableBytes(DrawablePtr drawable)
+{
+ int bytes = 0;
+
+ if (drawable)
+ {
+ int bytesPerPixel = drawable->bitsPerPixel >> 3;
+ int numberOfPixels = drawable->width * drawable->height;
+ bytes = numberOfPixels * bytesPerPixel;
+ }
+
+ return bytes;
+}
+
+/**
+ * Calculate pixmap size in bytes. Reference counting is taken into
+ * account. Any extra data attached by extensions and drivers is not
+ * taken into account. The purpose of this function is to estimate
+ * memory usage that can be attributed to single reference of the
+ * pixmap.
+ *
+ * @param[in] value Pointer to a pixmap.
+ *
+ * @param[in] id Resource ID of pixmap. If the pixmap hasn't been
+ * added as resource, just pass value->drawable.id.
+ *
+ * @param[out] size Estimate of memory usage attributed to a single
+ * pixmap reference.
+ */
+static void
+GetPixmapBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ PixmapPtr pixmap = value;
+
+ size->resourceSize = 0;
+ size->pixmapRefSize = 0;
+ size->refCnt = pixmap->refcnt;
+
+ if (pixmap && pixmap->refcnt)
+ {
+ DrawablePtr drawable = &pixmap->drawable;
+ size->resourceSize = GetDrawableBytes(drawable);
+ size->pixmapRefSize = size->resourceSize / pixmap->refcnt;
+ }
+}
+
+/**
+ * Calculate window size in bytes. The purpose of this function is to
+ * estimate memory usage that can be attributed to all pixmap
+ * references of the window.
+ *
+ * @param[in] value Pointer to a window.
+ *
+ * @param[in] id Resource ID of window.
+ *
+ * @param[out] size Estimate of memory usage attributed to a all
+ * pixmap references of a window.
+ */
+static void
+GetWindowBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
+ ResourceSizeRec pixmapSize = { 0, 0, 0 };
+ WindowPtr window = value;
+
+ /* Currently only pixmap bytes are reported to clients. */
+ size->resourceSize = 0;
+
+ /* Calculate pixmap reference sizes. */
+ size->pixmapRefSize = 0;
+
+ size->refCnt = 1;
+
+ if (window->backgroundState == BackgroundPixmap)
+ {
+ PixmapPtr pixmap = window->background.pixmap;
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+ if (window->border.pixmap && !window->borderIsPixel)
+ {
+ PixmapPtr pixmap = window->border.pixmap;
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+}
+
+/**
+ * Iterate through subresources of a window. The purpose of this
+ * function is to gather accurate information on what resources
+ * a resource uses.
+ *
+ * @note Currently only sub-pixmaps are iterated
+ *
+ * @param[in] value Pointer to a window
+ *
+ * @param[in] func Function to call with each subresource
+ *
+ * @param[out] cdata Pointer to opaque data
+ */
+static void
+FindWindowSubRes(pointer value, FindAllRes func, pointer cdata)
+{
+ WindowPtr window = value;
+
+ /* Currently only pixmap subresources are reported to clients. */
+
+ if (window->backgroundState == BackgroundPixmap)
+ {
+ PixmapPtr pixmap = window->background.pixmap;
+ func(window->background.pixmap, pixmap->drawable.id, RT_PIXMAP, cdata);
+ }
+ if (window->border.pixmap && !window->borderIsPixel)
+ {
+ PixmapPtr pixmap = window->border.pixmap;
+ func(window->background.pixmap, pixmap->drawable.id, RT_PIXMAP, cdata);
+ }
+}
+
+/**
+ * Calculate graphics context size in bytes. The purpose of this
+ * function is to estimate memory usage that can be attributed to all
+ * pixmap references of the graphics context.
+ *
+ * @param[in] value Pointer to a graphics context.
+ *
+ * @param[in] id Resource ID of graphics context.
+ *
+ * @param[out] size Estimate of memory usage attributed to a all
+ * pixmap references of a graphics context.
+ */
+static void
+GetGcBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
+ ResourceSizeRec pixmapSize = { 0, 0, 0 };
+ GCPtr gc = value;
+
+ /* Currently only pixmap bytes are reported to clients. */
+ size->resourceSize = 0;
+
+ /* Calculate pixmap reference sizes. */
+ size->pixmapRefSize = 0;
+
+ size->refCnt = 1;
+ if (gc->stipple)
+ {
+ PixmapPtr pixmap = gc->stipple;
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+ if (gc->tile.pixmap && !gc->tileIsPixel)
+ {
+ PixmapPtr pixmap = gc->tile.pixmap;
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+}
+
+/**
+ * Iterate through subresources of a graphics context. The purpose of
+ * this function is to gather accurate information on what resources a
+ * resource uses.
+ *
+ * @note Currently only sub-pixmaps are iterated
+ *
+ * @param[in] value Pointer to a window
+ *
+ * @param[in] func Function to call with each subresource
+ *
+ * @param[out] cdata Pointer to opaque data
+ */
+static void
+FindGCSubRes(pointer value, FindAllRes func, pointer cdata)
+{
+ GCPtr gc = value;
+
+ /* Currently only pixmap subresources are reported to clients. */
+
+ if (gc->stipple)
+ {
+ PixmapPtr pixmap = gc->stipple;
+ func(pixmap, pixmap->drawable.id, RT_PIXMAP, cdata);
+ }
+ if (gc->tile.pixmap && !gc->tileIsPixel)
+ {
+ PixmapPtr pixmap = gc->tile.pixmap;
+ func(pixmap, pixmap->drawable.id, RT_PIXMAP, cdata);
+ }
+}
+
static struct ResourceType *resourceTypes;
static const struct ResourceType predefTypes[] = {
[RT_NONE & (RC_LASTPREDEF - 1)] = {
.deleteFunc = (DeleteType) NoopDDA,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadValue,
},
[RT_WINDOW & (RC_LASTPREDEF - 1)] = {
.deleteFunc = DeleteWindow,
+ .sizeFunc = GetWindowBytes,
+ .findSubResFunc = FindWindowSubRes,
.errorValue = BadWindow,
},
[RT_PIXMAP & (RC_LASTPREDEF - 1)] = {
.deleteFunc = dixDestroyPixmap,
+ .sizeFunc = GetPixmapBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadPixmap,
},
[RT_GC & (RC_LASTPREDEF - 1)] = {
.deleteFunc = FreeGC,
+ .sizeFunc = GetGcBytes,
+ .findSubResFunc = FindGCSubRes,
.errorValue = BadGC,
},
[RT_FONT & (RC_LASTPREDEF - 1)] = {
.deleteFunc = CloseFont,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadFont,
},
[RT_CURSOR & (RC_LASTPREDEF - 1)] = {
.deleteFunc = FreeCursor,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadCursor,
},
[RT_COLORMAP & (RC_LASTPREDEF - 1)] = {
.deleteFunc = FreeColormap,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadColor,
},
[RT_CMAPENTRY & (RC_LASTPREDEF - 1)] = {
.deleteFunc = FreeClientPixels,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadColor,
},
[RT_OTHERCLIENT & (RC_LASTPREDEF - 1)] = {
.deleteFunc = OtherClientGone,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadValue,
},
[RT_PASSIVEGRAB & (RC_LASTPREDEF - 1)] = {
.deleteFunc = DeletePassiveGrab,
+ .sizeFunc = GetDefaultBytes,
+ .findSubResFunc = DefaultFindSubRes,
.errorValue = BadValue,
},
};
@@ -256,6 +520,8 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name)
lastResourceType = next;
resourceTypes = types;
resourceTypes[next].deleteFunc = deleteFunc;
+ resourceTypes[next].sizeFunc = GetDefaultBytes;
+ resourceTypes[next].findSubResFunc = DefaultFindSubRes;
resourceTypes[next].errorValue = BadValue;
/* Called even if name is NULL, to remove any previous entry */
@@ -264,6 +530,57 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name)
return next;
}
+/**
+ * Get the function used to calculate resource size. Extensions and
+ * drivers need to be able to determine the current size calculation
+ * function if they want to wrap or override it.
+ *
+ * @param[in] type Resource type used in size calculations.
+ *
+ * @return Function to calculate the size of a single
+ * resource.
+ */
+SizeType
+GetResourceTypeSizeFunc(RESTYPE type)
+{
+ return resourceTypes[type & TypeMask].sizeFunc;
+}
+
+/**
+ * Override the default function that calculates resource size. For
+ * example, video driver knows better how to calculate pixmap memory
+ * usage and can therefore wrap or override size calculation for
+ * RT_PIXMAP.
+ *
+ * @param[in] type Resource type used in size calculations.
+ *
+ * @param[in] sizeFunc Function to calculate the size of a single
+ * resource.
+ */
+void
+SetResourceTypeSizeFunc(RESTYPE type, SizeType sizeFunc)
+{
+ resourceTypes[type & TypeMask].sizeFunc = sizeFunc;
+}
+
+/**
+ * Provide a function for iterating the subresources of a resource.
+ * This allows for example more accurate accounting of the (memory)
+ * resources consumed by a resource.
+ *
+ * @see FindSubResources
+ *
+ * @param[in] type Resource type used in size calculations.
+ *
+ * @param[in] sizeFunc Function to calculate the size of a single
+ * resource.
+ */
+void
+SetResourceTypeFindSubResFunc(RESTYPE type, FindTypeSubResources findFunc)
+{
+ resourceTypes[type & TypeMask].findSubResFunc = findFunc;
+}
+
void
SetResourceTypeErrorValue(RESTYPE type, int errorValue)
{
@@ -326,25 +643,32 @@ InitClientResources(ClientPtr client)
return TRUE;
}
-static int
-Hash(int client, XID id)
+int
+HashResourceID(XID id, int numBits)
{
id &= RESOURCE_ID_MASK;
- switch (clientTable[client].hashsize) {
- case 6:
- return ((int) (0x03F & (id ^ (id >> 6) ^ (id >> 12))));
- case 7:
- return ((int) (0x07F & (id ^ (id >> 7) ^ (id >> 13))));
- case 8:
- return ((int) (0x0FF & (id ^ (id >> 8) ^ (id >> 16))));
- case 9:
- return ((int) (0x1FF & (id ^ (id >> 9))));
- case 10:
- return ((int) (0x3FF & (id ^ (id >> 10))));
- case 11:
- return ((int) (0x7FF & (id ^ (id >> 11))));
- }
- return -1;
+ switch (numBits)
+ {
+ case 6:
+ return ((int)(0x03F & (id ^ (id>>6) ^ (id>>12))));
+ case 7:
+ return ((int)(0x07F & (id ^ (id>>7) ^ (id>>13))));
+ case 8:
+ return ((int)(0x0FF & (id ^ (id>>8) ^ (id>>16))));
+ case 9:
+ return ((int)(0x1FF & (id ^ (id>>9))));
+ case 10:
+ return ((int)(0x3FF & (id ^ (id>>10))));
+ case 11:
+ return ((int)(0x7FF & (id ^ (id>>11))));
+ }
+ if (numBits >= 11)
+ return ((int)(0x7FF & (id ^ (id>>11))));
+ else
+ {
+ assert(numBits >= 0);
+ return id & ~((~0) << numBits);
+ }
}
static XID
@@ -355,7 +679,7 @@ AvailableID(int client, XID id, XID maxid, XID goodid)
if ((goodid >= id) && (goodid <= maxid))
return goodid;
for (; id <= maxid; id++) {
- res = clientTable[client].resources[Hash(client, id)];
+ res = clientTable[client].resources[HashResourceID(id, clientTable[client].hashsize)];
while (res && (res->id != id))
res = res->next;
if (!res)
@@ -481,7 +805,7 @@ AddResource(XID id, RESTYPE type, pointer value)
}
if ((rrec->elements >= 4 * rrec->buckets) && (rrec->hashsize < MAXHASHSIZE))
RebuildTable(client);
- head = &rrec->resources[Hash(client, id)];
+ head = &rrec->resources[HashResourceID(id, clientTable[client].hashsize)];
res = malloc(sizeof(ResourceRec));
if (!res) {
(*resourceTypes[type & TypeMask].deleteFunc) (value, id);
@@ -529,7 +853,7 @@ RebuildTable(int client)
for (res = *rptr; res; res = next) {
next = res->next;
res->next = NULL;
- tptr = &tails[Hash(client, res->id)];
+ tptr = &tails[HashResourceID(res->id, clientTable[client].hashsize)];
**tptr = res;
*tptr = &res->next;
}
@@ -561,7 +885,7 @@ FreeResource(XID id, RESTYPE skipDeleteFuncType)
int elements;
if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) {
- head = &clientTable[cid].resources[Hash(cid, id)];
+ head = &clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)];
eltptr = &clientTable[cid].elements;
prev = head;
@@ -595,7 +919,7 @@ FreeResourceByType(XID id, RESTYPE type, Bool skipFree)
ResourcePtr *prev, *head;
if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) {
- head = &clientTable[cid].resources[Hash(cid, id)];
+ head = &clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)];
prev = head;
while ((res = *prev)) {
@@ -630,7 +954,7 @@ ChangeResourceValue(XID id, RESTYPE rtype, pointer value)
ResourcePtr res;
if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets) {
- res = clientTable[cid].resources[Hash(cid, id)];
+ res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)];
for (; res; res = res->next)
if ((res->id == id) && (res->type == rtype)) {
@@ -674,6 +998,15 @@ FindClientResourcesByType(ClientPtr client,
}
}
+void FindSubResources(pointer resource,
+ RESTYPE type,
+ FindAllRes func,
+ pointer cdata)
+{
+ struct ResourceType rtype = resourceTypes[type & TypeMask];
+ rtype.findSubResFunc(resource, func, cdata);
+}
+
void
FindAllClientResources(ClientPtr client, FindAllRes func, pointer cdata)
{
@@ -859,7 +1192,7 @@ dixLookupResourceByType(pointer *result, XID id, RESTYPE rtype,
return BadImplementation;
if ((cid < MAXCLIENTS) && clientTable[cid].buckets) {
- res = clientTable[cid].resources[Hash(cid, id)];
+ res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)];
for (; res; res = res->next)
if (res->id == id && res->type == rtype)
@@ -892,7 +1225,7 @@ dixLookupResourceByClass(pointer *result, XID id, RESTYPE rclass,
*result = NULL;
if ((cid < MAXCLIENTS) && clientTable[cid].buckets) {
- res = clientTable[cid].resources[Hash(cid, id)];
+ res = clientTable[cid].resources[HashResourceID(id, clientTable[cid].hashsize)];
for (; res; res = res->next)
if (res->id == id && (res->type & rclass))
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 479ac2f77..cb8e213bb 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -135,7 +135,7 @@
/* Resource */
#define SERVER_XRES_MAJOR_VERSION 1
-#define SERVER_XRES_MINOR_VERSION 0
+#define SERVER_XRES_MINOR_VERSION 2
/* XvMC */
#define SERVER_XVMC_MAJOR_VERSION 1
diff --git a/include/resource.h b/include/resource.h
index 068057013..19f46d108 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -152,12 +152,46 @@ typedef Bool (*FindComplexResType) (pointer /*value */ ,
XID /*id */ ,
pointer /*cdata */ );
+/* Structure for estimating resource memory usage. Memory usage
+ * consists of space allocated for the resource itself and of
+ * references to other resources. Currently the most important use for
+ * this structure is to estimate pixmap usage of different resources
+ * more accurately. */
+typedef struct {
+ /* Size of resource itself. Zero if not implemented. */
+ unsigned long resourceSize;
+ /* Size attributed to pixmap references from the resource. */
+ unsigned long pixmapRefSize;
+ /* Number of references to this resource; typically 1 */
+ unsigned long refCnt;
+} ResourceSizeRec, *ResourceSizePtr;
+
+typedef void (*SizeType)(pointer /*value*/,
+ XID /*id*/,
+ ResourceSizePtr /*size*/);
+
extern _X_EXPORT RESTYPE CreateNewResourceType(DeleteType /*deleteFunc */ ,
const char * /*name */ );
+typedef void (*FindTypeSubResources)(pointer /* value */,
+ FindAllRes /* func */,
+ pointer /* cdata */);
+
extern _X_EXPORT void SetResourceTypeErrorValue(RESTYPE /*type */ ,
int /*errorValue */ );
+extern _X_EXPORT SizeType GetResourceTypeSizeFunc(
+ RESTYPE /*type*/);
+
+extern _X_EXPORT void SetResourceTypeFindSubResFunc(
+ RESTYPE /*type*/, FindTypeSubResources /*findFunc*/);
+
+extern _X_EXPORT void SetResourceTypeSizeFunc(
+ RESTYPE /*type*/, SizeType /*sizeFunc*/);
+
+extern _X_EXPORT void SetResourceTypeErrorValue(
+ RESTYPE /*type*/, int /*errorValue*/);
+
extern _X_EXPORT RESTYPE CreateNewResourceClass(void);
extern _X_EXPORT Bool InitClientResources(ClientPtr /*client */ );
@@ -193,6 +227,15 @@ extern _X_EXPORT void FindAllClientResources(ClientPtr /*client */ ,
FindAllRes /*func */ ,
pointer /*cdata */ );
+/** @brief Iterate through all subresources of a resource.
+
+ @note The XID argument provided to the FindAllRes function
+ may be 0 for subresources that don't have an XID */
+extern _X_EXPORT void FindSubResources(pointer /*resource*/,
+ RESTYPE /*type*/,
+ FindAllRes /*func*/,
+ pointer /*cdata*/);
+
extern _X_EXPORT void FreeClientNeverRetainResources(ClientPtr /*client */ );
extern _X_EXPORT void FreeClientResources(ClientPtr /*client */ );
@@ -231,4 +274,17 @@ extern _X_EXPORT unsigned int GetXIDList(ClientPtr /*client */ ,
extern _X_EXPORT RESTYPE lastResourceType;
extern _X_EXPORT RESTYPE TypeMask;
-#endif /* RESOURCE_H */
+/** @brief A hashing function to be used for hashing resource IDs
+
+ @param id The resource ID to hash
+ @param numBits The number of bits in the resulting hash. Must be >=0.
+
+ @note This function is really only for handling
+ INITHASHSIZE..MAXHASHSIZE bit hashes, but will handle any number
+ of bits by either masking numBits lower bits of the ID or by
+ providing at most MAXHASHSIZE hashes.
+*/
+extern _X_EXPORT int HashResourceID(XID id,
+ int numBits);
+
+#endif /* RESOURCE_H */
diff --git a/render/picture.c b/render/picture.c
index 2fd13fc37..da3e49936 100644
--- a/render/picture.c
+++ b/render/picture.c
@@ -591,6 +591,29 @@ PictureParseCmapPolicy(const char *name)
return PictureCmapPolicyInvalid;
}
+/** @see GetDefaultBytes */
+static void
+GetPictureBytes(pointer value, XID id, ResourceSizePtr size)
+{
+ PicturePtr picture = value;
+
+ /* Currently only pixmap bytes are reported to clients. */
+ size->resourceSize = 0;
+
+ size->refCnt = picture->refcnt;
+
+ /* Calculate pixmap reference sizes. */
+ size->pixmapRefSize = 0;
+ if (picture->pDrawable && (picture->pDrawable->type == DRAWABLE_PIXMAP))
+ {
+ SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
+ ResourceSizeRec pixmapSize = { 0, 0, 0 };
+ PixmapPtr pixmap = (PixmapPtr)picture->pDrawable;
+ pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
+ size->pixmapRefSize += pixmapSize.pixmapRefSize;
+ }
+}
+
Bool
PictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
{
@@ -602,6 +625,7 @@ PictureInit(ScreenPtr pScreen, PictFormatPtr formats, int nformats)
PictureType = CreateNewResourceType(FreePicture, "PICTURE");
if (!PictureType)
return FALSE;
+ SetResourceTypeSizeFunc(PictureType, GetPictureBytes);
PictFormatType = CreateNewResourceType(FreePictFormat, "PICTFORMAT");
if (!PictFormatType)
return FALSE;
diff --git a/test/Makefile.am b/test/Makefile.am
index eb6147021..8c7e412c3 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -5,7 +5,7 @@ if XORG
# Tests that require at least some DDX functions in order to fully link
# For now, requires xf86 ddx, could be adjusted to use another
SUBDIRS += xi2
-noinst_PROGRAMS += xkb input xtest misc fixes xfree86
+noinst_PROGRAMS += xkb input xtest misc fixes xfree86 hashtabletest
endif
check_LTLIBRARIES = libxservertest.la
@@ -36,6 +36,7 @@ misc_LDADD=$(TEST_LDADD)
fixes_LDADD=$(TEST_LDADD)
xfree86_LDADD=$(TEST_LDADD)
touch_LDADD=$(TEST_LDADD)
+hashtabletest_LDADD=$(TEST_LDADD) ../Xext/hashtable.c
libxservertest_la_LIBADD = $(XSERVER_LIBS)
if XORG
diff --git a/test/hashtabletest.c b/test/hashtabletest.c
new file mode 100644
index 000000000..64c7091fc
--- /dev/null
+++ b/test/hashtabletest.c
@@ -0,0 +1,162 @@
+#include <misc.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "hashtable.h"
+#include "resource.h"
+
+static void
+print_xid(void* ptr, void* v)
+{
+ XID *x = v;
+ printf("%ld", *x);
+}
+
+static void
+print_int(void* ptr, void* v)
+{
+ int *x = v;
+ printf("%d", *x);
+}
+
+static int
+test1(void)
+{
+ HashTable h;
+ XID id;
+ int c;
+ int ok = 1;
+ const int numKeys = 420;
+
+ printf("test1\n");
+ h = ht_create(sizeof(XID), sizeof(int), ht_resourceid_hash, ht_resourceid_compare, NULL);
+
+ for (c = 0; c < numKeys; ++c) {
+ int *dest;
+ id = c;
+ dest = ht_add(h, &id);
+ if (dest) {
+ *dest = 2 * c;
+ }
+ }
+
+ printf("Distribution after insertion\n");
+ ht_dump_distribution(h);
+ ht_dump_contents(h, print_xid, print_int, NULL);
+
+ for (c = 0; c < numKeys; ++c) {
+ XID id = c;
+ int* v = ht_find(h, &id);
+ if (v) {
+ if (*v == 2 * c) {
+ // ok
+ } else {
+ printf("Key %d doesn't have expected value %d but has %d instead\n",
+ c, 2 * c, *v);
+ ok = 0;
+ }
+ } else {
+ ok = 0;
+ printf("Cannot find key %d\n", c);
+ }
+ }
+
+ if (ok) {
+ printf("%d keys inserted and found\n", c);
+
+ for (c = 0; c < numKeys; ++c) {
+ XID id = c;
+ ht_remove(h, &id);
+ }
+
+ printf("Distribution after deletion\n");
+ ht_dump_distribution(h);
+ }
+
+ ht_destroy(h);
+
+ return ok;
+}
+
+static int
+test2(void)
+{
+ HashTable h;
+ XID id;
+ int c;
+ int ok = 1;
+ const int numKeys = 420;
+
+ printf("test2\n");
+ h = ht_create(sizeof(XID), 0, ht_resourceid_hash, ht_resourceid_compare, NULL);
+
+ for (c = 0; c < numKeys; ++c) {
+ id = c;
+ ht_add(h, &id);
+ }
+
+ for (c = 0; c < numKeys; ++c) {
+ XID id = c;
+ if (!ht_find(h, &id)) {
+ ok = 0;
+ printf("Cannot find key %d\n", c);
+ }
+ }
+
+ {
+ XID id = c + 1;
+ if (ht_find(h, &id)) {
+ ok = 0;
+ printf("Could find a key that shouldn't be there\n");
+ }
+ }
+
+ ht_destroy(h);
+
+ if (ok) {
+ printf("Test with empty keys OK\n");
+ } else {
+ printf("Test with empty keys FAILED\n");
+ }
+
+ return ok;
+}
+
+static int
+test3(void)
+{
+ int ok = 1;
+ HtGenericHashSetupRec hashSetup = {
+ .keySize = 4
+ };
+ HashTable h;
+ printf("test3\n");
+ h = ht_create(4, 0, ht_generic_hash, ht_generic_compare, &hashSetup);
+
+ if (!ht_add(h, "helo") ||
+ !ht_add(h, "wrld")) {
+ printf("Could not insert keys\n");
+ }
+
+ if (!ht_find(h, "helo") ||
+ !ht_find(h, "wrld")) {
+ ok = 0;
+ printf("Could not find inserted keys\n");
+ }
+
+ printf("Hash distribution with two strings\n");
+ ht_dump_distribution(h);
+
+ ht_destroy(h);
+
+ return ok;
+}
+
+int
+main(void)
+{
+ int ok = test1();
+ ok = ok && test2();
+ ok = ok && test3();
+
+ return ok ? 0 : 1;
+}