shithub: freetype+ttf2subf

Download patch

ref: b280537b6d330a2e24307afc8058a79f5b3b8864
parent: a3c378024bfb412f021223bc7eca3d5030423060
author: David Turner <[email protected]>
date: Thu Mar 13 16:07:51 EST 2003

* src/base/ftdbgmem.c, docs/DEBUG.TXT: added new environment variables
    to control memory debugging with FreeType. See the description of
    "FT2_DEBUG_MEMORY", "FT2_ALLOC_TOTAL_MAX" and "FT2_ALLOC_COUNT_MAX"
    in DEBUG.TXT

    * src/cache/ftccache.c, src/cache/ftccmap.c, src/cache/ftcsbits.c,
    ftlru.c: fixed the cache sub-system to correctly deal with out-of-memory
    conditions.

    * src/pfr/pfrobjs.c, src/pfr/pfrsbits.c: fixing compiler warnings and a
    small memory leak

    * src/psaux/psobjs.c (t1_reallocate_table): fixed a bug (memory leak) that
    only happened when trying to resize an array would end in an OOM.

    * src/smooth/ftgrays.c: removed compiler warnings / volatile bug

    * src/truetype/ttobjs.c: removed segmentation fault that happened in
    tight memory environments.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2003-03-13  David Turner  <[email protected]>
+
+    * src/base/ftdbgmem.c, docs/DEBUG.TXT: added new environment variables
+    to control memory debugging with FreeType. See the description of
+    "FT2_DEBUG_MEMORY", "FT2_ALLOC_TOTAL_MAX" and "FT2_ALLOC_COUNT_MAX"
+    in DEBUG.TXT
+
+    * src/cache/ftccache.c, src/cache/ftccmap.c, src/cache/ftcsbits.c,
+    ftlru.c: fixed the cache sub-system to correctly deal with out-of-memory
+    conditions.
+
+    * src/pfr/pfrobjs.c, src/pfr/pfrsbits.c: fixing compiler warnings and a
+    small memory leak
+
+    * src/psaux/psobjs.c (t1_reallocate_table): fixed a bug (memory leak) that
+    only happened when trying to resize an array would end in an OOM.
+
+    * src/smooth/ftgrays.c: removed compiler warnings / volatile bug
+
+    * src/truetype/ttobjs.c: removed segmentation fault that happened in
+    tight memory environments.
+
 2003-02-28  Pixel  <[email protected]>
 
     * src/gzip/ftgzip.c (ft_gzip_file_done): fixed memory leak, the ZLib
--- a/docs/DEBUG.TXT
+++ b/docs/DEBUG.TXT
@@ -159,4 +159,25 @@
     ignored in other builds.
 
 
+  FT2_ALLOC_TOTAL_MAX
+
+    this variable is ignored if FT2_DEBUG_MEMORY is not defined. It allows
+    you to specify a maximum heap size for all memory allocations performed
+    by FreeType. This is very useful to test the robustness of the font
+    engine and programs that use it in tight memory conditions.
+    
+    If it is undefined, or if its value is not strictly positive, then no
+    allocation bounds are checked at runtime.
+
+
+  FT2_ALLOC_COUNT_MAX
+  
+    this variable is ignored if FT2_DEBUG_MEMORY is not defined. It allows
+    you to sepcify a maximum number of memory allocations performed by
+    FreeType before returning the error FT_Err_Out_Of_Memory. This is
+    useful for debugging and testing the engine's robustness.
+    
+    If it is undefined, or if its value is not strictly positive, then no
+    allocation bounsd are checked at runtime.
+
 End of file
--- a/src/base/ftdbgmem.c
+++ b/src/base/ftdbgmem.c
@@ -62,7 +62,14 @@
     FT_ULong         alloc_total;
     FT_ULong         alloc_current;
     FT_ULong         alloc_max;
+    FT_ULong         alloc_count;
 
+    FT_Bool          bound_total;    
+    FT_ULong         alloc_total_max;
+    
+    FT_Bool          bound_count;
+    FT_ULong         alloc_count_max;
+
     const char*      file_name;
     FT_Long          line_no;
 
