home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / opendc12.zip / od124os2.exe / od12osr1.exe / src / FreeSpce.c < prev    next >
C/C++ Source or Header  |  1997-03-21  |  44KB  |  955 lines

  1. /* @(#)Z 1.4 com/src/cm/FreeSpce.c, odstorage, od96os2, odos29712d 97/03/21 17:19:36 (96/10/29 09:18:19) */
  2. /*====START_GENERATED_PROLOG======================================
  3.  */
  4. /*
  5.  *   COMPONENT_NAME: odstorage
  6.  *
  7.  *   CLASSES: none
  8.  *
  9.  *   ORIGINS: 82,27
  10.  *
  11.  *
  12.  *   (C) COPYRIGHT International Business Machines Corp. 1995,1996
  13.  *   All Rights Reserved
  14.  *   Licensed Materials - Property of IBM
  15.  *   US Government Users Restricted Rights - Use, duplication or
  16.  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  17.  *       
  18.  *   IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  19.  *   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20.  *   PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  21.  *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  22.  *   USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  23.  *   OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  24.  *   OR PERFORMANCE OF THIS SOFTWARE.
  25.  */
  26. /*====END_GENERATED_PROLOG========================================
  27.  */
  28.  
  29. /*
  30.     File:        FreeSpce.c 
  31.  
  32.     Contains:    Container Manager Free Space Value Management Routines
  33.  
  34.     Written by:    Ira L. Ruben
  35.  
  36.     Owned by:    Ed Lai
  37.  
  38.     Copyright:    ⌐ 1992-1994 by Apple Computer, Inc., all rights reserved.
  39.  
  40.     Change History (most recent first):
  41.  
  42.          <6>     9/26/95    EL        1286040: Make sure space from global names
  43.                                     do not go to free space if it is not on
  44.                                     disk.
  45.          <5>     8/16/95    EL        1238951: Bento file has wrong deleted-space
  46.                                                     value.
  47.          <4>     12/9/94    EL        #1182275 Optionally do not maintain
  48.                                                     continue flag. Keep all free space, no
  49.                                                     matter how small and delete small segment
  50.                                                     only on closing.
  51.          <3>     9/30/94    EL        #1182275 rename mergeCandidate to
  52.                                                     updateMergeCandidate.
  53.          <2>     8/26/94    EL        #1182275 Handling of temporary free list.
  54.                                                     Correct value in spaceDeletedValue in
  55.                                                     merging. Avoid giving out highly fragmented
  56.                                                     blocks.
  57.          <4>     5/10/94    EL        #1162327. ValueDoNotFree is no longer
  58.                                                     needed.
  59.          <3>      4/6/94    EL        Do not free free space property when it is
  60.                                                     taken over. #1150214
  61.          <2>     3/31/94    EL        Check to see if new free space is just
  62.                                                     behind another free segment. #1150214
  63.          <1>      2/3/94    EL        first checked in
  64.          <5>     1/19/94    EL        Free space just released will not be used
  65.                                                     immediately, but will be keep in a
  66.                                                     temporary list that is merge to the
  67.                                                     permanent free list at close time.
  68.          <4>      1/6/94    EL        Add cmTakeOverFreeList call to transfer the
  69.                                                     free list.
  70.          <3>    11/16/93    EL        Correct the value of spaceDeletedValue.
  71.          <2>     11/4/93    EL        Do not put deleted space in target
  72.                                                     container into the free list. Arrange data
  73.                                                     in free list in ascending order so that it
  74.                                                     can be merged into larger blocks.
  75.  
  76.     To Do:
  77. */
  78.  
  79. /*---------------------------------------------------------------------------*
  80.  |                                                                           |
  81.  |                            <<< FreeSpce.c  >>>                            |
  82.  |                                                                           |
  83.  |          Container Manager Free Space Value Management Routines           |
  84.  |                                                                           |
  85.  |                               Ira L. Ruben                                |
  86.  |                                 4/23/92                                   |
  87.  |                                                                           |
  88.  |                    Copyright Apple Computer, Inc. 1992-1994               |
  89.  |                           All rights reserved.                            |
  90.  |                                                                           |
  91.  *---------------------------------------------------------------------------*
  92.  
  93.  This file contains routines for reusing deleted data space.  The routines record the
  94.  deleted space and reuse it for value data writes.
  95.  
  96.  The free space is maintained as a list of value segment entries for a "free space"
  97.  property belonging to the TOC ID 1 entry.  It is written to the container like other ID 1
  98.  properties to keep track of all the deleted space.
  99.  
  100.  If a container is opened for reusing free space, we use the free list to reuse the space.
  101. */
  102.  
  103.  
  104. #include <stddef.h>
  105. #include <stdio.h>
  106.  
  107. #ifndef __CMTYPES__
  108. #include "CMTypes.h"
  109. #endif
  110. #ifndef __CM_API__
  111. #include "CMAPI.h"
  112. #endif
  113. #ifndef __LISTMGR__
  114. #include "ListMgr.h"
  115. #endif
  116. #ifndef __TOCENTRIES__
  117. #include "TOCEnts.h"   
  118. #endif
  119. #ifndef __TOCOBJECTS__
  120. #include "TOCObjs.h"   
  121. #endif
  122. #ifndef __TOCIO__
  123. #include "TOCIO.h"
  124. #endif
  125. #ifndef __GLOBALNAMES__
  126. #include "GlbNames.h"   
  127. #endif
  128. #ifndef __CONTAINEROPS__
  129. #include "Containr.h"  
  130. #endif
  131. #ifndef __HANDLERS__
  132. #include "Handlers.h"
  133. #endif
  134. #ifndef __SESSIONDATA__
  135. #include "Session.h"          
  136. #endif
  137. #ifndef __FREESPACE__
  138. #include "FreeSpce.h" 
  139. #endif
  140.                                                                     
  141.                                                                     
  142.                                                                     CM_CFUNCTIONS
  143.  
  144. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  145. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  146. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  147. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  148. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  149.  
  150. #if CM_MPW
  151. #pragma segment TOCEntries
  152. #endif
  153.  
  154. /*----------------------------------------------------------------*
  155.  | removeEmptyFreeSpace - remove free space header if it is empty |
  156.  *----------------------------------------------------------------*
  157.  
  158.  Check to see if free space is empty. If it is, completely remove that property.
  159. */
  160.  
  161. static void CM_NEAR removeEmptyFreeSpace(ContainerPtr container)
  162. {
  163.     TOCObjectPtr     theTOCObject;
  164.     TOCPropertyPtr theFreeListProperty;
  165.     /* If there are no more free list entries, delete the value header and the "free             */
  166.     /* space" property from TOC ID 1...                                                                                                        */
  167.         
  168.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  169.     if (freeSpaceValueHdr) {
  170.         if (cmIsEmptyList(&freeSpaceValueHdr->valueList)) {            /* if no more free list...    */
  171.             theFreeListProperty = freeSpaceValueHdr->theProperty;    /* ...get owning property        */
  172.             theTOCObject                 = theFreeListProperty->theObject;    /* ...get owning object (1)    */
  173.             CMfree(freeSpaceValueHdr);                                                        /* ...clobber the value hdr    */
  174.             CMfree(cmDeleteListCell(&theTOCObject->propertyList, theFreeListProperty));
  175.             container->freeSpaceValueHdr = NULL;                                    /* ...no more free list            */
  176.         }
  177. #if CMKEEP_CONTINUE_FLAG
  178.         else if (cmCountListCells(&freeSpaceValueHdr->valueList) == 1)
  179.             freeSpaceValueHdr->valueFlags &= ~ValueContinued;            /* only 1, then no cont            */
  180. #endif
  181.     }
  182. }
  183.  
  184. /*------------------------------------------------*
  185.  | deleteFreeListEntry - delete a free list entry |
  186.  *------------------------------------------------*
  187.  
  188.  This routine removes the specified free list entry and frees up its space.  If this is 
  189.  the last entry of the free list, the free list property itself (along with its value
  190.  header, of course) are removed.
  191.  
  192.  Note, this routine is a low-level routine called from the other routines in this file.
  193.  As such there are no error checks.  The caller is assumed "happy" with what its doing by
  194.  the time it calls this routine.
  195. */
  196.  
  197. static void CM_NEAR deleteFreeListEntry(TOCValuePtr theValue)
  198. {
  199.     ContainerPtr      container = theValue->theValueHdr->container;
  200.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  201. #if CMKEEP_CONTINUE_FLAG
  202.     TOCValuePtr         prevValue;
  203.     
  204.     prevValue = (TOCValuePtr) cmGetPrevListCell(theValue);
  205.     if (prevValue) {
  206.         prevValue->flags = theValue->flags;                /* if theValue is last, now you are last     */
  207.     }
  208. #endif
  209.  
  210.     /* Delete the value entry from its list and free its space...                                                    */
  211.     
  212.     freeSpaceValueHdr->size -= theValue->value.notImm.valueLen;
  213.     CMfree(cmDeleteListCell(&freeSpaceValueHdr->valueList, theValue));
  214.     
  215.     removeEmptyFreeSpace(container);                        /* remove the property if it is empty            */
  216. }
  217.  
  218. /*----------------------------------------------------------------------------------*
  219.  | rearrangePermFreeList - arrange items in free list in ascending order and merged |
  220.  *----------------------------------------------------------------------------------*
  221.  
  222.     !.0d5 or earlier code writes a free list that is not in sorted order.
  223.  
  224.     This version expects free list item to be in sorted order.
  225.     
  226.     We do this by moving all the items in the perm free list into the temp free list.
  227.     Then we move it back from the temp free list to the perm free list.
  228.     Since the 2nd operation always ensure list items are in ascending order and
  229.     adjacent items would be combined into one. We can back the list we want.
  230.     
  231.     However we do not want to move items originally in the temp free list to the perm
  232.     free list. So we save the temp free list in the beginning and restore the temp free 
  233.     list at the end.
  234. */
  235.  
  236. static void CM_NEAR rearrangePermFreeList(ContainerPtr container)
  237. {
  238.     ListHdr                 saveList = container->tmpList;
  239.     TOCValuePtr             theValue;
  240.     CM_ULONG                size;
  241.     
  242.     cmInitList(&container->tmpList);    
  243.     theValue = (TOCValuePtr)cmGetListHead(&container->freeSpaceValueHdr->valueList);
  244.     
  245.     while (theValue) {                                                            /* scan the free space list...                */
  246.         size = theValue->value.notImm.valueLen;
  247.         container->spaceDeletedValue->value.imm.ulongValue -= size;
  248.         cmAddToTmpFreeList(container, theValue->value.imm.ulongValue, size);
  249.         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  250.     }    
  251.     container->freeSpaceValueHdr->size = 0;                                        /* we removed everything        */
  252.     cmFreeAllListCells(&container->freeSpaceValueHdr->valueList, SESSION);
  253.     cmCopyTmpFreeToTOC(container);            /* move from temp tmp list back to perm free list    */
  254.     
  255.     container->tmpList = saveList;
  256. }
  257.  
  258. /*------------------------------------------------------------------------*
  259.  | addToPermFreeList - add freed space to the permanent "free space" list |
  260.  *------------------------------------------------------------------------*
  261.  
  262.     This routine takes some free space and put it into the permanent free space property
  263.     for use in future.
  264.  
  265.   As part of the freeing process, we scan the free list to see if the new space can be 
  266.     combined with an already existing entry.  It's a sort of on-the-fly garbage collector.
  267.     
  268.     For new containers, there is no "free space" property initially for ID 1.  It is created
  269.     here the first time we need to free space.  We remember the value header for the "free
  270.     space" property so that we may efficiently get at the list on all future calls.
  271. */
  272.  
  273. static void CM_NEAR addToPermFreeList(ContainerPtr container,
  274.                                               CM_ULONG offset, CM_ULONG size)
  275. {
  276.     TOCObjectPtr      theTOCobject;
  277.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  278.     TOCValuePtr         theValue = NULL, nextValue;
  279.     CM_ULONG              valueStart, valueEndPlus1, prevStart;
  280.     TOCValueBytes  valueBytes;
  281.     void                     *toc;
  282.     
  283.         
  284.     /* See if the space we're freeing can be combined with some already existing free            */
  285.     /* space.  If the new space is already freed (now how could that happen?) we ignore        */
  286.     /* the new space.  If the chunk of space we are about to free is less than the size        */
  287.     /* of a TOC entry, we ignore it (losing track of that space), since it would cost more*/
  288.     /* to remember it.  However, if that space can be combined with already existing             */
  289.     /* space, we do the combining.                                                                                                                */ 
  290.     
  291.     if (freeSpaceValueHdr != NULL) {                                /* if free space list exists...                */
  292.         theValue = (TOCValuePtr)cmGetListHead(&freeSpaceValueHdr->valueList);
  293.         prevStart = 0;
  294.         
  295.         while (theValue) {                                                        /* scan the free space list...                */
  296.             valueStart         = theValue->value.imm.ulongValue;
  297.             valueEndPlus1 = valueStart + theValue->value.notImm.valueLen;
  298.             if (prevStart > valueStart) {
  299.                 /* the list is not in order, must have been written by 1.0d5 or earlier software*/
  300.                 rearrangePermFreeList(container);                            /* make sure it is in order first    */
  301.                 addToPermFreeList(container, offset, size);        /* now do it again                                */
  302.                 return;
  303.             }
  304.             prevStart = valueStart;
  305.             if (offset >= valueStart) {                                                     /* this is after this                */
  306.                 if (offset <= valueEndPlus1) {                                             /* have overlap or concat        */
  307.                     if (offset + size > valueEndPlus1) {            /* combine new amount with this entry*/
  308.                         size = (offset + size) - valueEndPlus1;    /* reduce actual size to add in                */
  309.                         theValue->value.notImm.valueLen += size;/* this is what we combine in                    */
  310.                         freeSpaceValueHdr->size += size;                /* echo total size in header                    */
  311.                         container->spaceDeletedValue->value.imm.ulongValue += size;
  312.                         /* we may able to join with the next value to form a single value                        */
  313.                         valueEndPlus1 += size;
  314.                         nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  315.                         if (nextValue) 
  316.                             if (nextValue->value.imm.ulongValue <= valueEndPlus1) {
  317.                                 /* we can combine with next value, absorb the next value in this one        */
  318.                                 theValue->value.notImm.valueLen = nextValue->value.imm.ulongValue +
  319.                                     nextValue->value.notImm.valueLen - theValue->value.imm.ulongValue;
  320.                                 /* we bump up the free space to cancel out deleteFreeListEntry                    */
  321.                                 freeSpaceValueHdr->size += nextValue->value.imm.ulongValue;
  322.                                 /* now everything in next value in in this one, we can delete next value*/
  323.                                 deleteFreeListEntry(nextValue);
  324.                             }
  325.                     }
  326.                     return;                                                                        /* exit since we updated "old" entry*/
  327.                 }
  328.             } else if (offset + size >= valueStart) {            /* can combine from the lower end        */
  329.                 theValue->value.imm.ulongValue = offset;        /* this is the low end                            */
  330.                 if (offset + size > valueEndPlus1)
  331.                     valueEndPlus1 = offset + size;
  332.                 size = valueEndPlus1 - offset - theValue->value.notImm.valueLen; /* increment */
  333.                 theValue->value.notImm.valueLen += size;/* this is what we combine in                    */
  334.                 freeSpaceValueHdr->size += size;                /* echo total size in header                    */
  335.                 container->spaceDeletedValue->value.imm.ulongValue += size;
  336.                 return;                                                                        /* exit since we updated "old" entry    */
  337.             } else
  338.                 break;                                                                            /* it should be inserted before here*/
  339.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  340.         }
  341.     } /* checking for combining */
  342.     
  343. #if 0    
  344.     /* Since the cost of recording the free space in the container will be a TOC entry, we*/
  345.     /* only record free space chucks whose size is LARGER than a TOC entry.  Since we         */
  346.     /* couldn't combine it with already existing space, we simply exit and forget it.            */
  347.  
  348.     #if TOC1_SUPPORT
  349.     if (container->majorVersion == 1) {
  350.         if (size <= TOCentrySize) return;
  351.     } else 
  352.         if (size <= MinTOCSize) return;
  353.     #else
  354.     if (size <= MinTOCSize) return;
  355.     #endif
  356. #endif
  357.     /* If this is the first freed space in the container, create the "free space" property*/
  358.     /* for the TOC object 1.  Otherwise, just use it. We remember the pointer to the value*/
  359.     /* header for this property in the container.  Note, the TOC we want to deal with here*/
  360.     /* is the container's own (private) TOC.  This will be different from the current TOC    */
  361.     /* when we're updating containers.                                                                                                        */
  362.     
  363.     if (freeSpaceValueHdr == NULL) {                                /* if 1st free space for container...    */
  364.         toc = container->toc;                                                    /* ...remember current TOC                        */
  365.         container->toc = container->privateTOC;                /* use container's own (private) TOC    */
  366.         
  367.         theTOCobject = cmDefineObject(container,            /* ...create "free space" property        */
  368.                                                                      CM_StdObjID_TOC, CM_StdObjID_TOC_Free,
  369.                                                                     CM_StdObjID_TOC_Type, NULL,
  370.                                                                      container->generation, 0,
  371.                                                                     (ObjectObject | ProtectedObject),
  372.                                                                      &freeSpaceValueHdr);
  373.         
  374.         container->toc = toc;                                                    /* restore current container                    */
  375.         if (theTOCobject == NULL) return;
  376.         freeSpaceValueHdr->valueFlags |= ValueProtected;/* don't allow writing to this value*/
  377.         container->freeSpaceValueHdr = freeSpaceValueHdr;
  378.     } 
  379.  
  380.     /* At this point we want to create a new free list entry for the newly freed offset     */
  381.     /* size.  The value list are standard value entries for the "free space" property of    */
  382.     /* TOC object ID 1.                                                                                                                                        */
  383.  
  384.     container->spaceDeletedValue->value.imm.ulongValue += size;
  385.  
  386.     (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  387.     
  388. #if CMKEEP_CONTINUE_FLAG
  389.     if (theValue) {                                                                            /* it should come before this            */
  390.         /* create a new value, and insert before theValue                                                                        */
  391.         /* if we are successful, then update the value header to reflect it                                    */
  392.         if (cmInsertBeforeListCell(&freeSpaceValueHdr->valueList, 
  393.                                                              cmCreateValueSegment(freeSpaceValueHdr, &valueBytes, kCMContinued), 
  394.                                                              theValue)) {
  395.             freeSpaceValueHdr->valueFlags |= ValueContinued;/* also set a more convenient flag    */
  396.             freeSpaceValueHdr->size += size;                                /* echo total size in header                    */
  397.         }
  398.     } else {                                                                                        /* we should put it at very end        */
  399.         theValue = (TOCValuePtr)cmGetListTail(&freeSpaceValueHdr->valueList);
  400.         if (theValue) {
  401.             theValue->flags |= kCMContinued;                                /* flag the current value as cont'd    */
  402.             freeSpaceValueHdr->valueFlags |= ValueContinued;/* also set a more convenient flag    */
  403.         }
  404.         cmAppendValue(freeSpaceValueHdr, &valueBytes, 0);
  405.     }
  406. #else
  407.     if (theValue) {                                                                            /* it should come before this            */
  408.         /* create a new value, and insert before theValue                                                                        */
  409.         /* if we are successful, then update the value header to reflect it                                    */
  410.         if (cmInsertBeforeListCell(&freeSpaceValueHdr->valueList, 
  411.                                                              cmCreateValueSegment(freeSpaceValueHdr, &valueBytes, 0), 
  412.                                                              theValue)) {
  413.             freeSpaceValueHdr->size += size;                                /* echo total size in header                    */
  414.         }
  415.     } else {                                                                                        /* we should put it at very end        */
  416.         theValue = (TOCValuePtr)cmGetListTail(&freeSpaceValueHdr->valueList);
  417.         cmAppendValue(freeSpaceValueHdr, &valueBytes, 0);
  418.     }
  419. #endif
  420. }
  421.  
  422. /*-----------------------------------------------------------------------*
  423.  | cmAddToTmpFreeList - add freed space to the temporary "free space" list |
  424.  *-----------------------------------------------------------------------*
  425.  
  426.     This routine takes some free space and put it into the temporary free space property
  427.     for use in future.
  428.     
  429.     We want to put thing in the temporary free list for two reasons.
  430.     
  431.     For container opened for writing, we don't want to write into the old container
  432.     before we close it in case it crashes before it is closed, so we put it into
  433.     the temporary free list.
  434.     
  435.     For container opened for updating, we have free space that can be reused if
  436.     it is merged. Since we do not know whether we will just close the container
  437.     or merge it, we put it in the temporary free list and put it on the real
  438.     free list only if we do a merge at close time.
  439.  
  440. */
  441.  
  442. void cmAddToTmpFreeList(ContainerPtr container, CM_ULONG offset, CM_ULONG size)
  443. {
  444.   FreeSpaceItem    *toBeFree;
  445.  
  446.     toBeFree = CMmalloc(sizeof(FreeSpaceItem));            /* so we can get rid of SCpp warning*/
  447.     if (toBeFree) {
  448.         toBeFree->offset = offset;
  449.         toBeFree->size = size;
  450.         cmAppendListCell(&container->tmpList, toBeFree);
  451.     }
  452. }
  453.  
  454. /*-----------------------------------------------------------------------------------*
  455.  | cmAddValueToTmpFreeList - add space in a value to the temporary "free space" list |
  456.  *-----------------------------------------------------------------------------------*
  457.  
  458.     This routine put all the free space used by a value into the temporary free list
  459.  
  460. */
  461.  
  462. void cmAddValueToTmpFreeList(TOCValueHdrPtr theValueHdr)
  463. {
  464.     TOCValuePtr theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
  465.  
  466.     while (theValue) {
  467.         /* use targetContainer->updatingContainer because during applyUpdate                        */
  468.         /* updatingContainer point to itself, so we need to get the real updating cntr    */
  469.         cmAddToTmpFreeList(theValueHdr->container->targetContainer->updatingContainer,
  470.                                              theValue->value.notImm.value,
  471.                                              theValue->value.notImm.valueLen);
  472.         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  473.     }
  474. }
  475.  
  476. /*----------------------------------------------------------------------*
  477.  | cmAddToFreeList - add freed space to the permanent "free space" list |
  478.  *----------------------------------------------------------------------*
  479.  
  480.  This routine is called whenever space for value data is to be freed.  The space is
  481.  recorded in a free list. The free list entries are maintained as value segments belonging
  482.  to the "free space" property of TOC object ID 1.
  483.  
  484.  The amount of space to be freed is specified in one of two possible ways:
  485.  
  486.          1. By passing a pointer to a value segment in theValueToFree.  The offset and size
  487.              are extracted from the segment info.  If theValueToFree is for an immediate value,
  488.              we simply exit since there is no container space to actually free.  Global names,
  489.              as usual, are handled specially.  In particular, for no global names that have not
  490.              yet been written to the container yet (we keep 'em in memory unto we write the TOC
  491.              at container close time), then we ingore them just like immediates.  For "old"
  492.              global names we do account for the space.
  493.              
  494.         2. If theValueToFree is passed as NULL, then an explicit size and offset may be
  495.              passed.  Note that a non-null theValueToFree has precedence over explicit offset
  496.              and size.
  497. */
  498.  
  499. void cmAddToFreeList(ContainerPtr container, TOCValuePtr theValueToFree,
  500.                                         CM_ULONG offset, CM_ULONG size)
  501. {
  502.     Boolean        mayGoToTmpList = false;
  503.  
  504.     /* We only track free space when a container is opened for writing or updating.              */
  505.     /* However, even when writing there is an override switch to suppress the space                */
  506.     /* tracking.  This is done during applying updates during open time.  We don't want        */
  507.     /* to track what's going on then!                                                                                                            */
  508.     
  509.     if (!container->trackFreeSpace ||                                /* if no tracking of free space or...    */
  510.           (container->useFlags & kCMWriting) == 0)        /* ...not opened for writing/updating    */
  511.         return;                                                                                /* ...exit                                                        */
  512.     
  513.     /* Get the offset and size from the value if it's specified. This is the amount being    */
  514.     /* freed.  If theValueToFree is NULL, then the offset and size must be explicitly         */
  515.     /* specified, otherwise these parameters are ignored.  As a safety, if the value is        */
  516.     /* passed, but it is for an immediate value, there is no space being given up, so we    */
  517.     /* just exit.                                                                                                                                                    */
  518.     
  519.     if (theValueToFree != NULL) {                                        /* if value explicitly specified...        */
  520.         if ((theValueToFree->container->depth == 1) && (container->useFlags & kCMMerging))
  521.             mayGoToTmpList = true;
  522.         else if (theValueToFree->container != container)/* does it belong to this level?        */
  523.             return;                                                                                /* no, then just return                            */
  524.         if (theValueToFree->flags & kCMGlobalName) {    /* ...handle global names specially        */
  525.             if (theValueToFree->theValueHdr->valueRefCon == 0) 
  526.                 return;                                                                     /* not written, nothing to free                */
  527.             offset = theValueToFree->value.globalName.offset;    
  528.             if (offset == 0) return;                                         /* this is an extra precaution check */
  529.             size = GetGlobalNameLength(theValueToFree->value.globalName.globalNameSymbol) + 1;
  530.         } else if (theValueToFree->flags & kCMImmediate)
  531.             return;                                                                            /* ...ignore immediates...                        */
  532.         else {                                                                                /* ...have "normal" value                            */
  533.             offset = theValueToFree->value.notImm.value;
  534.             size      = theValueToFree->value.notImm.valueLen;
  535.         }
  536.     }
  537.     
  538.     if (size == 0) return;
  539.     
  540.     if (mayGoToTmpList || (container->useFlags & (kCMUpdateByAppend | kCMUpdateTarget) == 0)) {
  541.         /* If the space is from previous container, or in the same container but we are not */
  542.         /* doing an updating, do not reuse freespace until the container is closed.                    */
  543.         /* So even if we crashes before the container is closed, old data is not destroyed    */
  544.         cmAddToTmpFreeList(container, offset, size);
  545.     }
  546.     else {    
  547.         /* for updating, we do not touch the data in old container directly, so any free      */
  548.         /* space comes from the new container and we can put it into the free list directly */
  549.         addToPermFreeList(container, offset, size);
  550.     }
  551. }
  552.  
  553. /*------------------------------------------------------*
  554.  | tmpListCB - add freed space to the "free space" list |
  555.  *------------------------------------------------------*
  556.  
  557.  Action routine to take a free space and add it to the permanent free space list.
  558.  */
  559.  
  560. static void CM_NEAR tmpListCB(void * theCell, CMRefCon refCon)
  561. {
  562.     FreeSpaceItem    *toBeFree = (FreeSpaceItem *)theCell;
  563.  
  564.     addToPermFreeList((ContainerPtr)refCon, toBeFree->offset, toBeFree->size);
  565. }
  566.  
  567. /*---------------------------------------------------------------*
  568.  | cmCopyTmpFreeToTOC - add freed space to the "free space" list |
  569.  *---------------------------------------------------------------*
  570.  
  571.     This routine goes through the list of temporary free space and merge each one into
  572.     the permanent free space. At the end the temporary free space are removed.
  573. */
  574.  
  575. void cmCopyTmpFreeToTOC(ContainerPtr container)
  576. {
  577.     cmForEachListCell(&container->tmpList, (CMRefCon)container, tmpListCB);
  578.  
  579.   cmFreeAllListCells(&container->tmpList, container->sessionData);
  580. }
  581.  
  582. /*-----------------------------------------------------------------------*
  583.  | cmRemoveSmallFreeSpace - remove segment too small to be worth keeping |
  584.  *-----------------------------------------------------------------------*
  585.  
  586.  There is certain overhead of storing a value segment. If a free space segment is so small
  587.  that the overhead of storing it is greater than the free space itself, then it is not
  588.  worth to keep the free space. So we go through the free space list and remove the small
  589.  segments.
  590.  
  591.  On the other hand, later we may free space that are are around these small segments.
  592.  So if we ignore these small segments, we may cause fragmentation of the free space
  593.  later. So we should revisit this issue to see if it is worthwhile to keep them anyway.
  594. */
  595.  
  596. void cmRemoveSmallFreeSpace(ContainerPtr container)
  597. {
  598.     TOCValuePtr         theValue, nextValue;
  599.     
  600.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  601.     if (freeSpaceValueHdr) {
  602.         theValue = (TOCValuePtr)cmGetListHead(&freeSpaceValueHdr->valueList);
  603.         while (theValue) {
  604.             nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
  605.             /* If the free space is smaller than overhead of keeping track of it, remove it             */
  606.             if (theValue->value.notImm.valueLen < MinTOCSize) {
  607.                 freeSpaceValueHdr->size -= theValue->value.notImm.valueLen;
  608.                 CMfree(cmDeleteListCell(&freeSpaceValueHdr->valueList, theValue));
  609.             }
  610.             theValue = nextValue;
  611.         } /* while value */
  612.         
  613.         removeEmptyFreeSpace(container);
  614.     }
  615. }
  616.  
  617.  
  618. /*--------------------------------------------------------------*
  619.  | cmGetFreeListEntry - get a free space entry from "free list" |
  620.  *--------------------------------------------------------------*
  621.  
  622.  This routine returns one free list entry (if one exists).  The offset from that entry is
  623.  returned as the function result.  The size is returned in actualSize.  If there is no 
  624.  free space, or we can't find one big enough (see mustAllFit below), 0 is returned as the
  625.  function result and for the actualSize.  0's are also returned if the container was not
  626.  opened to reuse free space.
  627.  
  628.  The desiredSize is passed as what the caller would like as the single free list entry
  629.  amount, i.e., a "best fit".  It is also used when we find a bigger free list entry and
  630.  only have to reduce part of it.
  631.  
  632.  If mustAllFit is true, then we MUST find a single free list segment big enough or 0 will
  633.  be returned.  This is used for data that is not allowed to be continued with multiple
  634.  value segments (e.g., global names).
  635.  
  636.  The free list is built by cmAddToFreeList() as value segments belonging to a value header
  637.  of the "free space" property for TOC object ID 1.   If all the free list entries are
  638.  exhausted, the value header is freed along with the property.  If space is ever given up
  639.  after this through cmAddToFreeList(), it will recreate the "free space" property, its
  640.  value header, and the free list value entry segments.
  641. */
  642.  
  643. CM_ULONG cmGetFreeListEntry(ContainerPtr container, 
  644.                                                         CM_ULONG desiredSize, Boolean mustAllFit,
  645.                                                         CM_ULONG *actualSize)
  646. {
  647.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  648.     TOCValuePtr         theValue;
  649.     CM_ULONG              offset;
  650. #if MinFragmentSize
  651.     CM_ULONG              minSegmentSize;
  652. #endif
  653.  
  654.     /* Return null amount if there is no free list or not updating...                                            */
  655.     
  656.     if (freeSpaceValueHdr == NULL || (container->useFlags & kCMReuseFreeSpace) == 0)
  657.         return (*actualSize = 0);
  658.     
  659.     /* Use the next available free list entry (segment) no matter what it is unless we         */
  660.     /* mustAllFit.  For mustAllFit we must scann the free list for a single entry large        */
  661.     /* enough to hold the entire data (desiredSize bytes).                                                                */
  662.     
  663.     /* Note as just said, when mustAllFit is false, we just use the first free list             */
  664.     /* segment on the list.  A better algorithm might be to use the desired amount and         */
  665.     /* find a "best fit" by scanning the free list just as in the mustAllFit case.  So         */
  666.     /* which is better in time and/or space?  A scan each time or just reusing each entry */
  667.     /* unconditionally.  The latter guarantees reuse of all available space.  But the         */
  668.     /* former potentially uses less value segments.  For now I take the easy way.                    */
  669.     
  670.     /* Note we still need it to split larger free space entries when we don't need all        */
  671.     /* the space from an entry.                                                                                                                        */
  672.     
  673.     theValue = (TOCValuePtr)cmGetListHead(&freeSpaceValueHdr->valueList);
  674.     
  675. #if MinFragmentSize
  676.     if ((mustAllFit) || (desiredSize <= MinFragmentSize))
  677.         /* If the block is small, it does not make sense to have different segments. So we    */
  678.         /* treat it as a single segment to avoid fragmentation.                                                            */
  679.         minSegmentSize = desiredSize;
  680.     else {
  681.         /* Even if it is a large block, we still don't want it to fragment too much.                */
  682.         /* This is especially true since the block may be an embedded container. So we try    */
  683.         /* to limit the number of segments                                                                                                    */
  684. #if MaxFragmentCount
  685.         minSegmentSize = desiredSize / MaxFragmentCount;
  686.         if (minSegmentSize < MinFragmentSize)
  687.             minSegmentSize = MinFragmentSize;
  688. #else
  689.         minSegmentSize = MinFragmentSize;
  690. #endif
  691.     }
  692.     
  693.     while (theValue) {                                                                                /* ...scan free list                */
  694.         if (theValue->value.notImm.valueLen >= minSegmentSize)     /* ...for one big enough        */
  695.             break;
  696.         theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  697.     }
  698.     if (theValue == NULL) return (*actualSize = 0);                    /* if none found, too bad        */
  699. #else
  700.     if (mustAllFit) {                                                                                    /* if must find single seg    */
  701.         while (theValue) {                                                                            /* ...scan free list                */
  702.             if (theValue->value.notImm.valueLen >= desiredSize)     /* ...for one big enough        */
  703.                 break;
  704.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  705.         }
  706.         if (theValue == NULL) return (*actualSize = 0);                    /* if none found, too bad        */
  707.     }
  708. #endif
  709.     
  710.     offset             = theValue->value.notImm.value;                                /* set to return this offset*/
  711.     *actualSize = theValue->value.notImm.valueLen;                        /* and this size (maybe!)        */
  712.     
  713.     /* If the free list entry is big enough to cover all that is desired, use only the        */
  714.     /* required amount and retain the free list entry.  It must, however, be reduced by        */
  715.     /* the amount used.                                                                                                                                        */
  716.     
  717.     /* UNRESOLVED QUESTION! Since we never allow free list entries to be created when the */
  718.     /* space is less than a TOC entry size, should we keep the split space here when it        */
  719.     /* becomes less than a TOC entry size?  What does it cost?                                                        */
  720.     
  721.     if (desiredSize < *actualSize) {                                                    /* free entry is too big        */
  722.         theValue->value.notImm.value    += desiredSize;                    /* ...cut it down                        */
  723.         theValue->value.notImm.valueLen -= desiredSize;
  724.         container->spaceDeletedValue->value.imm.ulongValue -= desiredSize; /* cut total            */
  725.         freeSpaceValueHdr->size -= desiredSize;                                 /* less free space now            */
  726.         *actualSize = desiredSize;                                                            /* return what was desired    */
  727.         return (offset);
  728.     }
  729.     
  730.     /* Now that we used an entire an free list entry, delete it from the free list and         */
  731.     /* free its memory.  If there are no more free list entries, delete the value header     */
  732.     /* and the "free space" property from TOC ID 1...                                                                            */
  733.     
  734.     deleteFreeListEntry(theValue);
  735.     
  736.     container->spaceDeletedValue->value.imm.ulongValue -= *actualSize; /* cut total                */
  737.     
  738.     return (offset);                                                                                    /* return new space to use    */
  739. }
  740.  
  741. /*--------------------------------------------------------------------------*
  742.  | cmTakeOverFreeList - have one container take the free space from another |
  743.  *--------------------------------------------------------------------------*
  744.  
  745.  When a target container is open by a container, we want to take over the
  746.  free space in the target container so that we can reuse the free space
  747.  in the target container. Since the target container is open read only, we
  748.  cannot update the free space list there. So the update container move the list
  749.  over so that it becomes the free list of the update container.
  750.  
  751.  This should only be done for one level, so the update container takes the
  752.  free list of the target container. Any free list that is in container that
  753.  is further down may be used already and should not be used.
  754. */
  755.  
  756. void cmTakeOverFreeList(ContainerPtr toContainer, ContainerPtr container)
  757. {
  758.     TOCValuePtr         theValue;
  759.     
  760.     TOCValueHdrPtr freeSpaceValueHdr = container->freeSpaceValueHdr;
  761.     if (freeSpaceValueHdr) {
  762.         theValue = (TOCValuePtr)cmGetListHead(&freeSpaceValueHdr->valueList);
  763.         while (theValue) {
  764.             addToPermFreeList(toContainer,
  765.                                                 theValue->value.notImm.value, theValue->value.notImm.valueLen);
  766.             theValue = (TOCValuePtr)cmGetNextListCell(theValue);
  767.         } /* value */
  768.     }
  769. }
  770.  
  771. /*--------------------------------------------------------------------------*
  772.  | cmWriteData - write data to container with possible resuse of free space |
  773.  *--------------------------------------------------------------------------*
  774.  
  775.  This routine is the Container Manager's low-level value data writer.  It takes a value
  776.  header, and size bytes in the buffer and writes it to the container via the output
  777.  handler.  The amount written is returned.  If it is not equal to the passed size, the
  778.  caller should report an error.
  779.  
  780.  The reason this routine is used rather than using the CMfwrite() handler call directly is
  781.  that this routine checks to see if the container has been opened to reuse free space.  If
  782.  it hasn't, we degenerate into a simple CMfwrite() call.  If it has, then we use the free
  783.  list, built by cmAddToFreeList(), to write out the data segments to reuse the free space.
  784.  
  785.  In all cases the data written is recorded as value segments in the value list belonging
  786.  to the specified value header. The new segments are appended on to the END of the segment
  787.  list.  It is up to the caller to guarantee this is where the segments are to go.  The
  788.  continued flags are appropriately set.
  789.  
  790.  Note, as just mentioned, use this routine ONLY for appending data segments to a value.
  791.  Also, use this routine ONLY if continued segments are permitted for the data.  This means
  792.  you cannot call this routine to write global names.  They are always written as single
  793.  segments.
  794.  
  795.  Even with these restrictions, for the main case, i.e., CMWriteValueData(), this is not a
  796.  problem.  CMWriteValueData() is the primary caller.  It knows what it's doing with the
  797.  data (lets hope so).  So it knows when to call us here.
  798.  
  799.  The net effect is to reuse free space whenever we can if the container was opened for
  800.  updating, and to do straight CMfwrite() otherwise.
  801. */
  802.  
  803. CM_ULONG cmWriteData(TOCValueHdrPtr theValueHdr, CM_UCHAR *buffer,
  804.                                          CM_ULONG size)
  805. {
  806.     ContainerPtr  container = theValueHdr->container->updatingContainer;
  807.     TOCValuePtr        prevValue;
  808.     CM_ULONG             chunkSize, offset, amountWritten = 0;
  809.     TOCValueBytes valueBytes;
  810.     Boolean                appendToPrev;
  811.  
  812.     /* Write out all the data (size bytes of it).  To save a little code space we check     */
  813.     /* for updating in the loop.  If we are not updating we will attempt to do a standard    */
  814.     /* CMfwrite() of all the data as a single value segment.  That is also what happens        */
  815.     /* when we run out of free space.  Then we try to write out all the remaining data        */
  816.     /* as a single segment.  The code is the same, so that's why the update check is in     */
  817.     /* the loop.  If we are updating, and we do have free list entries, we use them to        */
  818.     /* see where to write and how much.  This will reuse the free list space.                            */
  819.     
  820.     prevValue = (TOCValuePtr)cmGetListTail(&theValueHdr->valueList); /* last value seg        */
  821.     
  822.     while (size > 0) {                                                                    /* loop till no more data                    */
  823.         if ((container->useFlags & kCMReuseFreeSpace)==0)    /* if not reusing free space...        */
  824.             chunkSize = 0;                                                                    /* ...this will cause std write        */
  825.         else                                                                                            /* if updating,get some free space*/
  826.             offset = cmGetFreeListEntry(container, size, false, &chunkSize);
  827.         
  828.         if (chunkSize == 0) {                                                            /* if no free space to reuse...        */
  829.             offset = CMgetContainerSize(container);
  830.             CMfseek(container, 0, kCMSeekEnd);                            /* position to current eof                */
  831.             if (CMfwrite(container, buffer, sizeof(CM_UCHAR), size) == size) {             /* write    */
  832.                 amountWritten += size;                                                /* count total amount written            */
  833.                 container->physicalEOF = offset + size;                /* update next free container byte*/
  834.                 SetLogicalEOF(container->physicalEOF);                /* logical EOF == physical EOF        */
  835.                 (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, size);
  836.                 cmAppendValue(theValueHdr, &valueBytes, 0);        /* append new value segment                */
  837. #if CMKEEP_CONTINUE_FLAG
  838.                 if (prevValue != NULL) {                                            /* if this is not 1st segment...    */
  839.                     prevValue->flags |= kCMContinued;                        /* ...flag last seg as cont'd            */
  840.                     theValueHdr->valueFlags |= ValueContinued;    /* ...also echo flag in the hdr        */
  841.                 }
  842. #endif
  843.             }
  844.             return (amountWritten);                                                    /* return amount we wrote                    */
  845.         } /* chunkSize == 0 */
  846.         
  847.         /* If we get here then we are going to reuse a free space segment...                                */
  848.         
  849.         CMfseek(container, offset, kCMSeekSet);                        /* overwrite the free space chunk    */
  850.         if (CMfwrite(container, buffer, sizeof(CM_UCHAR), chunkSize) != chunkSize) 
  851.             return (amountWritten);
  852.         
  853.         /* if the free space is right after a previous free space segment, just extend that */
  854.  
  855.         appendToPrev = true;
  856.         if (prevValue != NULL) {                                                    /* if this is not 1st segment...    */
  857.             if (((prevValue->flags & kCMImmediate) == 0) &&
  858.                     ((prevValue->value.notImm.value+prevValue->value.notImm.valueLen) == offset)) {
  859.                 prevValue->value.notImm.valueLen += chunkSize;    /* just add to previous value */
  860.                 theValueHdr->size += chunkSize;                                
  861.                 appendToPrev = false;
  862. #if CMKEEP_CONTINUE_FLAG
  863.             } else {                                                                                /* it is discontinous                            */
  864.                 prevValue->flags |= kCMContinued;                            /* ...flag last seg as cont'd            */
  865.                 theValueHdr->valueFlags |= ValueContinued;        /* ...also echo flag in the hdr        */
  866. #endif
  867.             };
  868.         }
  869.         
  870.         if (appendToPrev) {
  871.             (void)cmSetValueBytes(container, &valueBytes, Value_NotImm, offset, chunkSize);
  872.             prevValue = cmAppendValue(theValueHdr, &valueBytes, 0);    /* append new value segment    */
  873.             if (prevValue == NULL) return (amountWritten);
  874.         }
  875.         
  876.         offset += chunkSize;                                                            /* set logical EOF                                */
  877.         SetLogicalEOF(offset);
  878.         
  879.         buffer                 += chunkSize;                                                /* point to next chunk to write        */
  880.         amountWritten += chunkSize;                                                /* count total amount written            */
  881.         size                     -= chunkSize;                                                /* reduce amount yet to be written*/
  882.     } /* while */                                                                                /* loop through all the data            */
  883.     
  884.     return (amountWritten);                                                            /* return amount we wrote                    */
  885. }
  886.  
  887.  
  888. /*----------------------------------------------------------------*
  889.  | cmDeleteFreeSpace - delete free space beyond a specified point |
  890.  *----------------------------------------------------------------*
  891.  
  892.  This routine is called to remove all free list entries that have their space beyond the
  893.  specified offset, beyondHere.  All entries with starting offsets greater than or equal
  894.  to beyondHere are removed.  If one spans beyondHere it will be reduced appropriately.
  895.  
  896.  These entries are removed so so that we may overwrite from beyondHere with a new TOC.
  897.  Since we are reusing that space for the TOC we obviously can't keep free list entries for
  898.  it.
  899. */
  900.  
  901. void cmDeleteFreeSpace(ContainerPtr container, CM_ULONG beyondHere)
  902. {
  903.     TOCValuePtr        theValue, nextValue;
  904.     CM_ULONG             offset, size, newLen;
  905.     TOCValuePtr        spaceDeletedValue = container->spaceDeletedValue;
  906.  
  907.     if (container->freeSpaceValueHdr == NULL) return;                /* exit if no free list                */
  908.     
  909.     theValue = (TOCValuePtr)cmGetListHead(&container->freeSpaceValueHdr->valueList);
  910.     
  911.     while (theValue) {                                                                            /* ...scan free list                    */
  912.         nextValue = (TOCValuePtr)cmGetNextListCell(theValue);    /* get next now for deletes        */
  913.         offset    = theValue->value.notImm.value;                            /* offset of a free chunk            */
  914.         size    = theValue->value.notImm.valueLen;                        /* size of a free chunk                */
  915.         
  916.         if (offset >= beyondHere) {                                                        /* if chunk entirely beyond        */
  917.             spaceDeletedValue->value.imm.ulongValue -= size;
  918.             deleteFreeListEntry(theValue);                                            /* ...delete it                                */
  919.         }
  920.         else if (offset + size > beyondHere) {                                 /* if partial...                            */
  921.             newLen = beyondHere - offset;                                                /* ...cut size down                        */
  922.             size -= newLen;                                                                            /* actual size deleted                */
  923.             spaceDeletedValue->value.imm.ulongValue -= size;        /* decr space deleted                    */
  924.             #if TOC1_SUPPORT
  925.             if (container->majorVersion == 1) {                                    /* and if format 1 TOC...            */
  926.                 if (newLen <= TOCentrySize)                                             /* ...and too small to keep        */
  927.                     deleteFreeListEntry(theValue);                                    /* ...delete it too                        */
  928.                 else {                                                                                        /* ...if still acceptable            */
  929.                     container->freeSpaceValueHdr->size -= size;
  930.                     theValue->value.notImm.valueLen = newLen;                /* ...set its new smaller size*/
  931.                 }
  932.             } else {                                                                                        /* if >format 1 TOC                        */
  933.                 if (newLen <= MinTOCSize)                                                 /* ...and still too small            */
  934.                     deleteFreeListEntry(theValue);                                    /* ...delete it too                        */
  935.                 else {                                                                                        /* ...if still acceptable            */
  936.                     container->freeSpaceValueHdr->size -= size;
  937.                     theValue->value.notImm.valueLen = newLen;                /* ...set its new smaller size*/
  938.                 }
  939.             }
  940.             #else
  941.             if (newLen <= MinTOCSize)                                                     /* ...but if too small to keep*/
  942.                 deleteFreeListEntry(theValue);                                        /* ...delete it too                        */
  943.             else {                                                                                            /* ...if still acceptable            */
  944.                 container->freeSpaceValueHdr->size -= size;
  945.                 theValue->value.notImm.valueLen = newLen;                    /* ...set its new smaller size*/
  946.             }
  947.             #endif
  948.         }
  949.         
  950.         theValue = nextValue;                                                                    /* loop till no more entries    */
  951.     } /* while */
  952. }
  953.  
  954.                                                           CM_END_CFUNCTIONS
  955.