xserver: Branch 'master' - 9 commits

Keith Packard keithp at kemper.freedesktop.org
Thu Apr 19 08:42:38 PDT 2012


 Xext/Makefile.am            |    2 
 Xext/hashtable.c            |  291 +++++++++++++++
 Xext/hashtable.h            |  137 +++++++
 Xext/xres.c                 |  829 +++++++++++++++++++++++++++++++++++++++++++-
 composite/compext.c         |   25 +
 configure.ac                |    2 
 dix/resource.c              |  383 +++++++++++++++++++-
 include/protocol-versions.h |    2 
 include/resource.h          |   58 +++
 render/picture.c            |   24 +
 test/Makefile.am            |    3 
 test/hashtabletest.c        |  162 ++++++++
 12 files changed, 1882 insertions(+), 36 deletions(-)

New commits:
commit 3720aa33ee50788dd3d4acc9bbf8dfcb72c8f5ce
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Thu Apr 7 12:53:51 2011 +0300

    Added resourceproto version dependency, >= 1.2.0 now required
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/configure.ac b/configure.ac
index 65d29f2..ea1d286 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"
commit 233eab4d05cae1fdb4129a2e9905961b78693f74
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Wed Dec 8 17:09:30 2010 +0200

    dix: add reference count of the resource to ResourceSizeRec
    
    The ResourceSizeRec now contains the number of references to the
    resource. For example a Pixmap knows this value and it can be useful
    for determining the "weight" of the resource. Typically this value
    is 1.
    
    Reviewed-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/Xext/xres.c b/Xext/xres.c
index b7933f2..ecef0c0 100644
--- a/Xext/xres.c
+++ b/Xext/xres.c
@@ -27,6 +27,7 @@
 #include "list.h"
 #include "misc.h"
 #include <string.h>
+#include "hashtable.h"
 #include "picturestr.h"
 #include "compint.h"
 
@@ -39,6 +40,8 @@ typedef struct {
     /* 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 {
@@ -48,6 +51,41 @@ typedef struct {
     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.
 
@@ -118,6 +156,37 @@ 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)
 {
@@ -293,7 +362,7 @@ static void
 ResFindResourcePixmaps(pointer value, XID id, RESTYPE type, pointer cdata)
 {
     SizeType sizeFunc = GetResourceTypeSizeFunc(type);
-    ResourceSizeRec size = { 0, 0 };
+    ResourceSizeRec size = { 0, 0, 0 };
     unsigned long *bytes = cdata;
 
     sizeFunc(value, id, &size);
@@ -616,6 +685,388 @@ ProcXResQueryClientIds (ClientPtr client)
     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)
 {
@@ -632,8 +1083,7 @@ ProcResDispatch(ClientPtr client)
     case X_XResQueryClientIds:
         return ProcXResQueryClientIds(client);
     case X_XResQueryResourceBytes:
-        /* not implemented yet */
-        return BadRequest;
+        return ProcXResQueryResourceBytes(client);
     default: break;
     }
 
@@ -676,6 +1126,28 @@ SProcXResQueryClientIds (ClientPtr client)
     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)
 {
@@ -694,8 +1166,7 @@ SProcResDispatch (ClientPtr client)
     case X_XResQueryClientIds:
         return SProcXResQueryClientIds(client);
     case X_XResQueryResourceBytes:
-        /* not implemented yet */
-        return BadRequest;
+        return SProcXResQueryResourceBytes(client);
     default: break;
     }
 
diff --git a/dix/resource.c b/dix/resource.c
index cdbe547..2aafa34 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -207,6 +207,7 @@ GetDefaultBytes(pointer value, XID id, ResourceSizePtr size)
 {
     size->resourceSize = 0;
     size->pixmapRefSize = 0;
+    size->refCnt = 1;
 }
 
 /**
@@ -273,6 +274,7 @@ GetPixmapBytes(pointer value, XID id, ResourceSizePtr size)
 
     size->resourceSize = 0;
     size->pixmapRefSize = 0;
+    size->refCnt = pixmap->refcnt;
 
     if (pixmap && pixmap->refcnt)
     {
@@ -298,7 +300,7 @@ static void
 GetWindowBytes(pointer value, XID id, ResourceSizePtr size)
 {
     SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
-    ResourceSizeRec pixmapSize = { 0, 0 };
+    ResourceSizeRec pixmapSize = { 0, 0, 0 };
     WindowPtr window = value;
 
     /* Currently only pixmap bytes are reported to clients. */
