shithub: freetype+ttf2subf

Download patch

ref: 89f331b7130c22fd93bb1b15381eae9c6da2a8a5
parent: 32174ffba29883763853c45b74fd75ef59508c70
author: David Turner <[email protected]>
date: Sat Dec 20 20:41:32 EST 2003

important bug fixes for new cache code

git/fs: mount .git/fs: mount/attach disallowed
--- a/include/freetype/cache/ftccache.h
+++ b/include/freetype/cache/ftccache.h
@@ -19,8 +19,8 @@
 #ifndef __FTCCACHE_H__
 #define __FTCCACHE_H__
 
+#include FT_CACHE_INTERNAL_MRU_H
 
-
 FT_BEGIN_HEADER
 
   /* handle to cache object */
@@ -52,12 +52,11 @@
   /* structure size should be 20 bytes on 32-bits machines */
   typedef struct  FTC_NodeRec_
   {
-    FTC_Node   mru_next;     /* circular mru list pointer           */
-    FTC_Node   mru_prev;     /* circular mru list pointer           */
-    FTC_Node   link;         /* used for hashing                    */
-    FT_UInt32  hash;         /* used for hashing too                */
-    FT_UShort  cache_index;  /* index of cache the node belongs to  */
-    FT_Short   ref_count;    /* reference count for this node       */
+    FTC_MruNodeRec  mru;          /* circular mru list pointer           */
+    FTC_Node        link;         /* used for hashing                    */
+    FT_UInt32       hash;         /* used for hashing too                */
+    FT_UShort       cache_index;  /* index of cache the node belongs to  */
+    FT_Short        ref_count;    /* reference count for this node       */
 
   } FTC_NodeRec;
 
@@ -65,6 +64,8 @@
 #define FTC_NODE( x )    ( (FTC_Node)(x) )
 #define FTC_NODE_P( x )  ( (FTC_Node*)(x) )
 
+#define FTC_NODE__MRU_NEXT(x)  FTC_NODE( (x)->mru.next )
+#define FTC_NODE__MRU_PREV(x)  FTC_NODE( (x)->mru.prev )
 
   /*************************************************************************/
   /*                                                                       */
--- a/include/freetype/cache/ftcglyph.h
+++ b/include/freetype/cache/ftcglyph.h
@@ -54,12 +54,12 @@
    *
    *
    *  a typical FTC_GCache implementation must provide at least the following:
-   *  
+   *
    *  - FTC_GNode sub-class, e.g. MyNode, with relevant methods, i.e:
    *        my_node_new            ( must call FTC_GNode_Init )
    *        my_node_free           ( must call FTC_GNode_Done )
    *        my_node_compare        ( must call FTC_GNode_Compare )
-   *        my_node_remove_faceid  ( must call ftc_gnode_unselect in case 
+   *        my_node_remove_faceid  ( must call ftc_gnode_unselect in case
    *                                 of match )
    *
    *
@@ -130,13 +130,14 @@
   *
   *  families are implemented as MRU list nodes. They are reference-counted
   */
-  
+
   typedef struct  FTC_FamilyRec_
   {
-    FTC_MruNode       mrunode;
+    FTC_MruNodeRec    mrunode;
     FT_UInt           num_nodes;   /* current number of nodes in this family */
+    FTC_Cache         cache;
     FTC_MruListClass  clazz;
-  
+
   } FTC_FamilyRec, *FTC_Family;
 
 #define  FTC_FAMILY(x)    ( (FTC_Family)(x) )
@@ -166,16 +167,7 @@
 
 
 
-  /* each glyph node contains a 'chunk' of glyph items; */
-  /* translate a glyph index into a chunk index         */
-#define FTC_FAMILY_CHUNK( gfam, gindex )                  \
-          ( ( gindex ) / FTC_FAMILY( gfam )->item_count )
 
-  /* find a glyph index's chunk, and return its start index */
-#define FTC_FAMILY_START( gfam, gindex )       \
-          ( FTC_FAMILY_CHUNK( gfam, gindex ) * \
-            FTC_FAMILY( gfam )->item_count )
-
   /*************************************************************************/
   /*                                                                       */
   /* These functions are exported so that they can be called from          */
@@ -209,6 +201,10 @@
                   FTC_Cache      cache );
 
 
+  FT_EXPORT( void )
+  FTC_Family_Init( FTC_Family  family,
+                   FTC_Cache   cache );
+
   typedef struct FTC_GCacheRec_
   {
     FTC_CacheRec       cache;
@@ -243,6 +239,7 @@
 #define  FTC_GCACHE_CLASS(x)  ((FTC_GCacheClass)(x))
 
 #define  FTC_CACHE__GCACHE_CLASS(x)  FTC_GCACHE_CLASS( FTC_CACHE(x)->org_class )
+#define  FTC_CACHE__FAMILY_CLASS(x)  ((FTC_MruListClass) FTC_CACHE__GCACHE_CLASS(x)->family_class)
 
 
  /* convenience function. use instead of FTC_Manager_Register_Cache */
@@ -259,7 +256,7 @@
                      FTC_Node    *anode );
 
   /* */