@@ -476,10 +483,22 @@
     if ( size <= 0 )
       ft_mem_debug_panic( "negative block size allocation (%ld)", size );
 
+    /* return NULL if the maximum number of allocations was reached */
+    if ( table->bound_count &&
+         table->alloc_count >= table->alloc_count_max )
+      return NULL;
+
+    /* return NULL if this allocation would overflow the maximum heap size */
+    if ( table->bound_total && 
+         table->alloc_current + (FT_ULong)size > table->alloc_total_max )
+      return NULL;         
+
     block = (FT_Byte *)ft_mem_table_alloc( table, size );
     if ( block )
       ft_mem_table_set( table, block, (FT_ULong)size );
 
+    table->alloc_count++;
+
     table->file_name = NULL;
     table->line_no   = 0;
 
@@ -570,15 +589,42 @@
     FT_Int       result = 0;
 
 
-    if ( getenv( "FT_DEBUG_MEMORY" ) )
+    if ( getenv( "FT2_DEBUG_MEMORY" ) )
     {
       table = ft_mem_table_new( memory );
       if ( table )
       {
+        const char*  p;
+        
         memory->user    = table;
         memory->alloc   = ft_mem_debug_alloc;
         memory->realloc = ft_mem_debug_realloc;
         memory->free    = ft_mem_debug_free;
+        
+        p = getenv( "FT2_ALLOC_TOTAL_MAX" );
+        if ( p != NULL )
+        {
+          FT_Long   total_max = atol(p);
+          
+          if ( total_max > 0 )
+          {
+            table->bound_total     = 1;
+            table->alloc_total_max = (FT_ULong) total_max;
+          }
+        }
+        
+        p = getenv( "FT2_ALLOC_COUNT_MAX" );
+        if ( p != NULL )
+        {
+          FT_Long  total_count = atol(p);
+          
+          if ( total_count > 0 )
+          {
+            table->bound_count     = 1;
+            table->alloc_count_max = (FT_ULong) total_count;
+          }
+        }
+
         result = 1;
       }
     }
--- a/src/cache/ftccache.c
+++ b/src/cache/ftccache.c
@@ -357,10 +357,12 @@
 
     FT_FREE( node );
 
+#if 0
     /* check, just in case of general corruption :-) */
     if ( manager->num_nodes == 0 )
       FT_ERROR(( "ftc_node_destroy: invalid cache node count! = %d\n",
                   manager->num_nodes ));
+#endif                  
   }
 
 
