home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / g / gs252src.zip / GS252 / ISAVE.C < prev    next >
C/C++ Source or Header  |  1992-09-19  |  16KB  |  518 lines

  1. /* Copyright (C) 1991, 1992 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* isave.c */
  21. /* Save/restore machinery for Ghostscript */
  22. #include "ghost.h"
  23. #include "memory_.h"
  24. #include "alloc.h"
  25. #include "astate.h"
  26. #include "name.h"
  27. #include "packed.h"
  28. #include "save.h"
  29. #include "store.h"            /* for ref_assign */
  30.  
  31. /* Imported restore routines */
  32. extern void file_save(P0());
  33. extern void file_restore(P1(alloc_save *));
  34. extern void font_restore(P1(alloc_save *));
  35.  
  36. /*
  37.  * The logic for saving and restore the state is rather subtle.
  38.  * Both the changes to individual objects, and the overall state
  39.  * of the memory manager, must be saved and restored.
  40.  */
  41.  
  42. /*
  43.  * To save the state of the memory manager:
  44.  *    Save the state of the current chunk in which we are allocating.
  45.  *    Save the identity of the current chunk.
  46.  *    Save and reset the malloc chain and the orphan block chains.
  47.  * By doing this, we guarantee that no object older than the save
  48.  * can be freed.
  49.  *
  50.  * To restore the state of the memory manager:
  51.  *    Free all chunks newer than the save.
  52.  *    Free all malloc'ed blocks newer than the save.
  53.  *    Make current the chunk that was current at the time of the save.
  54.  *    Free all objects allocated in the current chunk since the save.
  55.  */
  56.  
  57. /*
  58.  * For saving changes to individual objects, we add an "attribute" bit
  59.  * (l_new) that logically belongs to the slot where the descriptor is stored,
  60.  * not to the descriptor itself.  The bit means "the contents
  61.  * of this slot have been changed since the last save."
  62.  * To keep track of changes since the save, we associate a chain of
  63.  * <slot, old_contents> pairs that remembers the old contents of slots.
  64.  *
  65.  * When creating an object, if the save level is non-zero:
  66.  *    Set the bit in all slots.
  67.  *
  68.  * When storing into a slot, if the save level is non-zero:
  69.  *    If the bit isn't set, save the address and contents of the slot
  70.  *      on the current contents chain.
  71.  *    Set the bit after storing the new value.
  72.  *
  73.  * To do a save:
  74.  *    If the save level is non-zero:
  75.  *        Reset the bit in all slots on the contents chain, and in all
  76.  *          objects created since the previous save.
  77.  *    Push the head of contents chain, and reset the chain to empty.
  78.  *
  79.  * To do a restore:
  80.  *    Check all the stacks to make sure they don't contain references
  81.  *      to objects created since the save.
  82.  *    Restore all the slots on the contents chain.
  83.  *    Pop the contents chain head.
  84.  *    If the save level is now non-zero:
  85.  *        Scan the newly restored contents chain, and set the bit in all
  86.  *          the slots it references.
  87.  *        Scan all objects created since the previous save, and set the
  88.  *          bit in all the slots of each object.
  89.  */
  90.  
  91. /* Declare the mask for checking stores. */
  92. /* This is -1 if we are not in a save, 0 if we are in a save. */
  93. int alloc_save_test_mask;
  94. /* Declare the mask for tagging new objects. */
  95. /* This is 0 if we are not in a save, l_new if we are in a save. */
  96. int alloc_save_new_mask;
  97. #define set_in_save()\
  98.   (alloc_save_test_mask = 0, alloc_save_new_mask = l_new)
  99. #define set_not_in_save()\
  100.   (alloc_save_test_mask = -1, alloc_save_new_mask = 0)
  101.  
  102. /* Structure for saved change chain for save/restore. */
  103. /* If where = 0, contents is a t_array that refers to */
  104. /* a newly allocated object; if contents.value.refs is 0, */
  105. /* this is a free record (possible since we allocate them two at a time.) */
  106. /* We merge adjacent objects to reduce */
  107. /* the need to allocate alloc_change records. */
  108. struct alloc_change_s {
  109.     alloc_change *next;
  110.     ref *where;
  111.     ref contents;
  112. };
  113. #define alloc_change_is_free(cp)\
  114.   ((cp)->where == 0 && (cp)->contents.value.refs == 0)
  115.  
  116. /*
  117.  * Macro to allocate a pair of change records.
  118.  * Must be used in the following form:
  119.  *    if_not_alloc_change_pair(cp, ap, cname)
  120.  *     { ... failure code ...
  121.  *     }
  122.  */
  123. #define if_not_alloc_change_pair(cp, ap, cname)\
  124.   cp = (alloc_change *)alloc(2, sizeof(alloc_change), cname);\
  125.   if ( cp != 0 )\
  126.    { cp->next = ap->changes;\
  127.      cp[1].next = cp;\
  128.      cp[1].where = 0;\
  129.      cp[1].contents.value.refs = 0;\
  130.      r_set_size(&cp[1].contents, 0);\
  131.      ap->changes = cp + 1;\
  132.    }\
  133.   else
  134.  
  135. /* Saved state of allocator and other things as needed. */
  136. struct alloc_save_s {
  137.     alloc_state state;
  138.     alloc_state_ptr cap;
  139.     uint name_cnt;
  140. };
  141.  
  142. /* Debugging printout */
  143. #ifdef DEBUG
  144. private void
  145. alloc_save_print(alloc_change *cp)
  146. {    dprintf1(" %lx:", (ulong)cp);
  147.     if ( cp->where )
  148.       dprintf4(" %lx: %x %x %lx\n", (ulong)cp->where,
  149.            r_type_attrs(&cp->contents), r_size(&cp->contents),
  150.            (ulong)cp->contents.value.intval);
  151.     else
  152.       dprintf2(" %lx(%u)\n", (ulong)cp->contents.value.refs,
  153.            r_size(&cp->contents));
  154. }
  155. #endif
  156.  
  157. /* Forward references */
  158. private void save_set_new(P2(alloc_state_ptr, int));
  159.  
  160. /* Initialize the save/restore machinery. */
  161. void
  162. alloc_save_init()
  163. {    set_not_in_save();
  164. }
  165.  
  166. /* Save the state. */
  167. alloc_save *
  168. alloc_save_state()
  169. {    register alloc_state_ptr ap = alloc_state_current;
  170.     alloc_save *save;
  171.     save = (alloc_save *)alloc(1, sizeof(alloc_save),
  172.                    "alloc_save_state");
  173.     if ( save == 0 ) return 0;
  174.     save->state = *ap;
  175.     save->cap = ap;
  176.     save->name_cnt = name_count();
  177.     /* Reset the l_new attribute in all slots.  The only slots that */
  178.     /* can have the attribute set are the ones on the changes chain. */
  179.     save_set_new(ap, 0);
  180.     /* Clear the free chains, to prevent old objects from being freed. */
  181.     memset(&ap->free[0], 0, num_free_chains * sizeof(char *));
  182.     ap->malloc_chain = 0;
  183.     ap->saved = save;
  184.     ap->save_level++;
  185.     ap->changes = 0;
  186.     ap->saved_cbot = ap->cbot;
  187.     ap->saved_ctop = ap->ctop;
  188.     /* Clear the last_freed cache, because the cache pointer */
  189.     /* must point to a chunk at the current save level. */
  190.     ap->last_freed = 0;
  191.     if_debug3('u', "[u]save at %lx: cbot=%lx ctop=%lx\n", (ulong)save,
  192.           (ulong)ap->cbot, (ulong)ap->ctop);
  193.     set_in_save();
  194.     /* Notify the file machinery we just did a save. */
  195.     file_save();
  196.     return save;
  197. }
  198.  
  199. /* Allocate a ref-containing object and record it as new. */
  200. ref *
  201. alloc_refs(uint num_refs, const char *client_name)
  202. {    register alloc_state_ptr ap = alloc_state_current;
  203.     register alloc_change *cp;
  204.     register ref *obj = (ref *)alloc(num_refs, sizeof(ref), client_name);
  205.     if ( obj == 0 ) return 0;
  206.     if ( ap->save_level == 0 ) /* no saving */
  207.       return obj;
  208.     if ( num_refs == 0 )    /* no saving */
  209.       return obj;
  210.     cp = ap->changes;
  211.     if ( cp != 0 && cp->where == 0 && cp->contents.value.refs != 0 &&
  212.          obj == cp->contents.value.refs + r_size(&cp->contents) &&
  213.          /* Don't create a single block that is large enough to */
  214.          /* mislead the allocator into thinking it was allocated */
  215.          /* with a single malloc. */
  216.          r_size(&cp->contents) + num_refs < ap->big_size / sizeof(ref)
  217.        )
  218.        {    /* Merge adjacent allocations. */
  219.         r_inc_size(&cp->contents, num_refs);
  220. #ifdef DEBUG
  221. if ( gs_debug['U'] )
  222.    {    dprintf1("[u]alloc_refs(%s) merge", client_name);
  223.     alloc_save_print(cp);
  224.    }
  225. #endif
  226.        }
  227.     else
  228.        {    if ( cp == 0 || !alloc_change_is_free(cp) )
  229.            {    /* Allocate a pair of entries. */
  230.             if_not_alloc_change_pair(cp, ap, "alloc_refs")
  231.                {
  232.                 alloc_free((char *)obj, num_refs, sizeof(ref),
  233.                        client_name);
  234.                 return 0;
  235.                }
  236.            }
  237.         cp->where = 0;
  238.         r_set_size(&cp->contents, num_refs);
  239.         cp->contents.value.refs = obj;
  240. #ifdef DEBUG
  241. if ( gs_debug['U'] )
  242.    {    dprintf1("[u]alloc_refs(%s)", client_name);
  243.     alloc_save_print(cp);
  244.    }
  245. #endif
  246.        }
  247.     return obj;
  248. }
  249.  
  250. /* Deallocate a ref-containing object.  Only do this if the object */
  251. /* was allocated at the current save level, and we can remove */
  252. /* the save record for the allocation cleanly. */
  253. void
  254. alloc_free_refs(ref *ptr, uint num_refs, const char *client_name)
  255. {    register alloc_state_ptr ap = alloc_state_current;
  256.     alloc_change *cp = ap->changes;
  257.     ref *top = ptr + num_refs;
  258.     if_debug3('U', "[u]alloc_free_refs(%s) (%lx,%d)\n",
  259.           client_name, (ulong)ptr, num_refs);
  260.     if ( num_refs == 0 )    /* may point anywhere! */
  261.       return;
  262.     if ( ap->save_level == 0 )
  263.     {    /* Always OK to free if not saving. */
  264.         if_debug0('U', "[u]... not saving\n");
  265.         alloc_free((char *)ptr, num_refs, sizeof(ref),
  266.                client_name);
  267.         return;
  268.     }
  269.     /* Search the save chain for the allocation event. */
  270.     while ( cp != 0 )
  271.     {    ref *rbot;
  272.         if ( cp->where == 0 &&
  273.              ((rbot = cp->contents.value.refs) == ptr ||
  274.               rbot + r_size(&cp->contents) == top)
  275.            )
  276.         {    /* We can undo the allocation record cleanly. */
  277.             if_debug0('U', "[u]... succeeded\n");
  278.             r_inc_size(&cp->contents, -num_refs);
  279.             if ( rbot == ptr )
  280.                 cp->contents.value.refs = top;
  281.             alloc_free((char *)ptr, num_refs, sizeof(ref),
  282.                    client_name);
  283.             break;
  284.         }
  285.         cp = cp->next;
  286.     }
  287. }
  288.  
  289.  
  290. /* Record a state change that must be undone for restore, */
  291. /* and mark it as having been saved. */
  292. /* This can only be called if we are in a save. */
  293. int
  294. alloc_save_change(ref *where, const char *client_name)
  295. {    register alloc_state_ptr ap = alloc_state_current;
  296.     register alloc_change *cp;
  297.     if ( ap->save_level == 0 ) return 0;    /* no saving */
  298.     cp = ap->changes;
  299.     if ( cp == 0 || !alloc_change_is_free(cp) )
  300.        {    /* Allocate a pair of entries. */
  301.         if_not_alloc_change_pair(cp, ap, "alloc_save_change")
  302.            {    return -1;
  303.            }
  304.        }
  305.     cp->where = where;
  306.     ref_assign(&cp->contents, where);
  307. #ifdef DEBUG
  308. if ( gs_debug['U'] )
  309.    {    dprintf1("[u]save(%s)", client_name);
  310.     alloc_save_print(cp);
  311.    }
  312. #endif
  313.     if ( !r_is_packed(where) ) r_set_attrs(where, l_new);
  314.     return 0;
  315. }
  316.  
  317. /* Return the current save level */
  318. int
  319. alloc_save_level()
  320. {    return alloc_state_current->save_level;
  321. }
  322.  
  323. /* Test whether a reference would be invalidated by a restore. */
  324. int
  325. alloc_is_since_save(const char *ptr, const alloc_save *save)
  326. {
  327.     /* A reference can postdate a save in one of three ways: */
  328.     /*    - It is in the chunk that was current at the time */
  329.     /*        of the save, and allocated more recently. */
  330.     /*    - It is in a chunk allocated since the save; */
  331.     /*    - It was malloc'ed since the save; */
  332.  
  333.     register alloc_state_ptr ap = save->cap;
  334.  
  335.     if_debug2('U', "[U]is_since_save %lx, %lx:\n",
  336.           (ulong)ptr, (ulong)save);
  337.  
  338.     /* Check against current chunk at the time of the save */
  339.     if ( ptr_is_in_chunk(ptr, &save->state.current) )
  340.        {    /* In the chunk, check against allocation pointers */
  341.         /* at the time of the save */
  342.         if_debug2('U', "[U?]  current chunk %lx, %lx\n",
  343.              (ulong)save->state.cbot, (ulong)save->state.ctop);
  344.         return ( (ptr_ord_t)ptr >= (ptr_ord_t)save->state.cbot &&
  345.              (ptr_ord_t)ptr < (ptr_ord_t)save->state.ctop );
  346.        }
  347.  
  348.     /* Check against chunks allocated since the save */
  349.        {    const alloc_chunk *chunk = &ap->current;
  350.         while ( chunk->save_level > save->state.save_level )
  351.            {    if ( ptr_is_in_chunk(ptr, chunk) )
  352.                {    if_debug3('U', "[U+]  new chunk %lx: %lx, %lx\n", chunk,
  353.                       (ulong)chunk->base, (ulong)chunk->limit);
  354.                 return 1;
  355.                }
  356.             chunk = chunk->next;
  357.            }
  358.        }
  359.  
  360.     /* Check the malloc chains since the save */
  361.        {    const alloc_state *asp = ap;
  362.         for ( ; asp != &save->state; asp = &asp->saved->state )
  363.            {    const alloc_block *mblk = asp->malloc_chain;
  364.             for ( ; mblk != 0; mblk = mblk->next )
  365.               if ( alloc_block_size + (char *)mblk == ptr )
  366.                {    if_debug0('U', "[U+]  malloc'ed\n");
  367.                 return 1;
  368.                }
  369.            }
  370.        }
  371.  
  372.     /* Not in any of those places, must be OK. */
  373.     return 0;
  374. }
  375.  
  376. /* Test whether a name would be invalidated by a restore. */
  377. int
  378. alloc_name_is_since_save(const ref *pnref, const alloc_save *save)
  379. {    return name_is_since_count(pnref, save->name_cnt);
  380. }
  381.  
  382. /* Validate a saved state pointer. */
  383. int
  384. alloc_restore_state_check(const alloc_save *save)
  385. {    const alloc_save *sprev = save->cap->saved;
  386.     while ( sprev != save )
  387.        {    if ( sprev == 0 ) return -1;    /* not on chain */
  388.         sprev = sprev->state.saved;
  389.        }
  390.     return 0;
  391. }
  392.  
  393. /* Restore the state.  The client is responsible for calling */
  394. /* alloc_restore_state_check first, and for ensuring that */
  395. /* there are no surviving pointers for which alloc_is_since_save is true. */
  396. void
  397. alloc_restore_state(alloc_save *save)
  398. {    register alloc_state_ptr ap = save->cap;
  399.     alloc_save *sprev;
  400.  
  401.     if_debug1('u', "[u]restore from %lx\n", (ulong)save);
  402.  
  403.     /* Iteratively restore the state */
  404.     do
  405.       { sprev = ap->saved;
  406.  
  407.         /* Close inaccessible files. */
  408.         file_restore(save);
  409.  
  410.         /* Remove entries from font and character caches. */
  411.         font_restore(save);
  412.  
  413.         /* Adjust the name table. */
  414.         name_restore(sprev->name_cnt);
  415.  
  416.         /* Undo changes since the save. */
  417.         { alloc_change *cp = ap->changes;
  418.           while ( cp )
  419.         {
  420. #ifdef DEBUG
  421. if ( gs_debug['U'] )
  422.    {        dprintf("[U]restore");
  423.         alloc_save_print(cp);
  424.    }
  425. #endif
  426.           if ( cp->where )
  427.             ref_assign(cp->where, &cp->contents);
  428.           else if ( cp->contents.value.refs != 0 )    /* might be an unfilled save record */
  429.             { alloc_free((char *)cp->contents.value.refs,
  430.                  r_size(&cp->contents), sizeof(ref),
  431.                  "alloc_restore_state");
  432.             }
  433.           cp = cp->next;
  434.         }
  435.         }
  436.  
  437.         /* Free chunks allocated since the save. */
  438.         { alloc_chunk *cp = ap->current_ptr;
  439.           *cp = ap->current;    /* update in memory */
  440.         }
  441.         while ( ap->current.save_level == ap->save_level )
  442.           {    byte *cp = (byte *)ap->current_ptr;
  443.         uint csize = ap->climit - cp;
  444.         ap->current_ptr = ap->current.next;
  445.         ap->current = *ap->current_ptr;
  446.         (*ap->pfree)((char *)cp, 1, csize, "alloc_restore_state(chunk)");
  447.           }
  448.  
  449.         /* Free blocks allocated with malloc since the save. */
  450.         /* Since we reset the chain when we did the save, */
  451.         /* we just free all the objects on the current chain. */
  452.         { while ( ap->malloc_chain != 0 )
  453.         { alloc_block *mblock = ap->malloc_chain;
  454.           ap->malloc_chain = mblock->next;
  455.           (*ap->pfree)((char *)mblock,
  456.                    1, alloc_block_size + mblock->size,
  457.                    "alloc_restore_state(malloc'ed)");
  458.         }
  459.         }
  460.  
  461.         /* Restore the allocator state. */
  462.         *ap = sprev->state;
  463.         alloc_free((char *)sprev, 1, sizeof(alloc_save),
  464.                "alloc_restore_state");
  465.  
  466.       }
  467.     while ( sprev != save );
  468.  
  469.     /* Clean up */
  470.     if ( sprev != 0 )
  471.       ap->saved_cbot = sprev->state.cbot,
  472.       ap->saved_ctop = sprev->state.ctop;
  473.     /* Clear the last_freed cache, because the cache pointer */
  474.     /* must point to a chunk at the current save level. */
  475.     ap->last_freed = 0;
  476.     if ( ap->save_level == 0 )
  477.       set_not_in_save();
  478.     /* Set the l_new attribute in all slots that have been saved. */
  479.     save_set_new(ap, l_new);
  480. }
  481.  
  482. /* ------ Internal routines ------ */
  483.  
  484. /* Set or reset the l_new attribute in every slot on the current */
  485. /* change chain. */
  486. private void
  487. save_set_new(alloc_state_ptr ap, int new)        /* l_new or 0 */
  488. {    register alloc_change *cp = ap->changes;
  489.     for ( ; cp; cp = cp->next )
  490.     {    ref *rp = cp->where;
  491.         if ( rp != 0 )
  492.         {    if ( !r_is_packed(rp) )
  493.                 rp->tas.type_attrs =
  494.                   (rp->tas.type_attrs & ~l_new) + new;
  495.         }
  496.         else
  497.         {    register ushort size = r_size(&cp->contents);
  498.             if ( size )
  499.             {    register ref *ep = cp->contents.value.refs;
  500.                 if ( new )
  501.                     do
  502.                     {    if ( !r_is_packed(ep) )
  503.                           ep->tas.type_attrs |= l_new;
  504.                         ep++;
  505.                     }
  506.                     while ( --size );
  507.                 else
  508.                     do
  509.                     {    if ( !r_is_packed(ep) )
  510.                           ep->tas.type_attrs &= ~l_new;
  511.                         ep++;
  512.                     }
  513.                     while ( --size );
  514.             }
  515.         }
  516.     }
  517. }
  518.