- 
+
 FT_END_HEADER
 
 
--- a/include/freetype/cache/ftcmanag.h
+++ b/include/freetype/cache/ftcmanag.h
@@ -71,6 +71,7 @@
 
 FT_BEGIN_HEADER
 
+#define FT_DEBUG_ERROR
 
   /*************************************************************************/
   /*                                                                       */
@@ -96,7 +97,7 @@
     FT_ULong            max_weight;
     FT_ULong            cur_weight;
     FT_UInt             num_nodes;
-    
+
     FTC_Cache           caches[ FTC_MAX_CACHES ];
     FT_UInt             num_caches;
 
@@ -158,7 +159,7 @@
     FT_Int       pixel;
     FT_UInt      x_res;
     FT_UInt      y_res;
-  
+
   } FTC_ScalerRec, *FTC_Scaler;
 
 
@@ -167,14 +168,14 @@
       (a)->width        == (b)->width        &&   \
       (a)->height       == (b)->height       &&   \
       ((a)->pixel != 0) == ((b)->pixel != 0) &&   \
-      ( (a)->pixel ||                   \
-        ( (a)->x_res == (b)->x_res &&   \
+      ( (a)->pixel ||                             \
+        ( (a)->x_res == (b)->x_res &&             \
           (a)->y_res == (b)->y_res ) ) )
 
 #define  FTC_SCALER_HASH(q)                                  \
     ( FTC_FACE_ID_HASH((q)->face_id) +                       \
       (q)->width + (q)->height*7 +                           \
-      (q)->pixel ? ( (q)->x_res*33 ^ (q)->y_res*61 ) : 0 )
+      ( (q)->pixel ? 0 : ( (q)->x_res*33 ^ (q)->y_res*61 ) ) )
 
 
   FT_EXPORT( FT_Error )
--- a/include/freetype/cache/ftcmru.h
+++ b/include/freetype/cache/ftcmru.h
@@ -56,18 +56,33 @@
 
 FT_BEGIN_HEADER
 
-  typedef struct FTC_MruListRec_*              FTC_MruList;
-
   typedef struct FTC_MruNodeRec_*              FTC_MruNode;
 
-  typedef struct FTC_MruListClassRec_ const *  FTC_MruListClass;
-
   typedef struct FTC_MruNodeRec_
   {
     FTC_MruNode   next;
+    FTC_MruNode   prev;
 
   } FTC_MruNodeRec;
 
+
+  FT_EXPORT( void )
+  FTC_MruNode_Prepend( FTC_MruNode  *plist,
+                       FTC_MruNode   node );
+
+  FT_EXPORT( void )
+  FTC_MruNode_Up( FTC_MruNode  *plist,
+                  FTC_MruNode   node );
+
+  FT_EXPORT( void )
+  FTC_MruNode_Remove( FTC_MruNode  *plist,
+                      FTC_MruNode   node );
+
+
+  typedef struct FTC_MruListRec_*              FTC_MruList;
+
+  typedef struct FTC_MruListClassRec_ const *  FTC_MruListClass;
+
   typedef FT_Int       (*FTC_MruNode_CompareFunc)( FTC_MruNode  node,
                                                    FT_Pointer   key );
 
@@ -92,6 +107,7 @@
 
   } FTC_MruListClassRec;
 
+
   typedef struct FTC_MruListRec_
   {
     FT_UInt                  num_nodes;
@@ -119,14 +135,22 @@
   FTC_MruList_Done( FTC_MruList  list );
 
 
-  FT_EXPORT( FT_Error )
+  FT_EXPORT( FTC_MruNode )
   FTC_MruList_Lookup( FTC_MruList   list,
-                      FT_Pointer    key,
-                      FTC_MruNode  *pnode );
+                      FT_Pointer    key );
 
   FT_EXPORT( void )
