home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / TIERRA40.ZIP / TIERRA / MEMTREE.C < prev    next >
C/C++ Source or Header  |  1992-09-09  |  56KB  |  1,606 lines

  1. /* memtree.c   9-9-92 memory allocation routines for the Tierra Simulator */
  2. /* Tierra Simulator V4.0: Copyright (c) 1992 C. J. Stephenson & Virtual Life*/
  3.  
  4. #ifndef lint
  5. static char     memtreec_sccsid[] = "@(#)memtree.c    1.2     7/21/92";
  6. #endif
  7.  
  8. #include <sys/types.h>
  9. #include "license.h"
  10. #include "tierra.h"
  11. #include "extern.h"
  12.  
  13. #ifdef ALCOMM
  14. #include "tmonitor.h"
  15. #include "trequest.h"
  16. #include <mlayer.h>
  17. #endif
  18.  
  19.  
  20. #ifdef MEM_CHK
  21. #include <memcheck.h>
  22. #endif
  23.  
  24. /* This is the file memtree.c 
  25.  
  26.    This file contains the code for:
  27.  
  28.        MemInit()    --  Initialize soup allocator
  29.        IsFree()     --  Enquire status of soup addr
  30.        MemAlloc()   --  Allocate an area of the soup
  31.        MemDealloc() --  Deallocate an area of soup   */
  32.  
  33.  
  34. /*  _________________________________________________
  35.    |                                                 | 
  36.    |        NEW  SOUP  ALLOCATOR  FOR  TIERRA        |
  37.    |_________________________________________________|
  38.  
  39.  
  40.    Program and program notes by C.J.Stephenson, Santa
  41.    Fe, July 1992.
  42.  
  43.                         *    *    *
  44.  
  45.    This soup allocator allows the program requesting 
  46.    space to supply a preferred address for allocation,
  47.    and also a tolerance. This permits a dividing creat-
  48.    ure to place its offspring near itself (which may be 
  49.    desirable for self-contained creatures), or scatter-
  50.    ed far and wide (which may make sense for seeds and
  51.    parasites).
  52.  
  53.       This allocator is also expected to perform better 
  54.    than the previous version.
  55.  
  56.                         *    *    *
  57.  
  58.    The `soup' begins at zero and has a size of SoupSize
  59.    (global var). The soup size is constant during a run.
  60.  
  61.       The soup allocator possesses two main entry points,
  62.    MemAlloc() and MemDealloc().  
  63.  
  64.       When MemAlloc() is called, it doles out some soup
  65.    by finding an unoccupied area of adequate size.  When 
  66.    MemDealloc() is called, it records that the specified 
  67.    area of soup is no longer occupied (and is therefore
  68.    available for reallocation).
  69.  
  70.       This soup allocator maintains a record of the unoc-
  71.    cupied areas in a `cartesian' tree.  A cartesian tree
  72.    is a binary search tree in which one property is ord-
  73.    ered horizontally (in this case the soup addr), and
  74.    some other property is ordered vertically (in this
  75.    case the size of the unoccupied area), such that no
  76.    son is `heavier' than its father. Therefore the root
  77.    of the tree always describes the biggest unoccupied
  78.    area.  Here is a picture of such a tree:
  79.  
  80.                        _________
  81.                       |         |
  82.                       | 300,500 |
  83.                       |_________|
  84.                       /    \
  85.                      /      \
  86.                     /        \
  87.                    /          \
  88.                   /            \
  89.                  /              \
  90.               __/______          \__________
  91.              |         |         |          |
  92.              | 100,100 |         | 1000,200 |
  93.              |_________|         |__________|
  94.              /                   /    \
  95.             /                   /      \
  96.            /                   /        \
  97.         __/___            ____/___       \__________
  98.        |      |          |        |      |          |
  99.        | 0,50 |          | 900,50 |      | 1400,200 |
  100.        |______|          |________|      |__________|
  101.                               \
  102.                                \
  103.                                 \
  104.                                  \________
  105.                                  |        |
  106.                                  | 960,10 |
  107.                                  |________|
  108.  
  109.  
  110.    A cartesian tree can be maintained by the technique 
  111.    called `root insertion'.  It possesses several nice
  112.    properties:
  113.  
  114.    (1)  The method offers better performance than list
  115.         methods (and does not impose course granularity
  116.         or possess the other restrictions of the `buddy'
  117.         methods).  When allocating, an area of adequate
  118.         size can be found by descending from the root 
  119.         and examining only the areas of adequate size,
  120.         without needing to inspect the numerous scraps
  121.         of inadequate size (which are relegated to the 
  122.         lower branches and the leaves).  And when deal-
  123.         locating, the area being returned can be placed 
  124.         correctly in the tree, and combined with its 
  125.         neighbour(s) if any, by performing a binary 
  126.         search, which usually requires far less work
  127.         than searching a list.
  128.  
  129.         (Note however that the tree is not balanced, and
  130.         cannot in general be balanced, so the worst-case
  131.         performance is still bad, though rarely encount-
  132.         ered in practice.  Techniques exist for alleviat-
  133.         ing the worst case, but they are not used in this
  134.         program.)
  135.  
  136.    (2)  The method allows several interesting allocation
  137.         policies to be offered cheaply:
  138.  
  139.         (a) `Friendly Fit'.  The caller of MemAlloc() 
  140.         can supply a preferred addr for allocation, and
  141.         an acceptable tolerance. The allocator can find 
  142.         the nearest unoccupied area (of adequate size) 
  143.         by performing a binary search for the prefer-
  144.         red address. If the nearest area is near enough
  145.         (i.e. within the given tolerance), the allocator
  146.         supplies some soup from this area; otherwise it 
  147.         reports that there is no suitably placed soup
  148.         available.
  149.  
  150.         First fit (or `Leftmost Fit' from the tree) can 
  151.         be realized simply by supplying a preferred addr
  152.         of zero and the most generous tolerance.
  153.  
  154.         (b) `Better Fit'. Alternatively the caller can
  155.         indicate the absence of a preference, in which
  156.         case the allocator employs a policy that tends
  157.         to minimize fragmentation of the soup.
  158.  
  159.       Control over placement is the issue that motivated
  160.    this new allocator.  In Tierra, proximity in the soup
  161.    has semantic significance, since it affects the inter-
  162.    actions between creatures.  It therefore seems desir-
  163.    able to give Mama some control over the position of
  164.    her offspring.
  165.  
  166.       For a description of root insertion, see:
  167.  
  168.       [1]  C.J.Stephenson, A method for constructing
  169.            binary search trees by making insertions
  170.            at the root, Int J Comp and Inf Sci, Vol
  171.            9 (1980).
  172.  
  173.       For the original description of cartesian trees,
  174.       see:
  175.  
  176.       [2]  J.Vuillemin, A unifying look at data
  177.            structures, CACM Vol 23 (1980).
  178.  
  179.       For the application of cartesian trees to memory
  180.       allocation, see:
  181.  
  182.       [3]  C.J.Stephenson, Fast Fits: New methods for
  183.            dynamic storage allocation, Operating Sys
  184.            Review (extended abstract), Vol 17 (1983).
  185.  
  186.       Note that ref [3] describes a method of memory
  187.       allocation in which the tree is constructed in
  188.       the unoccupied pieces themselves, i.e. in the
  189.       same space that is being managed. In the pre-
  190.       sent application, the tree is constructed in
  191.       separate memory (see below), and not within
  192.       the soup.
  193.  
  194.  
  195.    THE NODE ARRAY
  196.  
  197.       The nodes for the tree are `allocated' from an
  198.    array of nodes, and the tree `ptrs' are represented
  199.    by the indices of the nodes in the array. This per-
  200.    mits the entire tree to be preserved in a file (or
  201.    restored from file) simply be writing (or reading)
  202.    the array.  (A similar scheme was employed in the
  203.    previous soup allocator, and carried over to this
  204.    one.) 
  205.  
  206.       The structure that describes an indiividual node
  207.    is named MemFr (for `Memory Frame').  It contains 4
  208.    signed fullword integers, laid out thus:
  209.  
  210.                ___________________________ 
  211.               |             |             |
  212.        MemFr  | l=left ptr  | r=right ptr | 
  213.               |_____________|_____________|
  214.               |             |             |
  215.               | p=soup addr |   s = size  |
  216.               |_____________|_____________|
  217.  
  218.  
  219.    where:  l,r  =  `ptrs' to (i.e. indices of) the
  220.                    left and right sons, or zero if
  221.                    corresponding son does not exist
  222.  
  223.            p,s  =  addr and size of unoccupied area
  224.                    of the soup (0 <= addr < SoupSize,
  225.                    0 < s <= SoupSize)
  226.  
  227.    The addr of the array is maintained in FreeMem (glob-
  228.    al var) -- and the number of nodes in the array is in 
  229.    MaxFreeBlocks (another global var).  An initial modest
  230.    array is allocated when the program begins, and is en-
  231.    larged as necessary during the course of the run (with
  232.    FreeMem and MaxFreeBocks being adjusted accordingly).
  233.    Here is a picture of the array:
  234.  
  235.                      ___________
  236.                     |           |            
  237.         FreeMem[0]  |   MemFr   |  Node 0 is special (see below)
  238.                     |___________|
  239.                     |           |            
  240.         FreeMem[1]  |   MemFr   |  Some nodes are used for the tree
  241.                     |___________|  
  242.                     |           |            
  243.         FreeMem[2]  |   MemFr   | 
  244.                     |___________|
  245.                     |           |            
  246.         FreeMem[3]  |   MemFr   |  Some nodes are typically unused
  247.                     |___________|
  248.                     :           :
  249.                     :           :
  250.                     :           :
  251.                     :___________:
  252.                     |           |            
  253.     FreeMem[MFB-1]  |   MemFr   |  (MFB is short for MaxFreeBlocks)
  254.                     |___________|
  255.  
  256.  
  257.    The zeroth node in the array acts as an `anchor' for
  258.    the tree -- and also for the unused nodes. It is laid
  259.    out thus:
  260.  
  261.                   ___________________________ 
  262.                  |             |             |
  263.      FreeMem[0]  | l=`liberty' |  r = `root' |  The anchor
  264.                  |_____________|_____________|
  265.                  |             |             |
  266.                  |    p = 0    |    s = 0    |
  267.                  |_____________|_____________|
  268.  
  269.  
  270.    where:  l = `liberty ptr' = anchor for unused nodes
  271.                                (see below), or zero if
  272.                                no unused nodes exist
  273.            
  274.            r =  `root ptr'   = index of the tree root
  275.                                (1 <= r < MFB), or zero
  276.                                if the tree is empty
  277.  
  278.  
  279.    UNUSED NODES
  280.  
  281.       There are two flavours of unused nodes, untouched
  282.    and recycled.
  283.  
  284.       Untouched nodes are nodes that have never been used
  285.    in the tree.  Their contents are undefined.  They form
  286.    a contiguous group (which may be empty) at the end of
  287.    the array.  The position of the first is identified by 
  288.    a negative `liberty ptr' having a value of u-MFB, where
  289.    u = index of first untouched node; note that this nat-
  290.    urally acquires a value of zero when the last untouch-
  291.    ed node is pressed into service.
  292.  
  293.       Recycled nodes are nodes that have been used in the
  294.    tree, but are not currently used. They may be sprinkled
  295.    among the nodes that are in use.  They are chained to-   
  296.    gether, via their `l' ptrs, into a free list. 
  297.  
  298.       The liberty ptr in the anchor identifies the first
  299.    recycled node if any (positive ptr), or the first un-
  300.    touched node otherwise (negative ptr), or it is zero 
  301.    if the array is `full'  If recycled nodes exist, the 
  302.    last one identifies the first untouched node, or con-
  303.    tains a zero ptr if there are no untouched nodes. 
  304.  
  305.       Fortunately this scheme is easier to implement
  306.    than to describe, and has the nice property that the
  307.    tail of the array is not touched until it is needed
  308.    (or is never touched if never needed), which is good
  309.    for the working set.  [Actually, at the time of writ-
  310.    ing (1992/07), the array is allocated and reallocated  
  311.    with calloc(), which clears the entire thing to zero  
  312.    -- so in truth there is little benefit in postponing 
  313.    contact with the tail.  This soup allocator does not 
  314.    however require the array to be cleared, and malloc()
  315.    could be used instead of calloc(), which would result 
  316.    in slightly better performance.]
  317.  
  318.  
  319.    NULL POINTERS IN THE TREE
  320.  
  321.       In the tree, a left or right ptr of zero indic-
  322.    ates the absence of a left or right son. When examin-
  323.    ing or manipulating the tree, it is usually necessary
  324.    to check for such `null ptrs' explicitly, in order to 
  325.    avoid unwittingly falling out of the tree.  In some 
  326.    contexts, however, it is possible to omit the check, 
  327.    and cunningly plough on regardless (without risking
  328.    a broken arm).
  329.  
  330.       Consider for example a search for the deepest node
  331.    of adequate size on some particular path through the
  332.    tree.  (For the present purpose, the actual path is
  333.    immaterial.)  If we treat a null left or right ptr as
  334.    a valid tree ptr, we will find ourselves examining node
  335.    0 (the anchor), where the `size' field is permanently
  336.    set to zero. So we will conclude (correctly) that the
  337.    node possessing the null ptr does not possess a son of
  338.    adequate size, without needing to distinguish between
  339.    the case of a small son and the case of a missing one.
  340.  
  341.                        *    *    *
  342.  
  343.    My thanks to Tom Ray, for creating Tierra; to Tom and
  344.    his colleague Dan Pirone for encouraging this little
  345.    project (and for answering numerous questions); and
  346.    to the Santa Fe Institute and IBM for providing the
  347.    facilities and the time for the work.
  348.  
  349.    CJS, Santa Fe, July 1992.                         */
  350.  
  351.  
  352.  
  353.  
  354. /* Declare private internal functions */
  355.  
  356. static void delete  P_((I32s Hp pa, Pmf victim));
  357. static void demote  P_((I32s Hp pa, Pmf victim));
  358. static void promote P_((I32s Hp pa, Pmf riser));
  359. static I32s memnode P_((void));
  360.  
  361.  
  362. /*  _________________________________________________
  363.    |                                                 | 
  364.    |  Function MemInit -- Initialize soup allocator  |
  365.    |_________________________________________________|
  366.  
  367.    Call is:  MemInit ()
  368.  
  369.    On entry: FreeMem  = addr of initial memory node array 
  370.              MaxFreeBlocks = the size of this array (>=2)
  371.              SoupSize = size of soup in instr slots (>0)
  372.  
  373.    Effect is to initialize memory node 0 (anchor) and 1
  374.    (initial root), thus:
  375.  
  376.                _________________________ 
  377.               |            |            |
  378.    FreeMem[0] | l=-(MFB-2) |     r=1    | Anchor for tree
  379.               |____________|____________|
  380.               |            |            |
  381.               |    p=0     |     s=0    |
  382.               |____________|____________|
  383.               |            |            |
  384.    FreeMem[1] |    l=0     |     r=0    | The initial root
  385.               |____________|____________|
  386.               |            |            |
  387.               |    p=0     | s=SoupSize |
  388.               |____________|____________|
  389.               |            |            |
  390.               :            :            :
  391.  
  392.    where `MFB' is short for MaxFreeBlocks (array size).
  393.  
  394.    Notes:
  395.  
  396.    1.   This version of MemInit is for the new 
  397.         soup allocator (CJS, Santa Fe, July 1992).
  398.  
  399.    2.   This routine does not bother to initialize
  400.         fields that require an initial value of zero,
  401.         since the memory for the node array is obtain-
  402.         ed with calloc(), which clears the whole thing.
  403.         This soup allocator does not however require 
  404.         the entire array to be initialized, and the
  405.         array could equally well be obtained with
  406.         malloc() instead of calloc() provided this
  407.         routine was changed to clear the following
  408.         fields:
  409.         
  410.                  FreeMem[0].p, FreeMem[0].s,
  411.           FreeMem[1].l, FreeMem[1].r, FreeMem[1].p.  */
  412.  
  413. void MemInit () {
  414.  
  415. #ifdef ERROR
  416.  
  417.    if (MaxFreeBlocks < 2)
  418.      FEError (-601,EXIT,WRITE,
  419.               "Tierra MemInit() error: memory node array too small");
  420.  
  421.    if (SoupSize <= 0)
  422.      FEError (-601,EXIT,WRITE,
  423.               "Tierra MemInit() error: invalid soup size");
  424.  
  425. #endif
  426.  
  427.    FreeMem[0].l = -(MaxFreeBlocks-2);  /* -ve index of untouched */
  428.    FreeMem[0].r = 1;                   /* Initial root for tree  */
  429.    FreeMem[1].s = SoupSize;            /* Initial root holds all */
  430.  
  431.    return;
  432.  
  433. }
  434.  
  435.  
  436. /*  ________________________________________________________
  437.    |                                                        | 
  438.    |  Function IsFree -- Enquire whether soup addr is free  |
  439.    |________________________________________________________|
  440.  
  441.    Call is:  y = IsFree (x)
  442.  
  443.    where:    x = signed fullword integer containing soup addr
  444.              y = small integer (8 bits) which is set to 0 (if 
  445.                  given soup addr is occupied) or 1 (if free)     
  446.  
  447.    Also:     FreeMem  = addr of the memory node array
  448.              SoupSize = size of soup in instr slots
  449.  
  450.    This version of IsFree() was written for the new soup 
  451.    allocator; CJS, Santa Fe, July 1992.                   */ 
  452.  
  453.  
  454. I8s IsFree (x)   I32s x;  {
  455.  
  456. Pmf   a,         /* Addr of memory node array */
  457.       c;         /* Addr of current memory node */
  458.  
  459. #ifdef ERROR
  460.    if (x<0 || x>=SoupSize)
  461.      FEError (-601,EXIT,WRITE,
  462.               "Tierra IsFree() error: addr %ld not in soup",x);
  463. #endif
  464.  
  465.    a = FreeMem;              /* Addr of node array */
  466.    c = a + a->r;             /* Addr of the tree root */
  467.  
  468.    while (c != a)            /* Until I fall from tree */
  469.      if (c->p > x)           /* If this node exceeds x */
  470.        c = a + c->l;         /* Step down to left son */
  471.      else
  472.        if ((c->p + c->s) > x)   /* If node contains x */
  473.          return (1);            /* Yield 1 (addr free) */
  474.        else
  475.          c = a + c->r;       /* Step down to right son */
  476.  
  477.    return (0);               /* Yield 0 (addr occupied) */
  478.  
  479. }
  480.  
  481. /*  _____________________________________________________
  482.    |                                                     | 
  483.    |  Function MemAlloc -- Allocate an area of the soup  | 
  484.    |_____________________________________________________|
  485.  
  486.    Call is:  p = MemAlloc (size,pref,tol) 
  487.  
  488.    where:    size, pref, tol and p are all signed fullword 
  489.              integers
  490.  
  491.    and:      size = required amount of soup in instr slots 
  492.                     (0 < size <= SoupSize)
  493.  
  494.              pref = preferred soup addr (0 <= pref < SoupSize),
  495.                     or pref < 0 if the caller has no preference
  496.  
  497.              tol  = acceptable tolerance (0 <= tol < SoupSize),
  498.                     or irrel if pref < 0
  499.  
  500.              p    = allocated soup addr (normally),
  501.                     or -1 if there is no unoccupied  area
  502.                           of adequate size,
  503.                     or -2 if an area of adequate size exists,
  504.                           but not within the given tolerance
  505.  
  506.    Also:     FreeMem        = addr of memory node array  :  May be changed
  507.              MaxFreeBlocks  = size of memory node array  :  as side effect 
  508.              FreeMemCurrent = sum of unoccupied space    :  of call
  509.  
  510.              SoupSize = size of soup in instr slots      :  Not changed
  511.  
  512.  
  513.    ----------------  Summary of MemAlloc()  ----------------
  514.  
  515.    1.   If the largest unoccupied area of soup has inadequate
  516.         size, supply a dummy soup addr of -1, without allocat-
  517.         ing any soup at all.  Otherwise proceed as follows.
  518.  
  519.    2.   If pref < 0, ignore given tolerance, and allocate 
  520.         required soup using `Better Fit', which is sure to
  521.         succeed (see below).  Otherwise proceed as follows.
  522.    
  523.    3.   Identify the winning unoccupied area.
  524.  
  525.         If the preferred addr lies within an unoccupied area 
  526.         of adequate size, the winning area is the one contain-
  527.         ing the preferred addr; otherwise it is the area of
  528.         adequate size that possesses the minimum separation
  529.         from the preferred addr. (The `separation' is measur-
  530.         ed from `pref' to the beginning of the area, if pref
  531.         precedes the area; or from the end of the area to
  532.         `pref', if the area precedes pref.) If two adequate 
  533.         areas possess equal separation, the one on the left
  534.         prevails.
  535.  
  536.    4a.  If the winning unoccupied area does not contain the
  537.         preferred addr, identify the nearer edge of the area,
  538.         and measure the absolute difference between this edge
  539.         and the preferred addr.  If this distance exceeds the
  540.         given tolerance, supply a dummy soup addr of -2, with-
  541.         out allocating any soup at all.  Otherwise allocate
  542.         the required soup from the nearer edge.
  543.  
  544.    4b.  If the winning area contains the preferred addr,
  545.         identify the nearer edge of the area (or define the
  546.         left edge as the nearer if `pref' lies at the centre
  547.         of the winning area), and measure the absolute dis-
  548.         tance between this edge and the preferred addr.  If
  549.         the distance does not exceed size+tol, allocate the
  550.         required soup from this nearer edge; otherwise al-
  551.         locate it at the preferred addr exactly (i.e. beg-
  552.         inning at `pref' and extending to the right).
  553.  
  554.    Notes:
  555.  
  556.    1.   `Better Fit' is an allocation policy which selects
  557.         a winning node by descending from the root, always 
  558.         choosing the better-fitting son, until it encount-
  559.         ers a node which possess no sons of adequate size 
  560.         (or no sons at all). The winning area of the soup 
  561.         is the one described by the winning node. 
  562.  
  563.         In this implementation of Better Fit:
  564.  
  565.            if both sons are adequate, and have the  
  566.            same size, the one on the left prevails;
  567.  
  568.            if the size of the winning area exceeds 
  569.            the required size, allocation is made 
  570.            from its left edge.
  571.  
  572.    2.   `Leftmost Fit' from the tree (which is equivalent 
  573.         to `First Fit' from the corresponding list) can be
  574.         realized by supplying a preferred address of zero
  575.         and the most generous allowable tolerance.        */
  576.  
  577.  
  578. I32s MemAlloc (size,pref,tol)  I32s size,pref,tol;  {
  579.  
  580. Pmf   a,         /* Addr of memory node array */
  581.       c,         /* Addr of current memory node */
  582.       e;         /* Addr of new memory node */
  583.  
  584. I32s  Hp b,      /* Addr of parental `ptr' to c */
  585.       Hp pa,     /* Addr of `ptr' to best node */
  586.  
  587.       best,      /* Distance to nearest node */
  588.  
  589.       oc,        /* Offset of node `c' in array */
  590.       oe,        /* Offset of node `e' in array */
  591.       opa,       /* Offset of `pa' in node array */
  592.  
  593.       x,         /* Beg soup addr of selected node */
  594.       y,         /* The soup addr that is allocated */
  595.       z;         /* End soup addr of selected node */
  596.  
  597. #ifdef ERROR
  598.  
  599.    if (size<=0 || size>SoupSize)
  600.      FEError (-601,EXIT,WRITE,
  601.               "Tierra MemAlloc error: invalid size %ld",size);
  602.    if (pref >= SoupSize)
  603.      FEError (-601,EXIT,WRITE,
  604.               "Tierra MemAlloc error: invalid preference %ld",pref);
  605.    if (pref>=0 && (tol<0 || tol>=SoupSize))
  606.      FEError (-601,EXIT,WRITE,
  607.               "Tierra MemAlloc error: invalid tolerance %ld",tol);
  608.  
  609. #endif
  610.  
  611.    a = FreeMem;              /* Addr of node array */
  612.    c = a + a->r;             /* Addr of the tree root */
  613.  
  614.    if (c->s < size)          /* If biggest area is too small */
  615.      return (-1);            /* Yield -1 (cannot supply soup) */ 
  616.  
  617.    if (pref < 0)             /* If caller has no preference */
  618.      goto better;            /* Handle below (by better fit) */
  619.      
  620.    /* Arrive here if soup is to be allocated using  
  621.       given preference and tolerance. At this point:
  622.  
  623.         a = addr of memory array
  624.         c = addr of the root node
  625.  
  626.         size,pref,tol are as supplied by caller
  627.  
  628.       Search for the preferred addr until I reach
  629.       a node whose son is too short.              */
  630.  
  631.    b = &a->r;                /* Addr of my parental `ptr' */
  632.    best = LONG_MAX;          /* Best so far is pretty bad */
  633.  
  634.    do {                      /* Execute this at least once */
  635.      
  636.      if (c->p > pref) {          /* If this soup addr > pref */
  637.        if (c->p-pref <= best) {  /* And it is nearest so far */
  638.          pa = b;                 /* Remember the parental ptr */
  639.          best = c->p - pref;     /* Remember closest contact */
  640.        }
  641.        b = &c->l;                /* Step down to the left */
  642.      } else {
  643.        if (pref-(c->p+c->s-1) <= best) {
  644.          pa = b;
  645.          best = pref - (c->p + c->s - 1); /* May set best < 0 */
  646.        }
  647.        b = &c->r;
  648.      }
  649.  
  650.      c = a + *b;             /* Addr of the descendant node */
  651.  
  652.    } while (best > 0 && c->s >= size);
  653.  
  654.  
  655.    /* Arrive here after finding the nearest unoccupied area
  656.       of adequate size. See if it is near enough to his pre- 
  657.       ference.  At this point:
  658.  
  659.         a = addr of the memory array
  660.         pa = addr of `ptr' to best node
  661.         best = distance separating best area from
  662.                preferred addr, or <= 0 if preferred
  663.                addr lies within an unoccupied area
  664.                of adequate size
  665.  
  666.         size,pref,tol are as supplied by caller      */
  667.       
  668.    c = a + *pa;              /* Addr of winning node */
  669.  
  670.    x = c -> p;               /* x = beginning soup addr */
  671.    z = x + c->s;             /* z = ending addr for node */
  672.  
  673.    if (best > 0) {           /* If pref addr is not available */
  674.  
  675.      if (best > tol)         /* If distance exceeds tolerance */
  676.        return (-2);          /* Yield -2 (cannot satisfy him) */ 
  677.  
  678.      if (x > pref)           /* If best is to right of pref */
  679.        goto leftedge;        /* Allocate from left-hand edge */
  680.      else                    /* Or if the best is to the left */
  681.        goto rightedge;       /* Allocate from right-hand edge */
  682.  
  683.    }
  684.  
  685.    /* Arrive here if the preferred addr lies within an
  686.       unoccupied area of the soup. Allocate from the nearer
  687.       edge -- if this will place the area within the speci-
  688.       fied tolerance.  At this point:
  689.  
  690.         a = addr of the memory array
  691.         c = the addr of the best node
  692.         pa = addr of `ptr' to best node
  693.  
  694.         x = beg soup addr of best node
  695.         z = end soup addr of best node
  696.  
  697.         size,pref,tol are as supplied by caller      */
  698.  
  699.    if (pref-x <= z-(pref+size-1))  /* If left edge is nearer */
  700.  
  701.      if (pref-x <= tol)          /* And if it is near enough */
  702.        goto leftedge;            /* Allocate from left edge */
  703.      else
  704.        ;
  705.  
  706.    else                          /* If right edge nearer ... */
  707.  
  708.      if (z-(pref+size-1) <= tol)
  709.        goto rightedge;
  710.      else
  711.        ;
  712.  
  713.    /* Arrive here if both edges of the area containing 
  714.       the preferred addr are too far from the preferred 
  715.       addr.  Split the area into three, and allocate at
  716.       the preferred addr exactly.  At this point:  
  717.  
  718.         a = addr of the memory array
  719.         c = the addr of the best node
  720.         pa = addr of `ptr' to best node
  721.  
  722.         x = beg soup addr of best node
  723.         z = end soup addr of best node
  724.  
  725.         size,pref,tol are as supplied by caller      
  726.  
  727.       See note in `memnode' on relocation of ptrs.  */
  728.  
  729.    oc = c - a;                   /* Convert ptrs to offsets */
  730.    opa = pa - (I32s Hp)a;
  731.  
  732.    oe = memnode();               /* Acquire new memory node */
  733.    
  734.    if (oe == 0)                  /* If insufficient memory */
  735.      goto bust;                  /* Handle situation below */
  736.  
  737.    a = FreeMem;                  /* Addr of (new) node array */
  738.  
  739.    c = a + oc;                   /* Reset my  node ptrs */
  740.    pa = (I32s Hp)a + opa;       
  741.  
  742.    e = a + oe;                   /* Addr of new memory node */
  743.  
  744.    e->p = pref + size;           /* Addr of right-hand scrap */
  745.    e->s = z - e->p;              /* Size of right-hand scrap */
  746.  
  747.    c->s = pref - x;              /* Size of left-hand scrap */
  748.  
  749.    /* Demote the larger scrap and then insert the
  750.       smaller scrap at the correct place below it.
  751.  
  752.         a = addr of the memory array
  753.         c = addr of node for LH scrap
  754.         e = addr of node for RH scrap           
  755.  
  756.         pa = addr of `ptr' to LH scrap
  757.  
  758.         size,pref are as supplied by caller    */
  759.  
  760.    if (c->s >= e->s) {               /* If LHS is bigger */
  761.  
  762.      demote (pa,c);                  /* Demote LH scrap */
  763.  
  764.      pa = &c->r;                     /* Find home for RHS */     
  765.      while ((a + * pa)->s > e->s)
  766.        pa = &(a + * pa)->l;
  767.  
  768.      e->l = 0;                       /* Insert RH scrap */
  769.      e->r = *pa;     
  770.      *pa = e - a;
  771.  
  772.    } else {                          /* If RHS bigger ... */
  773.  
  774.      e->l = c->l;                    /* Ptrs for demote */
  775.      e->r = c->r;     
  776.  
  777.      demote (pa,e);                  /* Demote RH scrap */
  778.  
  779.      pa = &e->l;                     /* Find home for LHS */     
  780.      while ((a + * pa)->s > c->s)
  781.        pa = &(a + * pa)->r;
  782.  
  783.      c->l = *pa;                     /* Insert LH scrap */
  784.      c->r = 0;
  785.      *pa = c - a;
  786.  
  787.    }
  788.  
  789.    y = pref;                     /* Allocate at this addr */
  790.    goto foot;                    /* And handle rest below */
  791.  
  792.  
  793.    /* Come here if no preference is given. Allocate 
  794.       some soup using Better Fit.  (This is sure to 
  795.       succeed, since I already checked the size of 
  796.       the root.)  At this point:
  797.  
  798.         a = addr of the memory array
  799.         c = the addr of the root node 
  800.  
  801.         size is as supplied by caller            */
  802.  
  803. better:
  804.    pa = &a->r;                   /* Addr of `ptr' to the root */
  805.  
  806.    while ((a+c->l)->s >= size || (a+c->r)->s >= size) {  
  807.                                  /* While either son is big enough */
  808.  
  809.      if ((a+c->l)->s > (a+c->r)->s)  /* If left son is the larger */
  810.  
  811.        if ((a+c->r)->s >= size)      /* If right son is adequate */
  812.          pa = &c->r;                 /* Step down to the right */
  813.        else                          /* Otherwise ... */
  814.          pa = &c->l;                 /* Step down to the left */
  815.  
  816.      else                            /* If right son is larger ... */
  817.  
  818.        if ((a+c->l)->s >= size)
  819.          pa = &c->l;      
  820.        else              
  821.          pa = &c->r;
  822.  
  823.      c = a + *pa;                /* Step down (to left or right) */
  824.    
  825.    }
  826.  
  827.    /* Allocate from left edge of area. At this point:
  828.  
  829.         a = addr of the memory array
  830.         c = addr of the winning node
  831.         pa = addr of `ptr' to node
  832.  
  833.         size is as supplied by caller      */
  834.  
  835. leftedge:
  836.    y = c->p;                 /* Allocate from left edge */
  837.    c->p += size;             /* Adjust addr of remainder */
  838.    goto eitheredge;
  839.  
  840.    /* Allocate from right edge of area. On arrival:
  841.  
  842.         a = addr of the memory array
  843.         c = addr of the winning node
  844.         pa = addr of `ptr' to winner
  845.         z = end addr of winning node
  846.  
  847.         size is as supplied by caller      */
  848.  
  849. rightedge:
  850.    y = z - size;             /* Beginning addr for alloc */
  851.  
  852.    /* Adjust remaining size of area, and delete 
  853.       or demote the winning node. At this point:
  854.  
  855.         a = addr of the memory array
  856.         c = addr of the winning node
  857.         pa = addr of `ptr' to node
  858.         y = soup addr to allocate
  859.  
  860.         size is as supplied by caller      */
  861.  
  862. eitheredge:
  863.    c->s -= size;             /* Size of remaining scrap */
  864.  
  865.    if (c->s == 0) {          /* If nothing left at all */
  866.      delete (pa,c);          /* Delete the winning node */
  867.      c->l = a->l;            /* Recycle the memory node */
  868.      a->l = c-a;
  869.    }
  870.    else                      /* Otherwise ... */        
  871.      demote (pa,c);          /* Demote the winning node */
  872.  
  873.  
  874.    /* All successful paths join here. Keep `almond'
  875.       informed, and deliver the soup.  On arrival:
  876.  
  877.         y = soup addr to allocate
  878.         size = size to allocate                 */
  879.  
  880. foot:
  881.    FreeMemCurrent -= size;   /* Maintain all the books */
  882.  
  883. #ifdef ALCOMM                /* Keep `almond' informed */
  884.    if (MIsDFEnabled(TrtOrgLifeEvent))
  885.      TRepBirth (y,size);
  886. #endif
  887.  
  888.    return (y);               /* Supply this soup addr */
  889.  
  890.  
  891.    /* Come here if there is insufficient system memory
  892.       available to obtain an enlarged node array. Emit 
  893.       an unfriendly msg and abort execution.        */ 
  894.  
  895. bust:
  896.    FEError (-602,EXIT,WRITE,
  897.             "Tierra MemAlloc error: insuf system memory avail");
  898.  
  899. }
  900.  
  901.  
  902. /*  _____________________________________________________
  903.    |                                                     | 
  904.    |  Function MemDealloc -- Deallocate an area of soup  | 
  905.    |_____________________________________________________|
  906.  
  907.    Call is:  MemDealloc (addr,size) 
  908.  
  909.    where:    addr and size are signed fullword integers
  910.  
  911.    and:      addr = soup addr of piece to be deallocated
  912.              size = the size of the piece in instr slots
  913.  
  914.    Also:     FreeMem        = addr of memory node array  :  May be changed
  915.              MaxFreeBlocks  = size of memory node array  :  as side effect 
  916.              FreeMemCurrent = sum of unoccupied space    :  of call
  917.  
  918.              SoupSize = size of soup in instr slots      :  Not changed  */
  919.  
  920.  
  921. void MemDealloc (addr,size)  I32s addr,size;  {
  922.  
  923. Pmf   a,         /* Addr of memory node array */
  924.       c,         /* Addr of current memory node */
  925.       e;         /* Addr of the second neighbour */
  926.  
  927. I32s  Hp d,      /* Addr of parental `ptr' to e */
  928.       Hp pa,     /* Addr of ptr to inserted node */
  929.       pas,       /* Size of node containing ptr */
  930.  
  931.       Hp lh,     /* Left hook for root insertion */
  932.       Hp rh,     /* Right hook for root insertion */
  933.  
  934.       oc,        /* Offset of node `c' in array */
  935.       opa,       /* Offset of `pa' in node array */
  936.  
  937.       x,         /* Beg addr of (combined) piece */
  938.       z;         /* End addr of (combined) piece */
  939.  
  940. MemFr scratch;   /* Scratch node */
  941.  
  942.  
  943. #ifdef ERROR
  944.  
  945.    if (addr<0 || addr>=SoupSize)
  946.      FEError (-601,EXIT,WRITE,
  947.               "Tierra MemDealloc error: invalid soup addr %ld",addr);
  948.  
  949.    if (size<=0 || size>SoupSize)
  950.      FEError (-601,EXIT,WRITE,
  951.               "Tierra MemDealloc error: invalid size %ld",size);
  952.  
  953.    if (addr+size > SoupSize)
  954.      FEError (-601,EXIT,WRITE,
  955.               "Tierra MemDealloc error: invalid soup args");
  956.  
  957. #endif
  958.  
  959.    a = FreeMem;              /* Addr of node array */
  960.  
  961.    x = addr;                 /* Given beginning addr */
  962.    z = x + size;             /* Ending addr of area */
  963.  
  964.    pa = &a->r;               /* Addr of `ptr' to root */
  965.    pas = LONG_MAX;           /* Dummy indefinite size */
  966.  
  967.    c = a + *pa;              /* The addr of the root */
  968.  
  969.    /* Perform a passive binary search for the given piece 
  970.       until I encounter a neighbour, or a node whose son 
  971.       is no longer than the given piece.               */
  972.  
  973.    while (c->s > size) {     /* While among big nodes */
  974.     
  975.     if (c->p > z)            /* If node is to right */
  976.       pa = &c->l;            /* Descend to the left */  
  977.     else
  978.       if (c->p + c->s >= x)  /* If neighbour or overlap */
  979.         goto highneigh;      /* Handle situation below */
  980.       else
  981.         pa = &c->r;          /* Descend to the right */
  982.  
  983.     pas = c->s;              /* Size of my new father */
  984.     c = a + *pa;             /* Descend to this node */
  985.  
  986.   }
  987.  
  988.  
  989.   /* Arrive here if I reach a point where I can insert
  990.      a node describing the piece being deallocated before
  991.      encountering a neighbour (or discovering invalid over-
  992.      lap).  Start inserting a new node here (and continue 
  993.      to watch for neighbours).  If it turns out there are 
  994.      no neighbours, this will be the correct place for the
  995.      node; and even if there are neighbour(s), it *may* be 
  996.      the correct place. (At worst, I will have to promote 
  997.      the node later.)  At this point:
  998.  
  999.         a = addr of the node array
  1000.         c = addr of the descendant node
  1001.         pa = addr of `ptr' to this node
  1002.         pas = size of the paternal node
  1003.  
  1004.         x = addr of piece being deallocated
  1005.         y = size of piece being deallocated
  1006.         z = endad of piece being deallocated         
  1007.  
  1008.         addr,size are as supplied by caller
  1009.  
  1010.      Use my scratch node for the time being (a new node 
  1011.      will not actually be needed if it turns out there 
  1012.      is a neighbour further down).                    */
  1013.  
  1014.    lh = &scratch.l;          /* Init hooks (scratch node) */
  1015.    rh = &scratch.r;
  1016.  
  1017.    while (c != a)            /* Until I fall from the tree */
  1018.  
  1019.      if (c->p > z) {         /* If this node is on right */
  1020.        *rh = c - a;          /* Attach to the right hook */
  1021.        rh = &c->l;           /* And descend to the left */
  1022.        c = a + c->l;
  1023.      }
  1024.  
  1025.      else if (c->p + c->s >= x)  /* If neighbour or overlap */
  1026.        goto lowneigh;            /* Handle situation below */
  1027.  
  1028.      else {                  /* If node is to the left */
  1029.        *lh = c - a;          /* Attach to the left hook */
  1030.        lh = &c->r;           /* And descend to the right */
  1031.        c = a + c->r;
  1032.      }
  1033.  
  1034.  
  1035.    /* Arrive here if I complete the insertion without 
  1036.       encountering any neighbours (or discovering any 
  1037.       overlap). Clear both the hooks; and replace the
  1038.       scratch node by a proper one.  At this point:
  1039.  
  1040.         a = the addr of the node array
  1041.         pa = addr of `ptr' to scratch node
  1042.  
  1043.         x = addr of piece being deallocated
  1044.         y = size of piece being deallocated
  1045.         z = endad of piece being deallocated         
  1046.  
  1047.         addr,size are as supplied by caller
  1048.  
  1049.       See note in `memnode' on relocation of ptrs.  */
  1050.  
  1051.    *lh = *rh = 0;            /* Clear both the hooks */
  1052.  
  1053.    opa = pa - (I32s Hp)a;    /* Convert ptr to offset */
  1054.  
  1055.    oc = memnode();           /* Acquire new memory node */
  1056.    
  1057.    if (oc == 0)              /* If insufficient memory */
  1058.      goto bust;              /* Handle situation below */
  1059.  
  1060.    a = FreeMem;              /* Addr of (new) node array */
  1061.  
  1062.    pa = (I32s Hp)a + opa;    /* Relocated parental ptr */
  1063.    c = a + oc;               /* Addr of new memory node */
  1064.  
  1065.    c->l = scratch.l;         /* Copy ptrs from scratch */
  1066.    c->r = scratch.r;
  1067.  
  1068.    c->p = addr;              /* Set soup addr and size */
  1069.    c->s = size;
  1070.  
  1071.    *pa = c - a;              /* Attach new node to pa */
  1072.    goto foot;                /* Handle the rest below */
  1073.  
  1074.  
  1075.    /* Come here if I encounter a neighbour (or discover
  1076.       overlap) after I have started inserting the scratch
  1077.       node in the tree. Find the second neighbour, if any
  1078.       (and continue checking for overlap). If all is well,
  1079.       coalesce the 2 (or 3) pieces, using (and promoting) 
  1080.       the node that previously described the first neigh-
  1081.       bout (the higher one).  At this point:
  1082.  
  1083.         a = addr of the node array
  1084.         c = addr of possible neighbour
  1085.  
  1086.         pa = addr of `ptr' to scratch node
  1087.         pas = the size of the paternal node
  1088.  
  1089.         lh,rh = addr of left hook, right hook
  1090.  
  1091.         x = addr of piece being deallocated
  1092.         z = endad of piece being deallocated         
  1093.  
  1094.         addr,size are as supplied by caller          */
  1095.  
  1096. lowneigh:
  1097.    *lh = c->l;                  /* Attach subtrees to hooks */
  1098.    *rh = c->r;
  1099.  
  1100.    if (c->p == z) {             /* If this is right neigh */
  1101.      
  1102.      if (*lh != 0) {            /* If left subtree exists */
  1103.        while ((a+*lh)->r != 0)  /* Find rightmost node */
  1104.          lh = &(a+*lh)->r;      /* in the left subtree */
  1105.        e = a + *lh;             /* This is the ultimate node */
  1106.        if (e->p + e->s > x)     /* Watch for invalid overlap */
  1107.          goto recover;
  1108.        if (e->p + e->s == x) {  /* If this is left neigh */
  1109.          x = e->p;              /* Expand node to left */
  1110.          *lh = e->l;            /* Remove left neighbour */
  1111.          e->l = a->l;           /* And recycle the node */
  1112.          a->l = e-a;
  1113.        }
  1114.      }
  1115.  
  1116.      z += c->s;                 /* Expand node to the right */
  1117.  
  1118.    }
  1119.  
  1120.    else if (c->p + c->s == x) { /* If this is left neigh */
  1121.        
  1122.      if (*rh != 0) {            /* If right subtree exists */
  1123.        while ((a+*rh)->l != 0)  /* Find leftmost node */
  1124.          rh= &(a+*rh)->l;       /* in right subtree */
  1125.        e = a + *rh;             /* This is the ultimate node */
  1126.        if (e->p < z)            /* Watch for invalid overlap */
  1127.          goto recover;
  1128.        if (e->p == z) {         /* If this is right neigh */
  1129.          z += e->s;             /* Expand node to right */
  1130.          *rh = e->r;            /* Remove right neighbour */
  1131.          e->l = a->l;           /* And recycle the node */
  1132.          a->l = e-a;
  1133.        }
  1134.      }
  1135.  
  1136.      x = c->p;                  /* Expand node to the left */
  1137.  
  1138.    }
  1139.  
  1140.    else                         /* Or if invalid overlap */
  1141.      goto recover;              /* Handle situation below */
  1142.  
  1143.    c->l = scratch.l;            /* Tree ptrs from scratch */
  1144.    c->r = scratch.r;
  1145.  
  1146.    goto weigh;                  /* Promote node if needed */
  1147.  
  1148.  
  1149.    /* Come here if I encounter a neighbour (or discover
  1150.       overlap) before making any alterations to the tree.
  1151.       Find the second neighbour, if any (and continue to 
  1152.       check for overlap).  If all goes well, coalesce the
  1153.       2 (or 3) pieces, using the node that previously de-
  1154.       scribed the first neighbour (the higher one).  At
  1155.       this pt:
  1156.  
  1157.         a = addr of the node array
  1158.         c = addr of possible neighbour
  1159.  
  1160.         pa = addr of `ptr' to scratch node
  1161.         pas = the size of the paternal node
  1162.  
  1163.         x = addr of piece being deallocated
  1164.         z = endad of piece being deallocated         
  1165.  
  1166.         addr,size are as supplied by caller          */
  1167.  
  1168. highneigh:
  1169.    if (c->p == z) {             /* If this is right neigh */
  1170.      
  1171.      if (c->l != 0) {           /* If left subtree exists */
  1172.        d = &c->l;               /* Find rightmost node ... */
  1173.        while ((a+*d)->r != 0)   /* ... in left subtree */
  1174.          d = &(a+*d)->r;
  1175.        e = a + *d;              /* This is the ultimate node */
  1176.        if (e->p + e->s > x)     /* Watch for invalid overlap */
  1177.          goto overlap;
  1178.        if (e->p + e->s == x) {  /* If this is left neigh */
  1179.          x = e->p;              /* Expand node to left */
  1180.          *d = e->l;             /* Remove left neighbour */
  1181.          e->l = a->l;           /* And recycle the node */
  1182.          a->l = e-a;
  1183.        }
  1184.      }
  1185.  
  1186.      z += c->s;                 /* Expand node to the right */
  1187.  
  1188.    }
  1189.  
  1190.    else if (c->p + c->s == x) { /* If this is left neigh */
  1191.        
  1192.      if (c->r != 0) {           /* If right subtree exists */
  1193.        d = &c->r;               /* Find leftmost node ... */
  1194.        while ((a+*d)->l != 0)   /* ... in right subtree */
  1195.          d = &(a+*d)->l;
  1196.        e = a + *d;              /* This is the ultimate node */
  1197.        if (e->p < z)            /* Watch for invalid overlap */
  1198.          goto overlap;
  1199.        if (e->p == z) {         /* If this is right neigh */
  1200.          z += e->s;             /* Expand node to right */
  1201.          *d = e->r;             /* Remove right neighbour */
  1202.          e->l = a->l;           /* And recycle the node */
  1203.          a->l = e-a;
  1204.        }
  1205.      }
  1206.  
  1207.      x = c->p;                  /* Expand node to the left */
  1208.  
  1209.    }
  1210.  
  1211.    else                         /* Or if invalid overlap */
  1212.      goto overlap;              /* Handle situation below */
  1213.  
  1214.  
  1215.    /* Arrive here after combining the deallocated piece
  1216.       with one or two neighbours, to see if the combined
  1217.       node requires promotion.  At this point:
  1218.  
  1219.         a = addr of the node array
  1220.         c = addr of the combined node
  1221.  
  1222.         pa = addr of `ptr' to combined node
  1223.         pas = the size of the paternal node
  1224.  
  1225.         x = addr of piece being deallocated
  1226.         z = endad of piece being deallocated         
  1227.  
  1228.         addr,size are as supplied by caller          */
  1229.  
  1230. weigh:
  1231.    c->p = x;                    /* Combined addr and size */
  1232.    c->s = z-x;
  1233.  
  1234.    *pa = c - a;                 /* Hang correctly in tree */
  1235.  
  1236.    if (c->s > pas)              /* If size exceeds father */
  1237.      promote (&a->r,c);         /* Promote combined node */
  1238.  
  1239.  
  1240.    /* All successful paths join here.  Keep `almond'
  1241.       informed, and return to my caller. On arrival:
  1242.  
  1243.         addr,size are as supplied by caller          */
  1244.  
  1245. foot:
  1246.    FreeMemCurrent += size;      /* Maintain all the books */
  1247.  
  1248. #ifdef ALCOMM                   /* Keep `almond' informed */
  1249.    if (MIsDFEnabled(TrtOrgLifeEvent))
  1250.      TRepDeath (addr,size);
  1251. #endif
  1252.  
  1253.    return;                      /* And return exhausted */
  1254.  
  1255.  
  1256.    /* Come here if I discover invalid overlap after
  1257.       starting to insert a node describing the piece
  1258.       being deallocated. Promote the overlapping node
  1259.       (or the first neighbour, if that is higher) to
  1260.       the position occupied by the scratch node, and
  1261.       then demote it (again) if necessary. I do not 
  1262.       guarantee that the tree will have the same
  1263.       shape as before, but I do guarantee that 
  1264.       it will be valid.  On arrival:   
  1265.  
  1266.         a = the addr of the node array
  1267.         c = addr of 1st neighbour, or addr
  1268.             of overlapping node, whichever 
  1269.             is higher in the tree
  1270.  
  1271.         pa = addr of `ptr' to scratch node        */
  1272.  
  1273. recover:
  1274.    c->l = scratch.l;            /* Tree ptrs from scratch */
  1275.    c->r = scratch.r;
  1276.  
  1277.    demote (pa,c);               /* Demote or reattach */
  1278.  
  1279.  
  1280.    /* Join here if I discover invalid overlap before
  1281.       making any alterations to the tree. Report the
  1282.       problem and terminate the entire program.   */
  1283.  
  1284. overlap:   
  1285.    FEError (-602,EXIT,WRITE,
  1286.             "Tierra MemDealloc error: corrupted soup addrs");
  1287.  
  1288.    /* Come here if a new memory node is required but there
  1289.       is insufficient system memory available.  Remove the 
  1290.       scratch node from the tree, report the problem verb-
  1291.       ally, and terminate the entire program. At this pt:
  1292.  
  1293.         pa = addr of `ptr' to scratch node              */
  1294.  
  1295. bust:
  1296.    delete (pa,&scratch);        /* Delete the scratch node */  
  1297.  
  1298.    FEError (-602,EXIT,WRITE,
  1299.             "Tierra MemDealloc error: insuf system memory avail");
  1300.  
  1301.  
  1302.  
  1303. /*  __________________________________________________
  1304.    |                                                  | 
  1305.    |  Function delete -- Delete a node from the tree  |
  1306.    |__________________________________________________|
  1307.  
  1308.    Private function for MemAlloc() and MemDealloc()
  1309.  
  1310.    Call is:  delete (pa,victim)
  1311.  
  1312.    where:    pa = addr of parental `ptr' to victim
  1313.              victim = addr of the node to be deleted
  1314.  
  1315.    Also:     FreeMem = addr of memory node array   
  1316.  
  1317.    Notes:    1.  On entry, the victim need not be
  1318.                  connected to its paternal node.
  1319.  
  1320.              2.  The function deletes the node from
  1321.                  the tree but it does not chain it
  1322.                  into the recycle list. This is the
  1323.                  responsibility of the caller.    */
  1324.  
  1325.  
  1326. static void delete (pa,victim)  I32s Hp pa; Pmf victim;  {
  1327.  
  1328. Pmf   a;         /* Addr of the memory node array */
  1329.  
  1330. I32s  lb,rb;     /* `Ptrs' for left and right branch */
  1331.  
  1332.    a = FreeMem;              /* Addr of node array */
  1333.    lb = victim->l;           /* Index of the left son */ 
  1334.    rb = victim->r;           /* Index of the right son */
  1335.  
  1336.    while (lb!=0 && rb!=0)    /* While both subtrees exist */
  1337.      if ((a+lb)->s >= (a+rb)->s) {  /* If left node longer */
  1338.        *pa = lb;                    /* Attach to my father */  
  1339.        pa = &(a+lb)->r;             /* Father falls to right */
  1340.        lb = *pa;                    /* Remaining left subtree */
  1341.      } else {
  1342.        *pa = rb;
  1343.        pa = &(a+rb)->l;
  1344.        rb = *pa;
  1345.      }
  1346.  
  1347.    *pa = lb + rb;            /* Attach surviving subtree [sic] */
  1348.    return;
  1349.  
  1350. }        
  1351.  
  1352.  
  1353. /*  ________________________________________________
  1354.    |                                                | 
  1355.    |  Function demote -- Demote a node in the tree  |
  1356.    |________________________________________________|
  1357.  
  1358.    Private function for MemAlloc() and MemDealloc()
  1359.  
  1360.    Call is:  demote (pa,victim)
  1361.  
  1362.    where:    pa = addr of parental `ptr' to victim
  1363.              victim = addr of the node to be demoted
  1364.  
  1365.    Also:     FreeMem = addr of memory node array   
  1366.  
  1367.    Notes:    1.  On entry, the victim need not be
  1368.                  connected to its paternal node.
  1369.  
  1370.              2.  If the victim is already at the
  1371.                  correct level, it is simply con-
  1372.                  nected to the given father.      */
  1373.  
  1374.  
  1375. static void demote (pa,victim)  I32s Hp pa; Pmf victim;  {
  1376.  
  1377. Pmf   a;         /* Addr of the memory node array */
  1378.  
  1379. I32s  lb,rb;     /* `Ptrs' for left and right branch */
  1380.  
  1381.    a = FreeMem;              /* Addr of the node array */
  1382.    lb = victim->l;           /* Index of the left son */ 
  1383.    rb = victim->r;           /* Index of the right son */
  1384.  
  1385.    while ((a+lb)->s > victim->s || (a+rb)->s > victim->s) 
  1386.                                     /* While among big nodes */
  1387.      if ((a+lb)->s >= (a+rb)->s) {  /* If left node is longer */
  1388.        *pa = lb;                    /* Attach it to my father */  
  1389.        pa = &(a+lb)->r;             /* Father falls to right */
  1390.        lb = *pa;                    /* Remaining left subtree */
  1391.      } else {
  1392.        *pa = rb;
  1393.        pa = &(a+rb)->l;
  1394.        rb = *pa;
  1395.      }
  1396.  
  1397.    *pa = victim - a;         /* Insert demoted node here */
  1398.    victim->l = lb;           /* Set ptrs to both new sons */
  1399.    victim->r = rb;
  1400.    return;
  1401.  
  1402. }        
  1403.  
  1404.  
  1405. /*  __________________________________________________
  1406.    |                                                  | 
  1407.    |  Function promote -- Promote a node in the tree  |
  1408.    |__________________________________________________|
  1409.  
  1410.    Private function for MemAlloc() and MemDealloc()
  1411.  
  1412.    Call is:  promote (adam,riser)
  1413.  
  1414.    where:    adam = addr of `ptr' to root of tree
  1415.              riser = addr of node to be promoted
  1416.                      (must be somewhere in tree)
  1417.  
  1418.    Also:     FreeMem = addr of memory node array   
  1419.  
  1420.    Note:     On entry, the node to be promoted
  1421.              must be properly connected in the 
  1422.              tree, and must require promotion.    */
  1423.  
  1424.  
  1425. static void promote (pa,riser)  I32s Hp pa; Pmf riser;  {
  1426.  
  1427. Pmf   a,         /* Address of the memory node array */
  1428.       c;         /* Addr of current node in old tree */ 
  1429.  
  1430. I32s  Hp lh,     /* Left hook for root insertion */
  1431.       Hp rh,     /* Right hook for root insertion */
  1432.       ls,rs;     /* Indices for left,right subtree */
  1433.  
  1434.  
  1435.    a = FreeMem;              /* Addr of the node array */
  1436.    c = a + *pa;              /* Addr of the tree root */
  1437.  
  1438.    /* Perform a passive binary search for the node to be
  1439.       promoted until I encounter a node which describes
  1440.       an area of soup that is no longer than that des-
  1441.       cribed by the node to be promoted.              */
  1442.  
  1443.    while (c->s > riser->s) { /* While this size > riser */
  1444.  
  1445.      if (c->p < riser->p)    /* Perform a passive search */     
  1446.        pa = &c->r;
  1447.      else
  1448.        pa = &c->l;
  1449.  
  1450.      c = a + *pa;            /* Addr of next node down */
  1451.  
  1452.    }
  1453.  
  1454.    /* Insert the rising node at the root of the remaining
  1455.       subtree.                                         */
  1456.  
  1457.    *pa = riser - a;          /* Attach riser to new pa */
  1458.    lh = &riser->l; ls = *lh; /* Init hooks and save old */
  1459.    rh = &riser->r; rs = *rh;
  1460.  
  1461.    while (c != riser)        /* Until reach original riser */
  1462.  
  1463.      if (c->p < riser->p) {  /* If current node is to left */
  1464.        *lh = c - a;          /* Attach node to left hook */
  1465.        lh = &c->r;           /* Left hook falls to right */
  1466.        c = a + *lh;          /* Addr of next current node */
  1467.      } else {
  1468.        *rh = c - a;
  1469.        rh = &c->l;
  1470.        c = a + *rh;
  1471.      }
  1472.  
  1473.    *lh = ls; *rh = rs;       /* Attach both the subtrees */
  1474.    return;
  1475.  
  1476. }
  1477.  
  1478.  
  1479. /*  ____________________________________________________
  1480.    |                                                    | 
  1481.    |  Function memnode -- Supply an unused memory node  | 
  1482.    |____________________________________________________|
  1483.  
  1484.    Private function for MemAlloc() and MemDealloc()
  1485.  
  1486.    Call is:  b = memnode ()
  1487.  
  1488.    where:    b = index of memory node supplied, or zero 
  1489.                  if unable to supply a new memory node
  1490.  
  1491.    Also:     FreeMem       = addr of memory node array  :  May be changed
  1492.              MaxFreeBlocks = size of memory node array  :  as side effect 
  1493.  
  1494.    Summary:  Supply a recycled node if available.  Else
  1495.              supply an untouched node if available. Else
  1496.              increase the size of the array and supply 
  1497.              the first untouched node from the new part
  1498.              -- or yield a dummy value of zero if the
  1499.              array already occupies LONG_MAX bytes or
  1500.              more, or if there is insufficient system
  1501.              memory available.
  1502.  
  1503.    Note:     Callers of this function must relocate any
  1504.              private pointers which address data in the 
  1505.              node array, in order to compensate for pos-
  1506.              sible reallocation of the array.  This re-
  1507.              quires peculiar care, owing to the way in 
  1508.              which some C compilers implement ptr arith-
  1509.              metic.  Consider for example the following 
  1510.              situation:
  1511.  
  1512.                a = addr of the old array
  1513.                b = addr of the new array
  1514.                c = addr of an (old) node
  1515.  
  1516.              It is tempting to try and relocate `c' by
  1517.              writing:
  1518.  
  1519.                c = c + (b - a);      [or c += b - a;]
  1520.  
  1521.              However, some compilers assume that `a' and
  1522.              `b' refer to nodes in the same instance of
  1523.              the node array.  They therefore `know' that
  1524.              the value of (b-a) must be a multiple of 16
  1525.              (the size of a memory node). As a result (it
  1526.              appears) they feel justified in clearing the
  1527.              four low-order bits, which yields the wrong
  1528.              answer unless (a mod 16) happens to equal
  1529.              (b mod 16). 
  1530.  
  1531.              To avoid this problem, one is tempted to
  1532.              write:
  1533.  
  1534.                c = b + (c - a);
  1535.  
  1536.              This may in fact work (I have not tried it);
  1537.              but in principle parentheses in C do not de-
  1538.              termine the order of evaluation, so there is
  1539.              no guarantee that it will behave any differ-
  1540.              ently from the first method.      
  1541.  
  1542.              The safest thing to do seems to be to convert 
  1543.              all ptrs to offsets before calling newnode(),
  1544.              and then convert them back to ptrs afterwards.
  1545.              Suppose for example that u is a ptr to a node,
  1546.              and v is a ptr to an object of type V *in* a
  1547.              node.  Then the following sequence allocates
  1548.              a new node w and relocates the old ptrs if
  1549.              necessary:
  1550.  
  1551.                ou = u - FreeMem;       Integral offsets 
  1552.                ov = v - (V*)FreeMem;
  1553.  
  1554.                ow = memnode();         Allocate new node
  1555.  
  1556.                if (ow == 0)            If insuf sys mem
  1557.                  goto ...;             handle mess below 
  1558.  
  1559.                u = FreeMem + ou;       Updated node ptr
  1560.                v = (V*)FreeMem + ov;   Internal ptr too
  1561.                w = FreeMem + ow;       Addr of new node   */
  1562.  
  1563.  
  1564. static I32s memnode () {
  1565.  
  1566. Pmf   a;         /* Addr of memory node array */
  1567. I32s  b;         /* Index of the winning node */
  1568.  
  1569.    a = FreeMem;              /* Addr of the node array */
  1570.    b = a->l;                 /* Magic for available nodes */
  1571.  
  1572.    if (b > 0) {              /* If recycled nodes exist */
  1573.      a->l = (a+b)->l;        /* Remove 1st node from list */
  1574.      return (b);             /* Well that was quite easy */
  1575.    }
  1576.  
  1577.    if (b < 0) {              /* If untouched nodes exist */
  1578.      a->l ++;                /* Reduce the untouched part */
  1579.      return (MaxFreeBlocks+b);  /* Tricky, but still easy */
  1580.    }
  1581.  
  1582.    /* Arrive here if the entire node array is in use.
  1583.       Double the size of the array and supply the 1st
  1584.       node in the (new) second half.               */
  1585.  
  1586.    b = MaxFreeBlocks;                /* Existing array size */
  1587.  
  1588.    if (b > LONG_MAX/sizeof(MemFr))   /* If expansion impossible */
  1589.      return (0);                     /* Supply nothing whatever */
  1590.  
  1591.    a = (Pmf) threcalloc ((I8s Hp)a,      /* Enlarge node array */
  1592.              (I32u)(2*b*sizeof(MemFr)),  /* Desired array size */
  1593.              (I32u)(b*sizeof(MemFr)));   /* Old size of array */
  1594.   
  1595.    if (a == NULL)                    /* If insuf system memory */
  1596.      return (0);                     /* Supply nothing whatever */
  1597.  
  1598.    FreeMem = a;              /* Reset the global variables */
  1599.    MaxFreeBlocks = 2*b;
  1600.  
  1601.    a->l = b - (2*b) + 1;     /* -ve index of untouched part */
  1602.    return (b);               /* Supply 1st node in 2nd half */
  1603.  
  1604. }
  1605.