@@ -546,8 +548,10 @@
                     FTC_Query   query,
                     FTC_Node   *anode )
   {
-    FT_Error    error = FT_Err_Ok;
-    FT_LruNode  lru;
+    FT_Error     error = FT_Err_Ok;
+    FTC_Manager  manager;
+    FT_LruNode   lru;
+    FT_UInt      free_count = 0;
 
 
     if ( !cache || !query || !anode )
@@ -558,152 +562,237 @@
     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;
-      FT_LruNode_CompareFunc  compare = list->clazz->node_compare;
+    manager = cache->manager;
 
-      pfam = &list->nodes;
-      for (;;)
+   /*  here's a small note explaining what's hapenning in the code below.
+    *
+    *  we need to deal intelligently with out-of-memory (OOM) conditions
+    *  when trying to create a new family or cache node during the lookup.
+    *
+    *  when an OOM is detected, we'll try to free one or more "old" nodes
+    *  from the cache, then try again. it may be necessary to do that several
+    *  times, so a loop is needed.
+    *
+    *  the local variable "free_count" holds the number of "old" nodes to
+    *  discard on each attempt. it starts at 1 and doubles on each iteration.
+    *  the loop stops when:
+    *
+    *   - a non-OOM error is detected
+    *   - a succesful lookup is performed
+    *   - there are no more unused nodes in the cache
+    *
+    *  for the record, remember that all used nodes appear _before_
+    *  unused ones in the manager's MRU node list.
+    */
+
+    for (;;)
+    {
       {
-        fam = *pfam;
-        if ( fam == NULL )
+        /* first of all, find the relevant family */
+        FT_LruList              list    = cache->families;
+        FT_LruNode              fam, *pfam;
+        FT_LruNode_CompareFunc  compare = list->clazz->node_compare;
+  
+        pfam = &list->nodes;
+        for (;;)
         {
-          error = FT_LruList_Lookup( list, query, &lru );
-          if ( error )
-            goto Exit;
-
-          goto Skip;
+          fam = *pfam;
+          if ( fam == NULL )
+          {
+            error = FT_LruList_Lookup( list, query, &lru );
+            if ( error )
+              goto Fail;
+  
+            goto Skip;
+          }
+  
+          if ( compare( fam, query, list->data ) )
+            break;
+  
+          pfam = &fam->next;
         }
-
-        if ( 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;
+  
+      Skip:
+        ;
       }
 
-      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;
-
-    Skip:
-      ;
-    }
-
-    {
-      FTC_Family  family = (FTC_Family) lru;
-      FT_UFast    hash    = query->hash;
-      FTC_Node*   bucket;
-      FT_UInt     idx;
-
-
-      idx = hash & cache->mask;
-      if ( idx < cache->p )
-        idx = hash & ( cache->mask * 2 + 1 );
-
-      bucket  = cache->buckets + idx;
-
-
-      if ( query->family     != family                        ||
-           family->fam_index >= cache->manager->families.size )
-      {
-        FT_ERROR((
-          "ftc_cache_lookup: invalid query (bad 'family' field)\n" ));
-        return FTC_Err_Invalid_Argument;
-      }
-
-      if ( *bucket )
-      {
-        FTC_Node*             pnode   = bucket;
-        FTC_Node_CompareFunc  compare = cache->clazz->node_compare;
-
-
-        for ( ;; )
+        FTC_Manager  manager = cache->manager;
+        FTC_Family   family  = (FTC_Family) lru;
+        FT_UFast     hash    = query->hash;
+        FTC_Node*    bucket;
+        FT_UInt      idx;
+  
+  
+        idx = hash & cache->mask;
+        if ( idx < cache->p )
+          idx = hash & ( cache->mask * 2 + 1 );
+  
+        bucket  = cache->buckets + idx;
+  
+  
+        if ( query->family     != family                 ||
+             family->fam_index >= manager->families.size )
         {
-          FTC_Node  node;
+          FT_ERROR((
+            "ftc_cache_lookup: invalid query (bad 'family' field)\n" ));
+          error = FTC_Err_Invalid_Argument;
+          goto Exit;
+        }
+  
+        if ( *bucket )
+        {
+          FTC_Node*             pnode   = bucket;
+          FTC_Node_CompareFunc  compare = cache->clazz->node_compare;
+  
 
-
-          node = *pnode;
-          if ( node == NULL )
-            break;
-
-          if ( node->hash == hash                            &&
-               (FT_UInt)node->fam_index == family->fam_index &&
-               compare( node, query, cache ) )
+          for ( ;; )
           {
-            /* move to head of bucket list */
-            if ( pnode != bucket )
+            FTC_Node  node;
+  
+  
+            node = *pnode;
+            if ( node == NULL )
+              break;
+  
+            if ( node->hash == hash                            &&
+                 (FT_UInt)node->fam_index == family->fam_index &&
+                 compare( node, query, cache ) )
             {
-              *pnode     = node->link;
-              node->link = *bucket;
-              *bucket    = node;
+              /* 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 != manager->nodes_list )
+                ftc_node_mru_up( node, manager );
+  
+              *anode = node;
+              goto Exit;
             }
-
-            /* move to head of MRU list */
-            if ( node != cache->manager->nodes_list )
-              ftc_node_mru_up( node, cache->manager );
-
-            *anode = node;
-            goto Exit;
+  
+            pnode = &node->link;
           }
-
-          pnode = &node->link;
         }
+  
+        /* didn't find a node, create a new one */
+        {
+          FTC_Cache_Class  clazz   = cache->clazz;
+          FT_Memory        memory  = cache->memory;
+          FTC_Node         node;
+  
+  
+          if ( FT_ALLOC( node, clazz->node_size ) )
+            goto Fail;
+  
+          node->fam_index = (FT_UShort) family->fam_index;
+          node->hash      = query->hash;
+          node->ref_count = 0;
+  
+          error = clazz->node_init( node, query, cache );
+          if ( error )
+          {
+            FT_FREE( node );
+            goto Fail;
+          }
+  
+          error = ftc_node_hash_link( node, cache );
+          if ( error )
+          {
+            clazz->node_done( node, cache );
+            FT_FREE( node );
+            goto Fail;
+          }
+  
+          ftc_node_mru_link( node, cache->manager );
+  
+          cache->manager->cur_weight += clazz->node_weight( node, cache );
+  
+          /* now try to compress the node pool when necessary */
+          if ( manager->cur_weight >= manager->max_weight )
+          {
+            node->ref_count++;
+            FTC_Manager_Compress( manager );
+            node->ref_count--;
+          }
+  
+          *anode = node;
+        }
+        
+       /* all is well, exit now
+        */
+        goto Exit;
       }
-
-      /* didn't find a node, create a new one */
+      
+    Fail:
+      if ( error != FT_Err_Out_Of_Memory )
+        goto Exit;
+     
+     /* there is not enough memory, try to release some unused nodes
+      * from the cache to make room for a new one.
+      */
       {
-        FTC_Cache_Class  clazz   = cache->clazz;
-        FTC_Manager      manager = cache->manager;
-        FT_Memory        memory  = cache->memory;
-        FTC_Node         node;
+        FT_UInt   new_count;
 
+        new_count = 1 + free_count*2;
 
-        if ( FT_ALLOC( node, clazz->node_size ) )
+        /* check overflow and bounds */
+        if ( new_count < free_count || free_count > manager->num_nodes )
           goto Exit;
 
-        node->fam_index = (FT_UShort) family->fam_index;
-        node->hash      = query->hash;
-        node->ref_count = 0;
-
-        error = clazz->node_init( node, query, cache );
-        if ( error )
+        free_count = new_count;
+        
+        /* try to remove "new_count" nodes from the list */
         {
-          FT_FREE( node );
-          goto Exit;
-        }
+          FTC_Node   first = manager->nodes_list;
+          FTC_Node   node;
 
-        error = ftc_node_hash_link( node, cache );
-        if ( error )
-        {
-          clazz->node_done( node, cache );
-          FT_FREE( node );
-          goto Exit;
-        }
+          if ( first == NULL )  /* empty list ! */
+            goto Exit;
 
-        ftc_node_mru_link( node, cache->manager );
+         /* go to last node - it's a circular list */
+          node = first->mru_prev;
+          for ( ; node && new_count > 0; new_count-- )
+          {
+            FTC_Node  prev = node->mru_prev;
 
-        cache->manager->cur_weight += clazz->node_weight( node, cache );
-
-        /* now try to compress the node pool when necessary */
-        if ( manager->cur_weight >= manager->max_weight )
-        {
-          node->ref_count++;
-          FTC_Manager_Compress( manager );
-          node->ref_count--;
-        }
+           /* used nodes always appear before unused one in the MRU
+            * list. if we find one here, we'd better stop right now
+            * our iteration
+            */
+            if ( node->ref_count > 0 )
+            {
+              /* if there are no unused nodes in the list, we'd better exit */
+              if ( new_count == free_count )
+                goto Exit;
+                
+              break;
+            }
 
-        *anode = node;
+            ftc_node_destroy( node, manager );
+
+            if ( node == first )
+              break;
+
+            node = prev;
+          }
+        }
       }
     }
 
--- a/src/cache/ftccmap.c
+++ b/src/cache/ftccmap.c
@@ -27,6 +27,9 @@
 
 #include "ftcerror.h"
 
+#undef  FT_COMPONENT
+#define FT_COMPONENT  trace_cache
+
   /*************************************************************************/
   /*                                                                       */
   /* Each FTC_CMapNode contains a simple array to map a range of character */
--- a/src/cache/ftcsbits.c
+++ b/src/cache/ftcsbits.c
@@ -216,7 +216,7 @@
       /* we mark unloaded glyphs with `sbit.buffer == 0' */
       /* and 'width == 255', 'height == 0'               */
       /*                                                 */
-      if ( error )
+      if ( error && error != FT_Err_Out_Of_Memory )
       {
         sbit->width = 255;
         error       = 0;
--- a/src/cache/ftlru.c
+++ b/src/cache/ftlru.c
@@ -21,6 +21,7 @@
 #include FT_CACHE_INTERNAL_LRU_H
 #include FT_LIST_H
 #include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
 
 #include "ftcerror.h"
 
@@ -187,78 +188,133 @@
       goto Exit;
     }
 
-    /* we haven't found the relevant element.  We will now try */
-    /* to create a new one.                                    */
-    /*                                                         */
-
-    /* first, check if our list if full, when appropriate */
-    if ( list->max_nodes > 0 && list->num_nodes >= list->max_nodes )
+   /* since we haven't found the relevant element in our LRU list,
+    * we're going to "create" a new one.
+    *
+    * the following code is a bit special, because it tries to handle
+    * out-of-memory conditions (OOM) in an intelligent way.
+    *
+    * more precisely, if not enough memory is available to create a
+    * new node or "flush" an old one, we need to remove the oldest
+    * elements from our list, and try again. since several tries may
+    * be necessary, a loop is needed
+    *
+    * this loop will only exit when:
+    *
+    *   - a new node was succesfully created, or an old node flushed
+    *   - an error other than FT_Err_Out_Of_Memory is detected
+    *   - the list of nodes is empty, and it isn't possible to create
+    *     new nodes
+    *
+    * on each unsucesful attempt, one node will be removed from the list
+    *
+    */
+    
     {
-      /* this list list is full; we will now flush */
-      /* the oldest node, if there's one!          */
-      FT_LruNode  last = *plast;
+      FT_Int   drop_last = ( list->max_nodes > 0 && 
+                             list->num_nodes >= list->max_nodes );
 
-
-      if ( last )
+      for (;;)
       {
-        if ( clazz->node_flush )
+        node = NULL;
+
+       /* when "drop_last" is true, we should free the last node in
+        * the list to make room for a new one. note that we re-use
+        * its memory block to save allocation calls.
+        */
+        if ( drop_last )
         {
-          error = clazz->node_flush( last, key, list->data );
+         /* find the last node in the list
+          */
+          pnode = &list->nodes;
+          node  = *pnode;
+  
+          if ( node == NULL )
+          {
+            FT_ASSERT( list->nodes == 0 );
+            error = FT_Err_Out_Of_Memory;
+            goto Exit;
+          }
+
+          FT_ASSERT( list->nodes > 0 );
+
+          while ( node->next )
+          {
+            pnode = &node->next;
+            node  = *pnode;
+          }
+  
+         /* remove it from the list, and try to "flush" it. doing this will
+          * save a significant number of dynamic allocations compared to
+          * a classic destroy/create cycle
+          */
+          *pnode = NULL;
+          list->num_nodes -= 1;
+  
+          if ( clazz->node_flush )
+          {
+            error = clazz->node_flush( node, key, list->data );
+            if ( !error )
+              goto Success;
+
+           /* note that if an error occured during the flush, we need to
+            * finalize it since it is potentially in incomplete state.
+            */
+          }
+
+         /* we finalize, but do not destroy the last node, we
+          * simply re-use its memory block !
+          */
+          if ( clazz->node_done )
+            clazz->node_done( node, list->data );
+            
+          FT_MEM_ZERO( node, clazz->node_size );
         }
         else
         {
-          if ( clazz->node_done )
-            clazz->node_done( last, list->data );
-
-          last->key  = key;
-          error = clazz->node_init( last, key, list->data );
+         /* try to allocate a new node when "drop_last" is not TRUE
+          * this usually happens on the first pass, when the LRU list
+          * is not already full.
+          */
+          if ( FT_ALLOC( node, clazz->node_size ) )
+            goto Fail;
         }
+  
+        FT_ASSERT( node != NULL );
 
-        if ( !error )
+        node->key = key;
+        error = clazz->node_init( node, key, list->data );
+        if ( error )
         {
-          /* move it to the top of the list */
-          *plast      = NULL;
-          last->next  = list->nodes;
-          list->nodes = last;
+          if ( clazz->node_done )
+            clazz->node_done( node, list->data );
 
-          result = last;
-          goto Exit;
+          FT_FREE( node );
+          goto Fail;
         }
 
-        /* in case of error during the flush or done/init cycle, */
-        /* we need to discard the node                           */
-        if ( clazz->node_done )
-          clazz->node_done( last, list->data );
+      Success:
+        result = node;
 
-        *plast = NULL;
-        list->num_nodes--;
-
-        FT_FREE( last );
+        node->next  = list->nodes;
+        list->nodes = node;
+        list->num_nodes++;
         goto Exit;
+  
+      Fail:
+        if ( error != FT_Err_Out_Of_Memory )
+          goto Exit;
+        
+        drop_last = 1;
+        continue;
       }
     }
 
-    /* otherwise, simply allocate a new node */
-    if ( FT_ALLOC( node, clazz->node_size ) )
-      goto Exit;
-
-    node->key = key;
-    error = clazz->node_init( node, key, list->data );
-    if ( error )
-    {
-      FT_FREE( node );
-      goto Exit;
-    }
-
-    result      = node;
-    node->next  = list->nodes;
-    list->nodes = node;
-    list->num_nodes++;
-
   Exit:
     *anode = result;
     return error;
   }
+
 
 
   FT_EXPORT_DEF( void )
--- a/src/pfr/pfrobjs.c
+++ b/src/pfr/pfrobjs.c
@@ -41,6 +41,8 @@
   FT_LOCAL_DEF( void )
   pfr_face_done( PFR_Face  face )
   {
+    FT_Memory   memory = face->root.driver->root.memory;
+
     /* we don't want dangling pointers */
     face->root.family_name = NULL;
     face->root.style_name  = NULL;
@@ -49,6 +51,7 @@
     pfr_phy_font_done( &face->phy_font, FT_FACE_MEMORY( face ) );
 
     /* no need to finalize the logical font or the header */
+    FT_FREE( face->root.available_sizes );
   }
 
 
@@ -179,8 +182,8 @@
          strike = phy_font->strikes;
          for ( n = 0; n < count; n++, size++, strike++ )
          {
-           size->height = strike->y_ppm;
-           size->width  = strike->x_ppm;
+           size->height = (FT_UShort) strike->y_ppm;
+           size->width  = (FT_UShort) strike->x_ppm;
          }
          root->num_fixed_sizes = count;
        }