-  FTC_MruList_Remove( FTC_MruList   list,
-                      FTC_MruNode   node );
+  FTC_MruList_Up( FTC_MruList    list,
+                  FTC_MruNode    node );
+
+  FT_EXPORT( FT_Error )
+  FTC_MruList_New( FTC_MruList    list,
+                   FT_Pointer     key,
+                   FTC_MruNode   *anode );
+
+  FT_EXPORT( void )
+  FTC_MruList_Remove( FTC_MruList  list,
+                      FTC_MruNode  node );
 
   FT_EXPORT( void )
   FTC_MruList_RemoveSelection( FTC_MruList              list,
--- a/src/cache/descrip.mms
+++ b/src/cache/descrip.mms
@@ -20,7 +20,7 @@
 all : $(OBJS)
         library [--.lib]freetype.olb $(OBJS)
 
-ftcache.obj : ftcache.c ftlru.c ftcmanag.c ftccache.c ftcglyph.c ftcimage.c \
-              ftcsbits.c ftccmap.c 
+ftcache.obj : ftcache.c ftcmru.c ftcmanag.c ftccache.c ftcglyph.c ftcimage.c \
+              ftcsbits.c ftccmap.c  ftcbasic.c
 
 # EOF
--- a/src/cache/ftcbasic.c
+++ b/src/cache/ftcbasic.c
@@ -57,20 +57,12 @@
                          FTC_BasicQuery    query,
                          FTC_Cache         cache )
   {
-    ftc_family_init( FTC_FAMILY( family ), cache );
+    FTC_Family_Init( FTC_FAMILY( family ), cache );
     family->attrs = query->attrs;
     return 0;
   }
 
-  static FT_Error
-  ftc_basic_family_reset( FTC_BasicFamily  family,
-                          FTC_BasicQuery   query )
-  {
-    family->attrs = query->attrs;
-    return 0;
-  }
 
-
   static FT_Bool
   ftc_basic_gnode_compare_faceid( FTC_GNode   gnode,
                                   FTC_FaceID  face_id,
@@ -188,7 +180,7 @@
       sizeof( FTC_BasicFamilyRec ),
       (FTC_MruNode_CompareFunc)  ftc_basic_family_compare,
       (FTC_MruNode_InitFunc)     ftc_basic_family_init,
-      (FTC_MruNode_ResetFunc)    ftc_basic_family_reset,
+      (FTC_MruNode_ResetFunc)    NULL,
       (FTC_MruNode_DoneFunc)     NULL
     },
     (FTC_IFamily_LoadGlyphFunc)  ftc_basic_family_load_glyph
@@ -290,7 +282,7 @@
       sizeof( FTC_BasicFamilyRec ),
       (FTC_MruNode_CompareFunc)  ftc_basic_family_compare,
       (FTC_MruNode_InitFunc)     ftc_basic_family_init,
-      (FTC_MruNode_ResetFunc)    ftc_basic_family_reset,
+      (FTC_MruNode_ResetFunc)    NULL,
       (FTC_MruNode_DoneFunc)     NULL
     },
     (FTC_SFamily_GetCountFunc)   ftc_basic_family_get_count,
--- a/src/cache/ftccache.c
+++ b/src/cache/ftccache.c
@@ -29,7 +29,7 @@
 #define FTC_HASH_SUB_LOAD  ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
 
 /* this one _must_ be a power of 2! */
-#define FTC_HASH_INITIAL_SIZE  8
+#define FTC_HASH_INITIAL_SIZE  512
 
 
   /*************************************************************************/
@@ -45,31 +45,7 @@
   ftc_node_mru_link( FTC_Node     node,
                      FTC_Manager  manager )
   {
-    FTC_Node  first = manager->nodes_list;
-
-
-    if ( first )
-    {
-      FTC_Node  last = first->mru_prev;
-
-
-      FT_ASSERT( last->mru_next == first );
-
-      node->mru_prev = last;
-      node->mru_next = first;
-
-      last->mru_next  = node;
-      first->mru_prev = node;
-    }
-    else
-    {
-      FT_ASSERT( manager->num_nodes == 0 );
-
-      node->mru_next = node;
-      node->mru_prev = node;
-    }
-
-    manager->nodes_list = node;
+    FTC_MruNode_Prepend( (FTC_MruNode*)&manager->nodes_list, (FTC_MruNode)node );
     manager->num_nodes++;
   }
 
@@ -79,29 +55,7 @@
   ftc_node_mru_unlink( FTC_Node     node,
                        FTC_Manager  manager )
   {
-    FTC_Node  first = manager->nodes_list;
-    FTC_Node  prev  = node->mru_prev;
-    FTC_Node  next  = node->mru_next;
-
-
-    FT_ASSERT( first != NULL && manager->num_nodes > 0 );
-    FT_ASSERT( next->mru_prev == node );
-    FT_ASSERT( prev->mru_next == node );
-
-    next->mru_prev = prev;
-    prev->mru_next = next;
-
-    if ( node == first )
-    {
-      /* this is the last node in the list; update its head pointer */
-      if ( node == next )
-        manager->nodes_list = NULL;
-      else
-        manager->nodes_list = next;
-    }
-
-    node->mru_next = NULL;
-    node->mru_prev = NULL;
+    FTC_MruNode_Remove( (FTC_MruNode*)&manager->nodes_list, (FTC_MruNode)node );
     manager->num_nodes--;
   }
 