@@ -306,6 +308,9 @@ GetWindowBytes(pointer value, XID id, ResourceSizePtr size)
 
     /* Calculate pixmap reference sizes. */
     size->pixmapRefSize = 0;
+
+    size->refCnt = 1;
+
     if (window->backgroundState == BackgroundPixmap)
     {
         PixmapPtr pixmap = window->background.pixmap;
@@ -368,7 +373,7 @@ static void
 GetGcBytes(pointer value, XID id, ResourceSizePtr size)
 {
     SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
-    ResourceSizeRec pixmapSize = { 0, 0 };
+    ResourceSizeRec pixmapSize = { 0, 0, 0 };
     GCPtr gc = value;
 
     /* Currently only pixmap bytes are reported to clients. */
@@ -376,6 +381,8 @@ GetGcBytes(pointer value, XID id, ResourceSizePtr size)
 
     /* Calculate pixmap reference sizes. */
     size->pixmapRefSize = 0;
+
+    size->refCnt = 1;
     if (gc->stipple)
     {
         PixmapPtr pixmap = gc->stipple;
diff --git a/include/resource.h b/include/resource.h
index ae5dd51..19f46d1 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -162,6 +162,8 @@ typedef struct {
     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*/,
diff --git a/render/picture.c b/render/picture.c
index 24b6ba0..da3e499 100644
--- a/render/picture.c
+++ b/render/picture.c
@@ -600,12 +600,14 @@ GetPictureBytes(pointer value, XID id, ResourceSizePtr size)
     /* 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 };
+        ResourceSizeRec pixmapSize = { 0, 0, 0 };
         PixmapPtr pixmap = (PixmapPtr)picture->pDrawable;
         pixmapSizeFunc(pixmap, pixmap->drawable.id, &pixmapSize);
         size->pixmapRefSize += pixmapSize.pixmapRefSize;
commit ccb3e78124fb05defd0c9b438746b79d84dfc3ae
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Tue Dec 14 12:18:23 2010 +0200

    Xext: add a generic hashtable implementation
    
    The generic hashtable implementation adds a key-value container, that
    keeps the key and value inside the hashtable structure and manages
    their memory by itself. This data structure is best suited for
    fixed-length keys and values.
    
    One creates a new hash table with ht_create and disposes it with
    ht_destroy. ht_create accepts the key and value sizes (in bytes) in
    addition to the hashing and comparison functions to use. When adding
    keys with ht_add, they will be copied into the hash and a pointer to
    the value will be returned: data may be put into this structure (or if
    the hash table is to be used as a set, one can just not put anything
    in).
    
    The hash table comes also with one generic hashing function plus a
    comparison function to facilitate ease of use. It also has a custom
    hashing and comparison functions for hashing resource IDs with
    HashXID.
    
    Reviewed-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/Xext/Makefile.am b/Xext/Makefile.am
index cb432e0..5929a3e 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 0000000..2adf92e
--- /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 0000000..5d15984
--- /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/test/Makefile.am b/test/Makefile.am
index eb61470..8c7e412 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 0000000..64c7091
--- /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;
+}
commit a2ac01a8ea8508ed35aa844a589672c1165e05e4
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Wed Apr 6 10:16:53 2011 +0300

    dix: don't use a local wrapper for calling HashResourceID
    
    Calls to Hash(client, id) were replaced with calls directly to
    HashResourceID(id, clientTable[client].hashsize) and the Hash-function
    was removed.
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/dix/resource.c b/dix/resource.c
index 9714061..cdbe547 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -655,13 +655,13 @@ HashResourceID(XID id, int numBits)
         case 11:
             return ((int)(0x7FF & (id ^ (id>>11))));
     }
-    return -1;
-}
-
-static int
-Hash(int client, XID id)
-{
-    return HashResourceID(id, clientTable[client].hashsize);
+    if (numBits >= 11)
+        return ((int)(0x7FF & (id ^ (id>>11))));
+    else
+    {
+        assert(numBits >= 0);
+        return id & ~((~0) << numBits);
+    }
 }
 
 static XID
@@ -672,7 +672,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)
@@ -798,7 +798,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);
@@ -846,7 +846,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;
         }
@@ -878,7 +878,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;
@@ -912,7 +912,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)) {
@@ -947,7 +947,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)) {
@@ -1185,7 +1185,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)
@@ -1218,7 +1218,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/resource.h b/include/resource.h
index e8f2637..ae5dd51 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -275,10 +275,12 @@ extern _X_EXPORT RESTYPE TypeMask;
 /** @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
+    @param numBits The number of bits in the resulting hash. Must be >=0.
 
-    @note This function can only handle INITHASHSIZE..MAXHASHSIZE bit
-    hashes and will return -1 if numBits is not within those bounds.
+    @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);
commit a0b0fb83f91bb82534a0d83fdd6c0222567b098d
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Mon Dec 20 12:58:37 2010 +0200

    dix: add hashing functions to resource.h for others to use.
    
    The public hashing function HashResourceID uses the same hashing
    hashing algorithm as resource.c uses internally, but it provides an
    interface that will is usable by external modules. It provides a
    parameter for the number of bits for the hash, instead of finding the
    size from its internal hash table.
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/dix/resource.c b/dix/resource.c
index 5691b16..9714061 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -636,27 +636,34 @@ 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))));
+    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))));
     }
     return -1;
 }
 
+static int
+Hash(int client, XID id)
+{
+    return HashResourceID(id, clientTable[client].hashsize);
+}
+
 static XID
 AvailableID(int client, XID id, XID maxid, XID goodid)
 {
diff --git a/include/resource.h b/include/resource.h
index 663fac4..e8f2637 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -272,4 +272,15 @@ 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
+
+    @note This function can only handle INITHASHSIZE..MAXHASHSIZE bit
+    hashes and will return -1 if numBits is not within those bounds.
+*/
+extern _X_EXPORT int HashResourceID(XID id,
+                                    int numBits);
+
+#endif /* RESOURCE_H */
commit 3ba0decb4b55a1fd122d269e15b2b2da8ced8624
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Wed Dec 8 15:30:57 2010 +0200

    dix: add a mechanism for iterating through all subresources
    
    The mechanism allows iterating even through subresources that don't
    have specific XID's. When such 'resources' are iterated, the XID for
    them will be zero. A resource type can assign an iteration function
    for its subresources with SetResourceTypeFindSubResFunc; by default
    resources are assumed not to contain subresources.
    
    The purpose of this extension is to enable accurate accounting of
    the resources a resource consumes or uses.
    
    This patch provides the subresource iteration functions for Windows
    and GCs.
    
    Reviewed-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/dix/resource.c b/dix/resource.c
index 89dfb5b..5691b16 100644
--- a/dix/resource.c
+++ b/dix/resource.c
@@ -184,6 +184,7 @@ RESTYPE TypeMask;
 struct ResourceType {
     DeleteType deleteFunc;
     SizeType sizeFunc;
+    FindTypeSubResources findSubResFunc;
     int errorValue;
 };
 
@@ -209,6 +210,25 @@ GetDefaultBytes(pointer value, XID id, ResourceSizePtr size)
 }
 
 /**
+ * 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.
  *
@@ -301,13 +321,45 @@ GetWindowBytes(pointer value, XID id, ResourceSizePtr size)
 }
 
 /**
+ * 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[in] id    Resource ID of graphics context.
  *
  * @param[out] size Estimate of memory usage attributed to a all
  *                  pixmap references of a graphics context.
@@ -338,57 +390,99 @@ GetGcBytes(pointer value, XID id, ResourceSizePtr size)
     }
 }
 
+/**
+ * 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,
                                               },
 };
@@ -420,6 +514,7 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name)
     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 */
@@ -461,6 +556,24 @@ 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)
 {
@@ -871,6 +984,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)
 {
diff --git a/include/resource.h b/include/resource.h
index 8e58952..663fac4 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -171,12 +171,19 @@ typedef void (*SizeType)(pointer /*value*/,
 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*/);
 
@@ -218,6 +225,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 */ );
commit b8d0d19a6d410776b53a41e7cae90f68d4b22bb7
Author: Rami Ylimäki <rami.ylimaki at vincit.fi>
Date:   Wed Oct 27 17:25:50 2010 +0300

    composite: Report pixmap usage of client windows to resource extension.
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>
    Signed-off-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
    Reviewed-by: Mikhail Gusarov <dottedmag at dottedmag.net>
    Reviewed-by: Tiago Vignatti <tiago.vignatti at nokia.com>

diff --git a/Xext/xres.c b/Xext/xres.c
index a073409..b7933f2 100644
--- a/Xext/xres.c
+++ b/Xext/xres.c
@@ -28,6 +28,7 @@
 #include "misc.h"
 #include <string.h>
 #include "picturestr.h"
+#include "compint.h"
 
 /** @brief Holds fragments of responses for ConstructClientIds.
  *
@@ -342,6 +343,14 @@ ResFindPicturePixmaps(pointer value, XID id, pointer cdata)
 #endif
 }
 
+static void
+ResFindCompositeClientWindowPixmaps (pointer value, XID id, pointer cdata)
+{
+#ifdef COMPOSITE
+    ResFindResourcePixmaps(value, id, CompositeClientWindowType, cdata);
+#endif
+}
+
 static int
 ProcXResQueryClientPixmapBytes(ClientPtr client)
 {
@@ -384,7 +393,10 @@ ProcXResQueryClientPixmapBytes(ClientPtr client)
 #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;
diff --git a/composite/compext.c b/composite/compext.c
index 940eed1..1d4d8bf 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)
commit e83388cc70e21e7f377ed2e417d04469e23eb706
Author: Rami Ylimäki <rami.ylimaki at vincit.fi>
Date:   Wed Oct 27 16:59:06 2010 +0300

    render: Report pixmap usage of pictures to resource extension.
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>
    Signed-off-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
    Reviewed-by: Mikhail Gusarov <dottedmag at dottedmag.net>
    Reviewed-by: Tiago Vignatti <tiago.vignatti at nokia.com>

diff --git a/Xext/xres.c b/Xext/xres.c
index 4989d3e..a073409 100644
--- a/Xext/xres.c
+++ b/Xext/xres.c
@@ -27,6 +27,7 @@
 #include "list.h"
 #include "misc.h"
 #include <string.h>
+#include "picturestr.h"
 
 /** @brief Holds fragments of responses for ConstructClientIds.
  *
@@ -288,6 +289,17 @@ ResGetApproxPixmapBytes(PixmapPtr pix)
 }
 
 static void
+ResFindResourcePixmaps(pointer value, XID id, RESTYPE type, pointer cdata)
+{
+    SizeType sizeFunc = GetResourceTypeSizeFunc(type);
+    ResourceSizeRec size = { 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;
@@ -322,6 +334,14 @@ 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 int
 ProcXResQueryClientPixmapBytes(ClientPtr client)
 {
@@ -356,6 +376,13 @@ 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 */
 #endif
diff --git a/dix/resource.c b/dix/resource.c
index 89d0776..89dfb5b 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,212 @@ RESTYPE TypeMask;
 
 struct ResourceType {
     DeleteType deleteFunc;
+    SizeType sizeFunc;
     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;
+}
+
+/**
+ * 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;
+
+    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 };
+    WindowPtr window = value;
+
+    /* Currently only pixmap bytes are reported to clients. */
+    size->resourceSize = 0;
+
+    /* Calculate pixmap reference sizes. */
+    size->pixmapRefSize = 0;
+    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;
+    }
+}
+
+/**
+ * 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 };
+    GCPtr gc = value;
+
+    /* Currently only pixmap bytes are reported to clients. */
+    size->resourceSize = 0;
+
+    /* Calculate pixmap reference sizes. */
+    size->pixmapRefSize = 0;
+    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;
+    }
+}
+
 static struct ResourceType *resourceTypes;
 
 static const struct ResourceType predefTypes[] = {
     [RT_NONE & (RC_LASTPREDEF - 1)] = {
                                        .deleteFunc = (DeleteType) NoopDDA,
+                                       .sizeFunc = GetDefaultBytes,
                                        .errorValue = BadValue,
                                        },
     [RT_WINDOW & (RC_LASTPREDEF - 1)] = {
                                          .deleteFunc = DeleteWindow,
+                                         .sizeFunc = GetWindowBytes,
                                          .errorValue = BadWindow,
                                          },
     [RT_PIXMAP & (RC_LASTPREDEF - 1)] = {
                                          .deleteFunc = dixDestroyPixmap,
+                                         .sizeFunc = GetPixmapBytes,
                                          .errorValue = BadPixmap,
                                          },
     [RT_GC & (RC_LASTPREDEF - 1)] = {
                                      .deleteFunc = FreeGC,
+                                     .sizeFunc = GetGcBytes,
                                      .errorValue = BadGC,
                                      },
     [RT_FONT & (RC_LASTPREDEF - 1)] = {
                                        .deleteFunc = CloseFont,
+                                       .sizeFunc = GetDefaultBytes,
                                        .errorValue = BadFont,
                                        },
     [RT_CURSOR & (RC_LASTPREDEF - 1)] = {
                                          .deleteFunc = FreeCursor,
+                                         .sizeFunc = GetDefaultBytes,
                                          .errorValue = BadCursor,
                                          },
     [RT_COLORMAP & (RC_LASTPREDEF - 1)] = {
                                            .deleteFunc = FreeColormap,
+                                           .sizeFunc = GetDefaultBytes,
                                            .errorValue = BadColor,
                                            },
     [RT_CMAPENTRY & (RC_LASTPREDEF - 1)] = {
                                             .deleteFunc = FreeClientPixels,
+                                            .sizeFunc = GetDefaultBytes,
                                             .errorValue = BadColor,
                                             },
     [RT_OTHERCLIENT & (RC_LASTPREDEF - 1)] = {
                                               .deleteFunc = OtherClientGone,
+                                              .sizeFunc = GetDefaultBytes,
                                               .errorValue = BadValue,
                                               },
     [RT_PASSIVEGRAB & (RC_LASTPREDEF - 1)] = {
                                               .deleteFunc = DeletePassiveGrab,
+                                              .sizeFunc = GetDefaultBytes,
                                               .errorValue = BadValue,
                                               },
 };