--- a/src/psaux/psobjs.c
+++ b/src/psaux/psobjs.c
@@ -111,7 +111,10 @@
 
     /* allocate new base block */
     if ( FT_ALLOC( table->block, new_size ) )
+    {
+      table->block = old_base;
       return error;
+    }
 
     /* copy elements and shift offsets */
     if (old_base )
--- a/src/smooth/ftgrays.c
+++ b/src/smooth/ftgrays.c
@@ -1827,9 +1827,9 @@
   gray_convert_glyph( RAS_ARG )
   {
     TBand            bands[40];
-    volatile TBand*  band;
-    volatile int     n, num_bands;
-    volatile TPos    min, max, max_y;
+    TBand* volatile  band;
+    int volatile     n, num_bands;
+    TPos volatile    min, max, max_y;
     FT_BBox*         clip;
 
 
--- a/src/truetype/ttobjs.c
+++ b/src/truetype/ttobjs.c
@@ -70,14 +70,17 @@
   {
     FT_Memory  memory = zone->memory;
 
+    if ( memory )
+    {
+      FT_FREE( zone->contours );
+      FT_FREE( zone->tags );
+      FT_FREE( zone->cur );
+      FT_FREE( zone->org );
 
-    FT_FREE( zone->contours );
-    FT_FREE( zone->tags );
-    FT_FREE( zone->cur );
-    FT_FREE( zone->org );
-
-    zone->max_points   = zone->n_points   = 0;
-    zone->max_contours = zone->n_contours = 0;
+      zone->max_points   = zone->n_points   = 0;
+      zone->max_contours = zone->n_contours = 0;
+      zone->memory       = NULL;
+    }
   }