@@ -111,27 +65,7 @@
   ftc_node_mru_up( FTC_Node     node,
                    FTC_Manager  manager )
   {
-    FTC_Node  first = manager->nodes_list;
-
-
-    if ( node != first )
-    {
-      FTC_Node  prev = node->mru_prev;
-      FTC_Node  next = node->mru_next;
-      FTC_Node  last;
-
-
-      prev->mru_next = next;
-      next->mru_prev = prev;
-
-      last            = first->mru_prev;
-      node->mru_next  = first;
-      node->mru_prev  = last;
-      first->mru_prev = node;
-      last->mru_next  = node;
-
-      manager->nodes_list = node;
-    }
+    FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list, (FTC_MruNode)node );
   }
 
 
@@ -142,6 +76,9 @@
   static void
   ftc_cache_resize( FTC_Cache  cache )
   {
+#if 1
+    FT_UNUSED(cache);
+#else
     for (;;)
     {
       FTC_Node   node, *pnode;
@@ -236,6 +173,7 @@
       else /* the hash table is balanced */
         break;
     }
+#endif
   }
 
 
@@ -559,6 +497,10 @@
     }
 
   AddNode:
+    node->ref_count   = 0;
+    node->cache_index = (FT_UInt16) cache->index;
+    node->hash        = hash;
+
    /* don't assume that the cache has the same number of buckets, since
     * our allocation request might have triggered global cache flushing
     */
@@ -623,5 +565,6 @@
 
     ftc_cache_resize( cache );
   }
+
 
 /* END */
--- a/src/cache/ftccache.i
+++ /dev/null
@@ -1,157 +1,0 @@
-/***************************************************************************/
-/*                                                                         */
-/*  ftccache.i                                                             */
-/*                                                                         */
-/*    FreeType template for generic cache.                                 */
-/*                                                                         */
-/*  Copyright 2002 by                                                      */
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
-/*                                                                         */
-/*  This file is part of the FreeType project, and may only be used,       */
-/*  modified, and distributed under the terms of the FreeType project      */
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
-/*  this file you indicate that you have read the license and              */
-/*  understand and accept it fully.                                        */
-/*                                                                         */
-/***************************************************************************/
-
-
-#ifndef GEN_CACHE_FAMILY_COMPARE
-#error "GEN_CACHE_FAMILY_COMPARE not defined in template instantiation"
-#endif
-
-#ifndef GEN_CACHE_NODE_COMPARE
-#error "GEN_CACHE_NODE_COMPARE not defined in template instantiation"
-#endif
-
-
-  static FT_Error
-  GEN_CACHE_LOOKUP( FTC_Cache   cache,
-                    FTC_Query   query,
-                    FTC_Node   *anode )
-  {
-    FT_LruNode  lru;
-    FTC_Family  family;
-    FT_UFast    hash;
-
-
-    query->hash   = 0;
-    query->family = NULL;
-
-    /* XXX: we break encapsulation for the sake of speed! */
-    {
-      /* first of all, find the relevant family */
-      FT_LruList  list  = cache->families;
-      FT_LruNode  fam, *pfam;
-
-
-      pfam = &list->nodes;
-      for (;;)
-      {
-        fam = *pfam;
-        if ( fam == NULL )
-          goto Normal;
-
-        if ( GEN_CACHE_FAMILY_COMPARE( fam, query, list->data ) )
-          break;
-
-        pfam = &fam->next;
-      }
-
-      FT_ASSERT( fam != NULL );
-
-      /* move to top of list when needed */
-      if ( fam != list->nodes )
-      {
-        *pfam       = fam->next;
-        fam->next   = list->nodes;
-        list->nodes = fam;
-      }
-
-      lru = fam;
-    }
-
-    {
-      FTC_Node  node, *pnode, *bucket;
-
-
-      family = (FTC_Family)lru;
-      hash   = query->hash;
-
-      {
-        FT_UInt  idx;
-
-
-        idx = hash & cache->mask;
-        if ( idx < cache->p )
-          idx = hash & ( cache->mask * 2 + 1 );
-
-        bucket  = cache->buckets + idx;
-      }
-
-      pnode = bucket;
-
-      for ( ;; )
-      {
-        node = *pnode;
-        if ( node == NULL )
-          goto Normal;
-
-        if ( node->hash == hash                            &&
-             (FT_UInt)node->fam_index == family->fam_index &&
-             GEN_CACHE_NODE_COMPARE( node, query, cache )  )
-        {
-          /* we place the following out of the loop to make it */
-          /* as small as possible...                           */
-          goto Found;
-        }
-
-        pnode = &node->link;
-      }
-
-  Normal:
-      return ftc_cache_lookup( cache, query, anode );
-
-  Found:
-      /* move to head of bucket list */
-      if ( pnode != bucket )
-      {
-        *pnode     = node->link;
-        node->link = *bucket;
-        *bucket    = node;
-      }
-
-      /* move to head of MRU list */
-      if ( node != cache->manager->nodes_list )
-      {
-        /* XXX: again, this is an inlined version of ftc_node_mru_up */
-        FTC_Manager  manager = cache->manager;
-        FTC_Node     first   = manager->nodes_list;
-        FTC_Node     prev = node->mru_prev;
-        FTC_Node     next = node->mru_next;
-        FTC_Node     last;
-
-
-        prev->mru_next = next;
-        next->mru_prev = prev;
-
-        last            = first->mru_prev;
-        node->mru_next  = first;
-        node->mru_prev  = last;
-        first->mru_prev = node;
-        last->mru_next  = node;
-
-        manager->nodes_list = node;
-      }
-
-      *anode = node;
-      return 0;
-    }
-  }
-
-#undef GEN_CACHE_NODE_COMPARE
-#undef GEN_CACHE_FAMILY_COMPARE
-#undef GEN_CACHE_LOOKUP
-
-
-/* END */
--- a/src/cache/ftcglyph.c
+++ b/src/cache/ftcglyph.c
@@ -26,6 +26,7 @@
 #include "ftcerror.h"
 
 