@@ -256,6 +419,7 @@ CreateNewResourceType(DeleteType deleteFunc, const char *name)
     lastResourceType = next;
     resourceTypes = types;
     resourceTypes[next].deleteFunc = deleteFunc;
+    resourceTypes[next].sizeFunc = GetDefaultBytes;
     resourceTypes[next].errorValue = BadValue;
 
     /* Called even if name is NULL, to remove any previous entry */
@@ -264,6 +428,39 @@ 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;
+}
+
 void
 SetResourceTypeErrorValue(RESTYPE type, int errorValue)
 {
diff --git a/include/resource.h b/include/resource.h
index 0680570..8e58952 100644
--- a/include/resource.h
+++ b/include/resource.h
@@ -152,12 +152,37 @@ 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;
+} ResourceSizeRec, *ResourceSizePtr;
+
+typedef void (*SizeType)(pointer /*value*/,
+                         XID /*id*/,
+                         ResourceSizePtr /*size*/);
+
 extern _X_EXPORT RESTYPE CreateNewResourceType(DeleteType /*deleteFunc */ ,
                                                const char * /*name */ );
 
 extern _X_EXPORT void SetResourceTypeErrorValue(RESTYPE /*type */ ,
                                                 int /*errorValue */ );
 
+extern _X_EXPORT SizeType GetResourceTypeSizeFunc(
+    RESTYPE /*type*/);
+
+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 */ );
diff --git a/render/picture.c b/render/picture.c
index 2fd13fc..24b6ba0 100644
--- a/render/picture.c
+++ b/render/picture.c
@@ -591,6 +591,27 @@ 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;
+
+    /* Calculate pixmap reference sizes. */
+    size->pixmapRefSize = 0;
+    if (picture->pDrawable && (picture->pDrawable->type == DRAWABLE_PIXMAP))
+    {
+        SizeType pixmapSizeFunc = GetResourceTypeSizeFunc(RT_PIXMAP);
+        ResourceSizeRec pixmapSize = { 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 +623,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;
commit 96864bfa951ea8bf4ab697753fc62c6a97598bc0
Author: Erkki Seppälä <erkki.seppala at vincit.fi>
Date:   Mon Nov 29 12:40:56 2010 +0200

    Implemented first part of XResource extension v1.2: X_XResQueryClientIds
    
    This patch implements a part of the XResource extension v1.2 (as specified in
    http://patchwork.freedesktop.org/patch/2720/ ). The request implemented is
    X_XResQueryClientIds.
    
    This patch depends on the feature introduced by
    1e933665bef26c74196bb7c59910e6a78bcacf0e "dix: Add facilities for
    client ID tracking." .
    
    This latest version also adds Doxygen-formatted comments and takes a better
    notice of coding conventions (as in http://www.x.org/wiki/CodingStyle ).
    
    Signed-off-by: Erkki Seppälä <erkki.seppala at vincit.fi>

diff --git a/Xext/xres.c b/Xext/xres.c
index 9d89b65..4989d3e 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,98 @@
 #include "gcstruct.h"
 #include "modinit.h"
 #include "protocol-versions.h"
+#include "client.h"
+#include "list.h"
+#include "misc.h"
+#include <string.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;
+
+/** @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 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 int
 ProcXResQueryVersion(ClientPtr client)
@@ -288,6 +381,202 @@ 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;
+}
+
 static int
 ProcResDispatch(ClientPtr client)
 {
@@ -301,8 +590,12 @@ ProcResDispatch(ClientPtr client)
         return ProcXResQueryClientResources(client);
     case X_XResQueryClientPixmapBytes:
         return ProcXResQueryClientPixmapBytes(client);
-    default:
-        break;
+    case X_XResQueryClientIds:
+        return ProcXResQueryClientIds(client);
+    case X_XResQueryResourceBytes:
+        /* not implemented yet */
+        return BadRequest;
+    default: break;
     }
 
     return BadRequest;
@@ -335,7 +628,17 @@ SProcXResQueryClientPixmapBytes(ClientPtr client)
 }
 
 static int
-SProcResDispatch(ClientPtr client)
+SProcXResQueryClientIds (ClientPtr client)
+{
+    REQUEST(xXResQueryClientIdsReq);
+
+    REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq);
+    swapl(&stuff->numSpecs);
+    return ProcXResQueryClientIds(client);
+}
+
+static int
+SProcResDispatch (ClientPtr client)
 {
     REQUEST(xReq);
     swaps(&stuff->length);
@@ -349,8 +652,12 @@ SProcResDispatch(ClientPtr client)
         return SProcXResQueryClientResources(client);
     case X_XResQueryClientPixmapBytes:
         return SProcXResQueryClientPixmapBytes(client);
-    default:
-        break;
+    case X_XResQueryClientIds:
+        return SProcXResQueryClientIds(client);
+    case X_XResQueryResourceBytes:
+        /* not implemented yet */
+        return BadRequest;
+    default: break;
     }
 
     return BadRequest;
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 479ac2f..cb8e213 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


More information about the xorg-commit mailing list