+
   /* create a new chunk node, setting its cache index and ref count */
   FT_EXPORT_DEF( void )
   FTC_GNode_Init( FTC_GNode   gnode,
@@ -34,6 +35,7 @@
   {
     gnode->family = family;
     gnode->gindex = gindex;
+
     family->num_nodes++;
   }
 
@@ -79,7 +81,7 @@
   /*************************************************************************/
 
   FT_EXPORT_DEF( void )
-  ftc_family_init( FTC_Family  family,
+  FTC_Family_Init( FTC_Family  family,
                    FTC_Cache   cache )
   {
     FTC_GCacheClass  clazz = FTC_CACHE__GCACHE_CLASS(cache);
@@ -86,6 +88,7 @@
 
     family->clazz     = clazz->family_class;
     family->num_nodes = 0;
+    family->cache     = cache;
   }
 
 
@@ -134,15 +137,27 @@
                      FTC_GQuery   query,
                      FTC_Node    *anode )
   {
-    FT_Error  error;
+    FT_Error    error;
+    FTC_Family  family;
 
     query->gindex = gindex;
 
-    error = FTC_MruList_Lookup( &cache->families, query,
-                                (FTC_MruNode*) &query->family );
-    if ( !error )
-      error = FTC_Cache_Lookup( FTC_CACHE(cache), hash, query, anode );
+    family = (FTC_Family) FTC_MruList_Lookup( &cache->families, query );
+    if ( family == NULL )
+    {
+      error = FTC_MruList_New( &cache->families, query, (FTC_MruNode*) &family );
+      if ( error )
+      {
+        *anode = NULL;
+        goto Exit;
+      }
+    }
 
+    query->family = family;
+
+    error = FTC_Cache_Lookup( FTC_CACHE(cache), hash, query, anode );
+
+  Exit:
     return error;
   }
 
--- a/src/cache/ftcmanag.c
+++ b/src/cache/ftcmanag.c
@@ -144,7 +144,7 @@
                           FTC_Scaler     scaler,
                           FT_Size       *asize )
   {
-    FT_Error      error;
+    FT_Error      error = 0;
     FTC_SizeNode  node;
 
 
@@ -156,16 +156,20 @@
     if ( !manager )
       return FTC_Err_Invalid_Cache_Handle;
 
-    error = FTC_MruList_Lookup( &manager->sizes,
-                                scaler,
-                                (FTC_MruNode*) &node );
+    error = FTC_MruList_Get( &manager->sizes, scaler, (FTC_MruNode*) &node );
     if ( !error )
+    {
       *asize = node->size;
+      FT_Activate_Size( node->size );
+    }
 
+  Exit:
     return error;
   }
 
 
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -211,9 +215,6 @@
   ftc_face_node_done( FTC_FaceNode  node,
                       FTC_Manager   manager )
   {
-    FT_Memory  memory = manager->memory;
-
-
     /* we must begin by removing all scalers for the target face */
     /* from the manager's list                                   */
     FTC_MruList_RemoveSelection(
@@ -222,11 +223,12 @@
           node->face_id );
 
     /* all right, we can discard the face now */
-    FT_Done_Face( node->face );
-    node->face    = NULL;
+    if ( node->face )
+    {
+      FT_Done_Face( node->face );
+      node->face    = NULL;
+    }
     node->face_id = NULL;
-
-    FT_FREE( node );
   }
 
 
@@ -257,7 +259,7 @@
                           FTC_FaceID   face_id,
                           FT_Face     *aface )
   {
-    FT_Error      error;
+    FT_Error      error = 0;
     FTC_FaceNode  node;
 
 
@@ -269,12 +271,17 @@
     if ( !manager )
       return FTC_Err_Invalid_Cache_Handle;
 
-    error = FTC_MruList_Lookup( &manager->faces,
-                                face_id,
-                                (FTC_MruNode*) &node );
-    if ( !error )
-      *aface = node->face;
+    node = (FTC_FaceNode) FTC_MruList_Lookup( &manager->faces, face_id );
+    if ( node == NULL )
+    {
+      error = FTC_MruList_New( &manager->faces, face_id, (FTC_MruNode*) &node );
+      if (error)
+        goto Exit;
+    }
 
+    *aface = node->face;
+
+  Exit:
     return error;
   }
 
@@ -422,7 +429,7 @@
 
       do
       {
-        FTC_Cache     cache = manager->caches + node->cache_index;
+        FTC_Cache     cache = manager->caches[node->cache_index];
 
         if ( (FT_UInt)node->cache_index >= manager->num_caches )
           FT_ERROR(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
@@ -432,7 +439,7 @@
           weight += cache->clazz.node_weight( node, cache );
         }
 
-        node = node->mru_next;
+        node = FTC_NODE__MRU_NEXT(node);
 
       } while ( node != first );
 
@@ -451,7 +458,7 @@
       do
       {
         count++;
-        node = node->mru_next;
+        node = FTC_NODE__MRU_NEXT(node);
 
       } while ( node != first );
 
@@ -494,20 +501,23 @@
       return;
 
     /* go to last node - it's a circular list */
-    node = first->mru_prev;
+    node = FTC_NODE__MRU_PREV(first);
     do
     {
-      FTC_Node  prev = node->mru_prev;
+      FTC_Node  prev = FTC_NODE__MRU_PREV(node);
 
 
-      prev = ( node == first ) ? NULL : node->mru_prev;
+      prev = FTC_NODE__MRU_PREV(node);
 
       if ( node->ref_count <= 0 )
         ftc_node_destroy( node, manager );
 
+      if ( node == first )
+        break;
+
       node = prev;
 
-    } while ( node && manager->cur_weight > manager->max_weight );
+    } while ( manager->cur_weight > manager->max_weight );
   }
 
 
@@ -576,12 +586,11 @@
       return 0;
 
     /* go to last node - it's a circular list */
-    node = first->mru_prev;
+    node = FTC_NODE__MRU_PREV( first );
     for ( result = 0; result < count; )
     {
-      FTC_Node  prev = node->mru_prev;
+      FTC_Node  prev = FTC_NODE__MRU_PREV(node);
 
-
      /* don't touch locked nodes */
       if ( node->ref_count <= 0 )
       {
@@ -589,7 +598,7 @@
         result++;
       }
 
-      if ( prev == manager->nodes_list )
+      if ( node == first )
         break;
 
       node = prev;
--- a/src/cache/ftcmru.c
+++ b/src/cache/ftcmru.c
@@ -6,7 +6,95 @@
 
 #include "ftcerror.h"
 
+
   FT_EXPORT_DEF( void )
+  FTC_MruNode_Prepend( FTC_MruNode  *plist,
+                       FTC_MruNode   node )
+  {
+    FTC_MruNode  first = *plist;
+
+    if ( first )
+    {
+      FTC_MruNode  last = first->prev;
+
+      last->next  = node;
+      first->prev = node;
+      node->prev  = last;
+      node->next  = first;
+    }
+    else
+    {
+      node->next = node;
+      node->prev = node;
+    }
+    *plist = node;
+  }
+
+
+  FT_EXPORT_DEF( void )
+  FTC_MruNode_Up( FTC_MruNode  *plist,
+                  FTC_MruNode   node )
+  {
+    FTC_MruNode  first = *plist;
+
+    FT_ASSERT( first != NULL );
+
+    if ( node != first )
+    {
+      FTC_MruNode  prev = node->prev;
+      FTC_MruNode  next = node->next;
+      FTC_MruNode  last;
+
+      prev->next = next;
+      next->prev = prev;
+
+      last = first->prev;
+
+      first->prev = node;
+      last->next  = node;
+
+      node->prev  = last;
+      node->next  = first;
+
+      *plist = node;
+    }
+  }
+
+
+  FT_EXPORT( void )
+  FTC_MruNode_Remove( FTC_MruNode  *plist,
+                      FTC_MruNode   node )
+  {
+    FTC_MruNode  first = *plist;
+    FTC_MruNode  prev, next;
+
+    FT_ASSERT( first != NULL );
+
+    next = node->next;
+    prev = node->prev;
+
+    if ( node == next )
+    {
+      FT_ASSERT( node == prev );
+      FT_ASSERT( node == first );
+
+      *plist = NULL;
+    }
+    else
+    {
+      prev->next = next;
+      next->prev = prev;
+
+      if ( node == first )
+        *plist = next;
+    }
+    node->prev = NULL;
+    node->next = NULL;
+  }
+
+
+
+  FT_EXPORT_DEF( void )
   FTC_MruList_Init( FTC_MruList       list,
                     FTC_MruListClass  clazz,
                     FT_UInt           max_nodes,
@@ -22,111 +110,130 @@
   }
 
 
-  static void
-  ftc_mrulist_free_nodes( FTC_MruList  list,
-                          FTC_MruNode *plist )
+
+
+  FT_EXPORT( void )
+  FTC_MruList_Reset( FTC_MruList  list )
   {
-    FT_Memory  memory = list->memory;
+    FT_Memory    memory = list->memory;
+    FTC_MruNode  first  = list->nodes;
 
-    while ( *plist )
+    if ( first )
     {
-      FTC_MruNode  node = *plist;
+      FTC_MruNode  node = first;
+      FTC_MruNode  next;
 
-      *plist = node->next;
+      do
+      {
+        next = node->next;
 
-      if ( list->clazz.node_done )
-        list->clazz.node_done( node, list->data );
+        if ( list->clazz.node_done )
+          list->clazz.node_done( node, list->data );
 
-      FT_FREE( node );
+        FT_FREE( node );
+
+        node = next;
+      }
+      while ( node != first );
     }
+    list->nodes     = NULL;
+    list->num_nodes = 0;
   }
 
 
   FT_EXPORT( void )
-  FTC_MruList_Reset( FTC_MruList  list )
+  FTC_MruList_Done( FTC_MruList  list )
   {
-    ftc_mrulist_free_nodes( list, &list->nodes );
-    list->num_nodes = 0;
+    FTC_MruList_Reset( list );
   }
 
 
-  FT_EXPORT( void )
-  FTC_MruList_Done( FTC_MruList  list )
+  FT_EXPORT_DEF( void )
+  FTC_MruList_Up( FTC_MruList  list,
+                  FTC_MruNode  node )
   {
-    FTC_MruList_Reset( list );
+    FTC_MruNode_Up( &list->nodes, node );
   }
 
 
-  FT_EXPORT( FT_Error )
-  FTC_MruList_Lookup( FTC_MruList   list,
-                      FT_Pointer    key,
-                      FTC_MruNode  *anode )
+  FT_EXPORT_DEF( FTC_MruNode )
+  FTC_MruList_Lookup( FTC_MruList  list,
+                      FT_Pointer   key )
   {
-    FT_Memory                memory = list->memory;
     FTC_MruNode_CompareFunc  compare = list->clazz.node_compare;
-    FTC_MruNode              *plast, *pnode, *pfirst;
-    FTC_MruNode              node;
-    FT_Error                 error = 0;
+    FTC_MruNode              node, first;
 
-    pfirst = &list->nodes;
-    plast  = pnode = pfirst;
+    first = list->nodes;
+    node  = NULL;
 
-    for (;;)
+    if ( first )
     {
-      node = *pnode;
-      if ( node == NULL )
-        goto NewNode;
-      if ( compare( node, key ) )
-        break;
-      plast = pnode;
-      pnode = &node->next;
-    }
+      node = first;
+      do
+      {
+        if ( compare( node, key ) )
+          goto Exit;
 
-    if ( node != *pfirst )
-    {
-      *pnode     = node->next;
-      node->next = *pfirst;
-      *pfirst    = node;
+        node = node->next;
+
+      } while ( node != first );
+
+      node = NULL;
     }
-    goto Exit;
+  Exit:
+    return node;
+  }
 
-  NewNode:
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FTC_MruList_New( FTC_MruList    list,
+                   FT_Pointer     key,
+                   FTC_MruNode   *anode )
+  {
+    FT_Memory    memory = list->memory;
+    FT_Error     error;
+    FTC_MruNode  node;
+
     if ( list->max_nodes > 0 && list->num_nodes >= list->max_nodes )
     {
-      node = *plast;
+      FT_ASSERT( list->nodes != NULL );
 
-      if ( node )
+      node = list->nodes->prev;  /* last node */
+
+      if ( list->clazz.node_reset )
       {
-        *plast = NULL;
-        list->num_nodes--;
+        FTC_MruNode_Up( &list->nodes, node );
 
-        if ( list->clazz.node_reset )
-        {
-          error = list->clazz.node_reset( node, key, list->data );
-          if ( !error ) goto AddNode;
-        }
+        error = list->clazz.node_reset( node, key, list->data );
+        if ( !error )
+          goto Exit;
+      }
 
+      FTC_MruNode_Remove( &list->nodes, node );
+      list->num_nodes--;
+
+      if ( list->clazz.node_done )
         list->clazz.node_done( node, list->data );
-      }
     }
-    else if ( FT_ALLOC( node, list->clazz.node_size ) )
-      goto Exit;
-
+    else
+    {
+      if ( FT_ALLOC( node, list->clazz.node_size ) )
+        goto Exit;
+    }
     error = list->clazz.node_init( node, key, list->data );
-    if ( error )
+    if ( !error )
     {
-      if ( list->clazz.node_done )
-        list->clazz.node_done( node, list->data );
-
-      FT_FREE( node );
+      FTC_MruNode_Prepend( &list->nodes, node );
+      list->num_nodes++;
       goto Exit;
     }
 
-  AddNode:
-    node->next  = list->nodes;
-    list->nodes = node;
-    list->num_nodes++;
+    if ( list->clazz.node_done )
+      list->clazz.node_done( node, list->data );
 
+    FT_FREE( node );
+
   Exit:
     *anode = node;
     return error;
@@ -133,39 +240,42 @@
   }
 
 
-  FT_EXPORT_DEF( void )
-  FTC_MruList_Remove( FTC_MruList   list,
-                      FTC_MruNode   node )
+
+  FT_EXPORT_DEF( FT_Error )
+  FTC_MruList_Get( FTC_MruList   list,
+                   FT_Pointer    key,
+                   FTC_MruNode  *anode )
   {
-    FTC_MruNode  *pnode = &list->nodes;
+    FT_Error     error = 0;
+    FTC_MruNode  node;
 
-    for ( ;; )
+    node = FTC_MruList_Lookup( list, key );
+    if ( node == NULL )
     {
-      if ( *pnode == NULL )  /* should not happen !! */
-      {
-        FT_ERROR(( "%s: trying to remove unknown node !!\n",
-                   "FTC_MruList_Remove" ));
-        return;
-      }
+      error = FTC_MruList_New( list, key, &node );
+      if ( error )
+        node = NULL;
+    }
+    *anode = node;
+    return error;
+  }
 
-      if ( *pnode == node )
-        break;
 
-      pnode = &node->next;
-    }
+  FT_EXPORT_DEF( void )
+  FTC_MruList_Remove( FTC_MruList   list,
+                      FTC_MruNode   node )
+  {
+    FT_Memory    memory = list->memory;
 
-    *pnode     = node->next;
-    node->next = NULL;
+    FT_ASSERT( list->nodes != NULL && list->num_nodes > 0 );
+
+    FTC_MruNode_Remove( &list->nodes, node );
     list->num_nodes--;
 
-    {
-      FT_Memory  memory = list->memory;
+    if ( list->clazz.node_done )
+      list->clazz.node_done( node, list->data );
 
-      if ( list->clazz.node_done )
-        list->clazz.node_done( node, list->data );
-
-      FT_FREE( node );
-    }
+    FT_FREE( node );
   }
 
 
@@ -174,30 +284,29 @@
                                FTC_MruNode_CompareFunc  select,
                                FT_Pointer               key )
   {
-    FTC_MruNode  *pnode = &list->nodes;
-    FTC_MruNode   node, free = NULL;;
+    FTC_MruNode   first = list->nodes;
 
-    if ( select )
+    while ( first && select( first, key ) )
     {
-      for (;;)
-      {
-        FTC_MruNode  node = *pnode;
+      FTC_MruList_Remove( list, first );
+      first = list->nodes;
+    }
 
-        if ( node == NULL )
-          break;
+    if ( first )
+    {
+      FTC_MruNode  node = first->next;
+      FTC_MruNode  next;
 
+      while ( node != first )
+      {
+        next = node->next;
+
         if ( select( node, key ) )
-        {
-          *pnode     = node->next;
-          node->next = free;
-          free       = node;
-        }
-        else
-          pnode = &node->next;
+          FTC_MruList_Remove( list, node );
+
+        node = next;
       }
     }
-
-    ftc_mrulist_free_nodes( list, &free );
   }
 
 /* END */
--- a/src/cache/ftcsbits.c
+++ b/src/cache/ftcsbits.c
@@ -91,7 +91,7 @@
     FTC_SFamilyClass   clazz;
 
 
-    if ( (FT_UInt)gindex >= gnode->gindex + snode->count )
+    if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count )
     {
       FT_ERROR(( "ftc_snode_load: invalid glyph index" ));
       return FTC_Err_Invalid_Argument;
@@ -267,7 +267,8 @@
     FT_UInt     gindex = gquery->gindex;
     FT_Bool     result;
 
-    result = FT_BOOL( (FT_UInt)(gindex - gnode->gindex) < snode->count );
+    result = FT_BOOL( gnode->family == gquery->family                  &&
+                      (FT_UInt)(gindex - gnode->gindex) < snode->count );
     if ( result )
     {
       /* check if we need to load the glyph bitmap now */