Annotation of coherent/f/etc/conf/streams/src/st_alloc.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Main C routines for the quick heap manager.
                      3:  *
                      4:  * This implementation is directly derived from the article in which this
                      5:  * algorithm first appeared ;
                      6:  *
                      7:  *     "Efficient Implementation of the First-Fit Strategy for Dynamic
                      8:  *      Storage Allocation"
                      9:  *     R. P. Brent, Australian National University
                     10:  *     ACM Transactions on Programming Languages and Systems
                     11:  *     Volume 11, No. 3, July 1989 pp 388-403.
                     12:  */
                     13: 
                     14: /*
                     15:  *-IMPORTS:
                     16:  *     <common/ccompat.h>
                     17:  *             __CONST__
                     18:  *             __USE_PROTO__
                     19:  *             __ARGS ()
                     20:  *     <common/xdebug.h>
                     21:  *             __LOCAL__
                     22:  *     <stddef.h>
                     23:  *             NULL
                     24:  */
                     25: 
                     26: #include <common/ccompat.h>
                     27: #include <common/xdebug.h>
                     28: #include <stddef.h>
                     29: 
                     30: #include <kernel/st_alloc.h>
                     31: 
                     32: 
                     33: /*
                     34:  * Since this code can be used in an embedded environment, we may not want to
                     35:  * use the C library version of assert () to report errors. On the other hand,
                     36:  * in a hosted environment we might.
                     37:  */
                     38: 
                     39: #if    _HOSTED
                     40: 
                     41: #include <assert.h>
                     42: 
                     43: #define        ASSERT(x)       assert (x)
                     44: 
                     45: #else
                     46: 
                     47: #include <sys/debug.h>
                     48: 
                     49: #endif
                     50: 
                     51: 
                     52: /*
                     53:  * In several places in the code we need to adjust pointer values by +1 or -1
                     54:  * to account for the space taken up the by the links between adjacent
                     55:  * blocks, allocated or not.
                     56:  *
                     57:  * Here we define some private macros to help deal with this. While it is
                     58:  * correct and reasonably documented as it stands, we will likely want to
                     59:  * implement a variant algorithm that does not keep allocated blocks on a
                     60:  * list. This should reduce both search time and space overhead, with the
                     61:  * expense of requiring the user to supply the original block size. For now,
                     62:  * we pay both penalties.
                     63:  */
                     64: 
                     65: #define _ST_WORD_S     sizeof (_ST_WORD_T)
                     66: 
                     67: #define        _ST_AHDR_SIZE   1               /* word size of an allocated header */
                     68: 
                     69: #define        _ST_BYTE2WORD(s) ((s + (1 + _ST_AHDR_SIZE) * _ST_WORD_S - 1) / _ST_WORD_S)
                     70:                                        /*
                     71:                                         * _ST_BYTE2WORD () converts a size
                     72:                                         * passed in by a client into a block
                     73:                                         * word size, which includes space
                     74:                                         * for the allocated-block header.
                     75:                                         */
                     76: #define        _ST_WORD2BYTE(s) ((s - _ST_AHDR_SIZE) * _ST_WORD_S)
                     77:                                        /*
                     78:                                         * _ST_WORD2BYTE () reverses the
                     79:                                         * mapping given by _ST_BYTE2WORD ().
                     80:                                         */
                     81: #define        _ST_ADDR2PTR(q,a)       ((__VOID__ *) a)
                     82:                                        /*
                     83:                                         * Mapping from an _ST_ADDR_T to a
                     84:                                         * user pointer to real memory.
                     85:                                         */
                     86: #define        _ST_PTR2ADDR(q,a)       ((_ST_WORD_T *) a)
                     87:                                        /*
                     88:                                         * Mapping from a user pointer to
                     89:                                         * an _ST_ADDR_T.
                     90:                                         */
                     91: 
                     92: #define        _ST_BUCKET_BASE(q,b) (q->_arena_base + (b) * q->_words_per_bucket)
                     93: 
                     94: /*
                     95:  * This function does some general checks on the consistency of the contents
                     96:  * of the _ST_HEAP_CONTROL block passed as "q". If the contents of the control
                     97:  * block (and the auxiliary arrays which are part of it) do not pass muster,
                     98:  * then the _qheap_error member of the control block is set, and the function
                     99:  * returns an error indication.
                    100:  *
                    101:  * The return value is 0 if the control block tested OK, -1 if it did not.
                    102:  */
                    103: 
                    104: #if    __USE_PROTO__
                    105: int (st_assert) (_ST_HEAP_CONTROL_P q)
                    106: #else
                    107: int
                    108: st_assert __ARGS ((q))
                    109: _ST_HEAP_CONTROL_P     q;
                    110: #endif
                    111: {
                    112:        _ST_ADDR_T      scan;
                    113:        int             count, cntl, bucket;
                    114: 
                    115:        scan = _ST_BUCKET_BASE (q, 0);
                    116: 
                    117:        for (bucket = 0 ; bucket < q->_buckets_inuse ; bucket ++) {
                    118:                _ST_ADDR_T      end;
                    119:                int             max;
                    120: 
                    121: 
                    122:                if ((end = _ST_BUCKET_BASE (q, bucket + 1)) > q->_arena_end)
                    123:                        end = q->_arena_end;
                    124: 
                    125:                /*
                    126:                 * Verify that _ST_HEAP_FIRST () points within the bucket,
                    127:                 * to the current value of scan if there are blocks in this
                    128:                 * bucket.
                    129:                 */
                    130: 
                    131: 
                    132:                if (scan < end &&
                    133:                    _ST_HEAP_FIRST (q, bucket) != scan) {
                    134: 
                    135:                        _ST_SET_ERROR (q, "Bad first block pointer in bucket");
                    136:                        return -1;
                    137:                } else if (scan >= end && _ST_HEAP_FIRST (q, bucket) < end) {
                    138: 
                    139:                        _ST_SET_ERROR (q, "First block pointer should be pointing past bucket end");
                    140:                        return -1;
                    141:                }
                    142: 
                    143: 
                    144:                /*
                    145:                 * Walk over the contents of the bucket to verify the maximum
                    146:                 * block size within the bucket. We also check here for
                    147:                 * consecutive unmerged free blocks.
                    148:                 */
                    149: 
                    150:                count = max = 0;
                    151: 
                    152:                while (scan < end) {
                    153: 
                    154:                        cntl = _ST_BLOCK_CONTROL (q, scan);
                    155: 
                    156:                        if (_ST_BLOCK_FREE (cntl)) {    /* block is free */
                    157: 
                    158:                                if (cntl > max)
                    159:                                        max = cntl;
                    160: 
                    161:                                if (++ count > 1) {
                    162: 
                    163:                                        _ST_SET_ERROR (q, "Adjacent free blocks not merged");
                    164:                                        return -1;
                    165:                                }
                    166:                        } else                          /* block in use */
                    167:                                count = 0;
                    168: 
                    169:                        scan = _ST_HEAP_NEXT (scan, cntl);
                    170:                }
                    171: 
                    172:                if (_ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) != max) {
                    173: 
                    174:                        _ST_SET_ERROR (q, "Incorrect maximum free block size");
                    175:                        return -1;
                    176:                }
                    177: 
                    178: 
                    179:                /*
                    180:                 * Check for unmerged free blocks between bucket boundaries.
                    181:                 */
                    182: 
                    183:                if (count != 0 && scan < q->_arena_end &&
                    184:                    _ST_BLOCK_FREE (_ST_BLOCK_CONTROL (q, scan))) {
                    185: 
                    186:                        _ST_SET_ERROR (q, "Unmerged free blocks between block boundaries");
                    187:                        return -1;
                    188:                }
                    189:        }
                    190: 
                    191: 
                    192:        if (scan != q->_arena_end) {
                    193: 
                    194:                _ST_SET_ERROR (q, "Last block too large!");
                    195:                return -1;
                    196:        }
                    197: 
                    198:        return 0;
                    199: }
                    200: 
                    201: 
                    202: /*
                    203:  * st_double () doubles the depth of the index heap, assuming that
                    204:  * _buckets_inuse < _buckets_maximum / 2
                    205:  *
                    206:  * For internal use only.
                    207:  */
                    208: 
                    209: #if    __USE_PROTO__
                    210: __LOCAL__ void (st_double) (_ST_HEAP_CONTROL_P q)
                    211: #else
                    212: __LOCAL__ void
                    213: st_double __ARGS ((q))
                    214: _ST_HEAP_CONTROL_P     q;
                    215: #endif
                    216: {
                    217:        int i,k;
                    218: 
                    219:        ASSERT (q->_buckets_inuse * 2 <= q->_buckets_maximum);
                    220: 
                    221: 
                    222:        /*
                    223:         * The new buckets don't have any blocks starting within them, so
                    224:         * set the base address over the top.
                    225:         */
                    226: 
                    227:        k = q->_buckets_inuse;
                    228:        for (i = 0 ; i < k ; i ++)
                    229:                q->_bucket_first [i + k] = q->_arena_end;
                    230: 
                    231:        /*
                    232:         * The bucket size heap needs to be expanded by copying the maximum
                    233:         * sizes down for the preexisting buckets, and inserting zeroes for
                    234:         * the new buckets.
                    235:         *      eg      max(b1,b2) b1 b2
                    236:         *      becomes max(b1,b2) max(b1,b2) 0 b1 b2 0 0
                    237:         */
                    238: 
                    239:        for (; k > 0 ; k >>= 1)
                    240:                for (i = 0 ; i < k ; i ++) {
                    241: 
                    242:                        _ST_HEAP_BIGGEST (q, 2 * k + i) = _ST_HEAP_BIGGEST (q, k + i);
                    243:                        _ST_HEAP_BIGGEST (q, 3 * k + i) = 0;
                    244:                }
                    245: 
                    246:        q->_buckets_inuse <<= 1;
                    247: }
                    248: 
                    249: 
                    250: /*
                    251:  * st_reduced () does the housekeeping necessary if a block with control word
                    252:  * at "a" has been allocated, or merged with a block on it's left in a
                    253:  * different segment. Note that we count on a block having been freed/
                    254:  * allocated and having a valid control word.
                    255:  *
                    256:  * It should be that case that the _ST_HEAP_FIRST () has already been updated
                    257:  * in order for this routine to correctly calculate the sizes
                    258:  *
                    259:  * This implementation modifies this function, in that a test
                    260:  *     if (BlockIsFree && CurrentMaximum > SizeOfNewBlock)
                    261:  *             return;
                    262:  * from the C transliteration of the published algorithm has been moved out
                    263:  * to the callers of this function. This allows the callers to use a block
                    264:  * size value cached in their locals to perform the calculation, rather than
                    265:  * making this function fetch the control word for the block. This is NOT
                    266:  * an important size/speed optimisation (although parts of the original test
                    267:  * have been elided in the broken-out version), rather it allows algorithms
                    268:  * like realloc () considerably more latitude in the order in which things
                    269:  * are done.
                    270:  *
                    271:  * This function was called blfix1 () in the published algorithm. The original
                    272:  * algorithm passed in the block address, but since we have moved the check
                    273:  * discussed above outside, we only need the bucket number.
                    274:  */
                    275: 
                    276: #if    __USE_PROTO__
                    277: __LOCAL__ void (st_reduced) (_ST_HEAP_CONTROL_P q, int bucket)
                    278: #else
                    279: __LOCAL__ void
                    280: st_reduced __ARGS ((q, bucket))
                    281: _ST_HEAP_CONTROL_P     q;
                    282: int                    bucket;
                    283: #endif
                    284: {
                    285:        _ST_ADDR_T first, next;
                    286:        _ST_WORD_T max;
                    287: 
                    288: #if 0  /* this test moved out to our callers */
                    289:        /*
                    290:         * if (BlockIsFree && CurrentMaximum > SizeOfNewBlock) then there
                    291:         * is nothing to be done to segment "bucket".
                    292:         */
                    293: 
                    294:        max = _ST_BLOCK_CONTROL (q, a);
                    295:        if (_ST_BLOCK_FREE (max) &&
                    296:            _ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) > _ST_BLOCK_SIZE (max))
                    297:                return;
                    298: #endif
                    299: 
                    300:        /*
                    301:         * Work out address of first block in "current" bucket, and the
                    302:         * base address of the "next" bucket. Note that we have to test to
                    303:         * see if "next" winds up past the end of space because the total
                    304:         * size of the arena may not divide evenly into buckets..
                    305:         */
                    306: 
                    307:        first = _ST_HEAP_FIRST (q, bucket);
                    308:        next = q->_arena_base + (bucket + 1) * q->_words_per_bucket;
                    309: 
                    310:        if (next >= q->_arena_end)
                    311:                next = q->_arena_end;
                    312: 
                    313:        /*
                    314:         * Calculate (by walking the block chain) the new maximum size free
                    315:         * block in this bucket, which may now be empty.
                    316:         */
                    317: 
                    318:        max = 0;
                    319:        while (first < next) {  /* There is a block starting in this bucket */
                    320:                _ST_WORD_T cntl = _ST_BLOCK_CONTROL (q, first);
                    321: 
                    322:                ASSERT (cntl != 0);
                    323: 
                    324:                if (max < cntl)
                    325:                        max = cntl;
                    326: 
                    327:                first = _ST_HEAP_NEXT (first, cntl);
                    328:        }
                    329: 
                    330: 
                    331:        /*
                    332:         * Now propagate the new maximum size information up the heap.
                    333:         */
                    334: 
                    335:        bucket += q->_buckets_inuse;
                    336:        _ST_HEAP_BIGGEST (q, 0) = 0;    /* sentinel */
                    337: 
                    338:        while (_ST_HEAP_BIGGEST (q, bucket) > max) {
                    339:                int temp;
                    340: 
                    341:                _ST_HEAP_BIGGEST (q, bucket) = max;
                    342: 
                    343:                temp = _ST_HEAP_BIGGEST (q, _ST_BUCKET_SIBLING (bucket));
                    344:                if (max < temp)
                    345:                        max = temp;
                    346: 
                    347:                bucket = _ST_BUCKET_PARENT (bucket);
                    348:        }
                    349: }
                    350: 
                    351: 
                    352: /*
                    353:  * st_grown () does the housekeeping necessary after a block with control word
                    354:  * at "a" is freed, or merged with a block on its right, or created by
                    355:  * splitting (with "a" on the right of the split, in a different bucket than
                    356:  * the start of the original block that was split)
                    357:  *
                    358:  * This function was called blfix2 () in the published algorithm.
                    359:  */
                    360: 
                    361: #if    __USE_PROTO__
                    362: __LOCAL__ void (st_grown) (_ST_HEAP_CONTROL_P q, _ST_ADDR_T a, int bucket)
                    363: #else
                    364: __LOCAL__ void
                    365: st_grown __ARGS ((q, a, bucket))
                    366: _ST_HEAP_CONTROL_P     q;
                    367: _ST_ADDR_T             a;
                    368: int                    bucket;
                    369: #endif
                    370: {
                    371:        int     max;
                    372: 
                    373:        ASSERT (bucket == _ST_HEAP_BUCKET (q, a));
                    374: 
                    375: 
                    376:        /*
                    377:         * Expand the number of buckets in the heap if necessary.
                    378:         */
                    379: 
                    380:        while (bucket >= q->_buckets_inuse)
                    381:                st_double (q);
                    382: 
                    383:        /*
                    384:         * This may be a new first free block.
                    385:         */
                    386: 
                    387:        if (_ST_HEAP_FIRST (q, bucket) > a)
                    388:                _ST_HEAP_FIRST (q, bucket) = a;
                    389: 
                    390:        /*
                    391:         * Propagate changed block-size information upwards through heap.
                    392:         */
                    393: 
                    394:        bucket += q->_buckets_inuse;
                    395:        max = _ST_BLOCK_CONTROL (q, a);
                    396:        _ST_HEAP_BIGGEST (q, 0) = max;  /* sentinel */
                    397: 
                    398:        while (_ST_HEAP_BIGGEST (q, bucket) < max) {
                    399: 
                    400:                _ST_HEAP_BIGGEST (q, bucket) = max;
                    401: 
                    402:                bucket = _ST_BUCKET_PARENT (bucket);
                    403:        }
                    404: }
                    405: 
                    406: 
                    407: /*
                    408:  * Returns the predecessor block to "a", which is guaranteed to exist thanks
                    409:  * to the dummy first block.
                    410:  *
                    411:  * If the address passed in does not belong to a valid block then the value
                    412:  * returned is the same value that was passed in, ie "a".
                    413:  */
                    414: 
                    415: #if    __USE_PROTO__
                    416: __LOCAL__ _ST_ADDR_T (st_pred) (_ST_HEAP_CONTROL_P q, _ST_ADDR_T a, int bucket)
                    417: #else
                    418: __LOCAL__ _ST_ADDR_T
                    419: st_pred __ARGS ((q, a, bucket))
                    420: _ST_HEAP_CONTROL_P     q;
                    421: _ST_ADDR_T             a;
                    422: int                    bucket;
                    423: #endif
                    424: {
                    425:        _ST_ADDR_T prev, scan;
                    426: 
                    427:        ASSERT (bucket == _ST_HEAP_BUCKET (q, a));
                    428: 
                    429: 
                    430:        /*
                    431:         * If the passed-in block is the first in the bucket, then the
                    432:         * predecessor lives in the rightmost non-empty bucket to the left.
                    433:         */
                    434: 
                    435:        if (_ST_HEAP_FIRST (q, bucket) == a) {
                    436:                /*
                    437:                 * We walk the heap to find the nearest bucket on the left
                    438:                 * that has a free block in it, as buckets that do not
                    439:                 * contain free blocks may not contain a valid "first block"
                    440:                 * entry, since due to block coalescence these entries may
                    441:                 * refer to blocks which have been combined with others.
                    442:                 *
                    443:                 * st_pred () is the only code which is affected by this.
                    444:                 */
                    445: 
                    446:                bucket += q->_buckets_inuse;
                    447: 
                    448:                _ST_HEAP_BIGGEST (q, 0) = 1;    /* boundary */
                    449: 
                    450:                while (_ST_HEAP_BIGGEST (q, bucket - 1) == 0)
                    451:                        bucket = _ST_BUCKET_PARENT (bucket);
                    452: 
                    453:                /*
                    454:                 * Code here fixes two known "defects" in the original
                    455:                 * published algorithm.
                    456:                 *  (i) In the case where "bucket" above could reach the root
                    457:                 *      node (eg, every word of storage had been previously
                    458:                 *      allocated), the "bucket" would go to zero and the
                    459:                 *      descend code would loop indefinitely.
                    460:                  * (ii) In the case where storage in the left subtree of the
                    461:                  *     heap relative to the initial point is completely
                    462:                 *      allocated, the code to climb the heap will eventually
                    463:                 *      look into the next higher level of the heap once
                    464:                  *     "bucket" became a power of two (unless defect (i)
                    465:                 *      occurred instead) and as a result would descend into
                    466:                 *      the wrong part of the heap.
                    467:                 *
                    468:                 * We check here for "bucket" being a power of two, in which
                    469:                 * case we snap the result of the search to the lowest bucket
                    470:                 * on the left.
                    471:                 */
                    472: 
                    473: #define        IS_POWER_OF_TWO(n) (((n - 1) ^ n) >= n)
                    474: 
                    475:                if (! IS_POWER_OF_TWO (bucket)) {
                    476: 
                    477:                        bucket --;
                    478: 
                    479:                        while (bucket < q->_buckets_inuse) {
                    480: 
                    481:                                bucket = 2 * bucket + 1;
                    482:                                if (_ST_HEAP_BIGGEST (q, bucket) <= 0)
                    483:                                        bucket --;
                    484:                        }
                    485: 
                    486:                        bucket -= q->_buckets_inuse;
                    487:                } else
                    488:                        bucket = 0;
                    489:        }
                    490: 
                    491: 
                    492:        /*
                    493:         * Either way, find the predecessor block by following the internal
                    494:         * block linkage within this bucket.
                    495:         */
                    496: 
                    497:        scan = _ST_HEAP_FIRST (q, bucket);
                    498:        do {
                    499:                prev = scan;
                    500:                scan = _ST_HEAP_NEXT (scan, _ST_BLOCK_CONTROL (q, scan));
                    501: 
                    502:                ASSERT (prev != scan);
                    503:        } while (scan < a);
                    504: 
                    505:        if (scan != a) {
                    506: 
                    507:                _ST_SET_ERROR (q, "Unable to find previous for block");
                    508:                return a;       /* flag error by returning same */
                    509:        }
                    510: 
                    511:        return prev;
                    512: }
                    513: 
                    514: 
                    515: /*
                    516:  * Returns the index of a block of at least "size" words, or 0 if no such
                    517:  * block exists.
                    518:  */
                    519: 
                    520: #if    __USE_PROTO__
                    521: __VOID__ * (st_alloc) (_ST_HEAP_CONTROL_P q, size_t size)
                    522: #else
                    523: __VOID__ *
                    524: st_alloc __ARGS ((q, size))
                    525: _ST_HEAP_CONTROL_P     q;
                    526: size_t                 size;
                    527: #endif
                    528: {
                    529:        int             n, bucket, cntl;
                    530:        _ST_ADDR_T      scan;
                    531: 
                    532:        /*
                    533:         * Before we begin, convert the size_t passed in into a word count.
                    534:         * Note that we assume that the integer division of a size_t will
                    535:         * be optimised by the compiler into an appropriate number of right-
                    536:         * shifts (since a size_t is always unsigned, right?).
                    537:         *
                    538:         * We store the result into an integer because the free/used
                    539:         * comparison means we involve the sign bit.
                    540:         */
                    541: 
                    542:        n = _ST_BYTE2WORD (size);       /* includes header size */
                    543: 
                    544: 
                    545:        /*
                    546:         * Since the node at the top of the size heap contains the size of
                    547:         * the largest available block, quickly determine whether or not
                    548:         * this request can be satisfied at all.
                    549:         */
                    550: 
                    551:        if (_ST_HEAP_BIGGEST (q, 1) < n)
                    552:                return 0;
                    553: 
                    554: 
                    555:        /*
                    556:         * Now traverse the heap to find the first bucket containing a block
                    557:         * of sufficient size to satisfy the request.
                    558:         */
                    559: 
                    560:        bucket = 1;
                    561: 
                    562:        while (bucket < q->_buckets_inuse) {
                    563: 
                    564:                bucket = 2 * bucket;
                    565:                if (_ST_HEAP_BIGGEST (q, bucket) < n)
                    566:                        bucket ++;
                    567:        }
                    568: 
                    569:        bucket -= q->_buckets_inuse;
                    570: 
                    571: 
                    572:        /*
                    573:         * Now traverse the internal linkage within the bucket to find the
                    574:         * first block of the requisite size.
                    575:         */
                    576: 
                    577:        scan = _ST_HEAP_FIRST (q, bucket);
                    578: 
                    579:        while ((cntl = _ST_BLOCK_CONTROL (q, scan)) < n) {
                    580: 
                    581:                ASSERT (cntl != 0);
                    582:                scan = _ST_HEAP_NEXT (scan, cntl);
                    583:        }
                    584: 
                    585: 
                    586:        /*
                    587:         * Now "scan" contains the index of the control word of the
                    588:         * desired block.
                    589:         */
                    590: 
                    591:        _ST_BLOCK_SET_USED (q, scan, n);
                    592: 
                    593: 
                    594:        /*
                    595:         * The published algorithm used a variable here to hold the result
                    596:         * of a test on the basis that the call to st_grown () below might
                    597:         * change the result. Actually, st_grown () would not change the
                    598:         * result, but since st_grown () may call st_double () and alter
                    599:         * the offset of the leaf layer of the block heap, that would
                    600:         * invalidate his test. Since our "bucket" does not have that offset
                    601:         * built into it, we perform the test when needed.
                    602:         *
                    603:         * Note that we still perform st_grown () before st_reduced (), as
                    604:         * in the original. The reason for this was not explicated, but
                    605:         * appears to be because doing it in this order may reduce the
                    606:         * average number of heap nodes visited due to the particular heap
                    607:         * update termination conditions.
                    608:         */
                    609: 
                    610:        /*
                    611:         * If necessary, split block; this may require a call to st_grown ()
                    612:         * if the block created by the split is not in the same bucket.
                    613:         */
                    614: 
                    615:        if (cntl > n) {
                    616:                _ST_ADDR_T      next = _ST_HEAP_NEXT (scan, n);
                    617:                int             next_bucket;
                    618: 
                    619:                _ST_BLOCK_SET_FREE (q, next, cntl - n);
                    620: 
                    621:                if ((next_bucket = _ST_HEAP_BUCKET (q, next)) > bucket)
                    622:                        st_grown (q, next, next_bucket);
                    623:        }
                    624: 
                    625:        if (cntl == _ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse))
                    626:                st_reduced (q, bucket);
                    627: 
                    628:        return _ST_ADDR2PTR (q, _ST_HEAP_NEXT (scan, _ST_AHDR_SIZE));
                    629: }
                    630: 
                    631: 
                    632: /*
                    633:  * Release a block of memory obtained using st_alloc (), where "a" is the
                    634:  * memory word index that was returned by st_alloc ().
                    635:  *
                    636:  * Note that this function comes in two flavours, depending on whether you
                    637:  * want clients to have to pass in the block-size.
                    638:  */
                    639: 
                    640: #if    __USE_PROTO__
                    641: int (st_free) (_ST_HEAP_CONTROL_P q, __VOID__ * a ST_FREE_SIZE (size_t size))
                    642: #else
                    643: int
                    644: #ifdef USE_ST_SIZE
                    645: st_free __ARGS ((q, a, size))
                    646: size_t                 size;
                    647: #else
                    648: st_free __ARGS ((q, a))
                    649: #endif
                    650: _ST_HEAP_CONTROL_P     q;
                    651: __VOID__             * a;
                    652: #endif
                    653: {
                    654:        int             bucket, cntl, temp;
                    655:        _ST_ADDR_T      prev, next, addr;
                    656:        int             reduce = 0;     /* optimisation flag, see below */
                    657: 
                    658:        addr = _ST_HEAP_NEXT_RAW (_ST_PTR2ADDR (q, a), - _ST_AHDR_SIZE);
                    659: 
                    660:        cntl = _ST_BLOCK_CONTROL (q, addr);
                    661: 
                    662:        if (_ST_BLOCK_FREE (cntl))
                    663:                return -1;              /* Block already free */
                    664: 
                    665:        cntl = _ST_BLOCK_SIZE (cntl);
                    666: 
                    667: #ifdef USE_ST_SIZE
                    668:        /*
                    669:         * As discussed in st_alloc (), we convert a passed-in byte count
                    670:         * into a word count to isolate the clients from the notion of
                    671:         * what we are using as a "word".
                    672:         *
                    673:         * The expression we want to test is
                    674:         *      cntl == ceil (size / sizeof (_ST_WORD_T)) + 1
                    675:         * where the + 1 factor is for the block size header.
                    676:         */
                    677: 
                    678:        if (cntl != _ST_BYTE2WORD (size))
                    679:                return -2;              /* Block size mismatch */
                    680: #endif
                    681: 
                    682:        bucket = _ST_HEAP_BUCKET (q, addr);
                    683: 
                    684: 
                    685:        /*
                    686:         * Locate the previous block. Note that we attempt this operation
                    687:         * considerably earlier than we really need to; this is done since
                    688:         * it is the only really reliable way of verifying that the address
                    689:         * given to this routine really does belong to a block that was
                    690:         * allocated with st_alloc ().
                    691:         *
                    692:         * (Note that the above statement is only true for versions of the
                    693:         * algorithm that maintain allocated blocks on the block list)
                    694:         */
                    695: 
                    696:        if ((prev = st_pred (q, addr, bucket)) == addr)
                    697:                return -3;              /* not a valid block */
                    698: 
                    699:        /*
                    700:         * Now that we have performed some sanity checks, free the block.
                    701:         */
                    702: 
                    703:        _ST_BLOCK_SET_FREE (q, addr, cntl);
                    704: 
                    705: 
                    706:        /*
                    707:         * Check the next rightmost block to see if we should merge with it.
                    708:         */
                    709: 
                    710:        next = _ST_HEAP_NEXT (addr, cntl);
                    711: 
                    712:        if (next < q->_arena_end &&
                    713:            _ST_BLOCK_FREE (temp = _ST_BLOCK_CONTROL (q, next))) {
                    714:                /*
                    715:                 * Merge the new block with its immediate neighbour on the
                    716:                 * right. Note that we elide the _ST_BLOCK_SIZE () of temp
                    717:                 * immediately below because _ST_BLOCK_SET_FREE masks the
                    718:                 * third argument anyway.
                    719:                 */
                    720: 
                    721:                _ST_BLOCK_SET_FREE (q, addr, cntl += temp);
                    722: 
                    723:                temp = _ST_HEAP_BUCKET (q, next);
                    724: 
                    725:                /*
                    726:                 * Do we need to recalculate the maximum block size
                    727:                 * for the block that "next" is in ? Yes, iff
                    728:                 * heap_biggest (temp) == block_size (next).
                    729:                 * We elide the call to _ST_BLOCK_SIZE below since
                    730:                 * we know the block is free, hence needs no masking.
                    731:                 */
                    732: 
                    733:                reduce = _ST_HEAP_BIGGEST (q, temp + q->_buckets_inuse) ==
                    734:                                _ST_BLOCK_CONTROL (q, next);
                    735:                ASSERT (_ST_HEAP_BIGGEST (q, temp + q->_buckets_inuse) >=
                    736:                                _ST_BLOCK_CONTROL (q, next));
                    737:                if (temp > bucket) {
                    738: 
                    739:                        _ST_HEAP_FIRST (q, temp) = _ST_HEAP_NEXT (addr, cntl);
                    740: 
                    741:                        if (reduce) {
                    742: 
                    743:                                st_reduced (q, temp);
                    744:                                reduce = 0;
                    745:                        }
                    746:                } else
                    747:                        ASSERT (_ST_HEAP_FIRST (q, temp) != next);
                    748:        }
                    749: 
                    750: 
                    751:        /*
                    752:         * Check the next leftmost block to see if we should merge with it.
                    753:         */
                    754:        /*
                    755:         * Optimisation note: the published version of the algorithm
                    756:         * does a normal call to st_reduced () below. However, since we
                    757:         * are freeing a block, we note that the only circumstance where
                    758:         * this will be at all necessary is when the block we are freeing
                    759:         * was merged with a block on it's right which was the previous
                    760:         * largest free block (or at least the same size). We can thus
                    761:         * reduce the number of calls to st_reduced () by putting in an
                    762:         * extra guard condition with a flag set above.
                    763:         *
                    764:         * Is this worth the effort? Let's profile it and see.
                    765:         */
                    766: 
                    767:        if (_ST_BLOCK_FREE (temp = _ST_BLOCK_CONTROL (q, prev))) {
                    768: 
                    769:                _ST_BLOCK_SET_FREE (q, prev, cntl += temp);
                    770: 
                    771:                /*
                    772:                 * If we are merging with a block in a previous bucket, then
                    773:                 * we must adjust our "first block" and call st_reduced () to
                    774:                 * update the size heap.
                    775:                 */
                    776: 
                    777:                if (_ST_HEAP_FIRST (q, bucket) == addr) {
                    778: 
                    779:                        _ST_HEAP_FIRST (q, bucket) = _ST_HEAP_NEXT (prev, cntl);
                    780: 
                    781:                        /*
                    782:                         * As discussed above, we call st_reduced () iff
                    783:                         * we have merged with a block on the right of a
                    784:                         * size that indicates recomputing is necessary.
                    785:                         */
                    786: 
                    787:                        if (reduce) {
                    788: 
                    789:                                ASSERT (_ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) <=
                    790:                                        _ST_BLOCK_CONTROL (q, addr));
                    791:                                st_reduced (q, bucket);
                    792:                        }
                    793: 
                    794:                        /*
                    795:                         * We cannot incrementally update "bucket" since
                    796:                         * "prev" may refer to address a number of buckets
                    797:                         * prior to "addr".
                    798:                         */
                    799: 
                    800:                        bucket = _ST_HEAP_BUCKET (q, prev);
                    801:                }
                    802: 
                    803:                st_grown (q, prev, bucket);
                    804: 
                    805:        } else if (cntl > _ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse)) {
                    806:                /*
                    807:                 * The total size of the newly freed block exceeds the
                    808:                 * previous maximum block size of the current bucket, so call
                    809:                 * st_grown () to update the size heap.
                    810:                 *
                    811:                 * Note that st_alloc () calls st_grown () without performing
                    812:                 * the size test like we do, because st_alloc () may want the
                    813:                 * heap size doubled. Here, that cannot happen, so we avoid
                    814:                 * the call.
                    815:                 */
                    816: 
                    817:                st_grown (q, addr, bucket);
                    818:        }
                    819: 
                    820:        return 0;
                    821: }
                    822: 
                    823: 
                    824: #ifndef        _ST_BLOCK_COPY
                    825: 
                    826: /*
                    827:  * Helper function which supplies a default block-copy routine for the
                    828:  * st_realloc () function in case there is no special-purpose copy routine
                    829:  * in the target environment.
                    830:  */
                    831: 
                    832: #if    __USE_PROTO__
                    833: __LOCAL__  void (st_copy) (_ST_WORD_T * dest, __CONST__ _ST_WORD_T * src,
                    834:                           size_t copywords)
                    835: #else
                    836: __LOCAL__ void
                    837: st_copy __ARGS ((dest, src, copywords))
                    838: _ST_WORD_T           * dest;
                    839: _ST_WORD_T           * src;
                    840: size_t                 copywords;
                    841: #endif
                    842: {
                    843:        while (copywords --)
                    844:                * dest ++ = * src ++;
                    845: }
                    846: 
                    847: #define        _ST_BLOCK_COPY(q,d,s,n) st_copy (_ST_HEAP_ADDR (q, d), _ST_HEAP_ADDR(q, s), n)
                    848: 
                    849: #endif /* ! defined (_ST_BLOCK_COPY) */
                    850: 
                    851: 
                    852: /*
                    853:  * Request that the block of memory at address "a" be grown (or shrunk) in
                    854:  * size to "newsize" bytes (possibly from "oldsize" bytes). The returned
                    855:  * value is the base address of the new block of memory, which may be
                    856:  * different than the old address. If the block was moved, then the contents
                    857:  * are guarenteed to be preserved bit-for-bit, but the client must take
                    858:  * responsibility for relocating pointers.
                    859:  *
                    860:  * If it is not possible for the allocator to either grow or relocate the
                    861:  * block due to a lack of space, st_realloc () returns 0, which for us can
                    862:  * never be a valid return address. In this case, the original block has been
                    863:  * left untouched.
                    864:  *
                    865:  * Note that this function comes in two flavours, depending on whether you
                    866:  * want clients to have to pass in the block-size.
                    867:  *
                    868:  * --------------------------------------------------------------------------
                    869:  *
                    870:  * Design note: there are many, many ways that this function could be
                    871:  * implemented, depending on how sensitive you are to issues of code size,
                    872:  * execution speed, or arena space efficiency.
                    873:  *
                    874:  * The possible checks are (in order of execution time reduction)
                    875:  *   (i) Check to see if the block can be extended upwards,
                    876:  *  (ii) Find the previous block to see if it can be extended downwards,
                    877:  * (iii) See if we can grow both down and up to fulfil the request,
                    878:  *  (iv) Find any other block of sufficient size to fulfil the request.
                    879:  *
                    880:  * The primary criterion I have set for this routine is that it must always
                    881:  * succeed in finding memory to saisfy the request. Note that the number of
                    882:  * cases that this produces as a result is extremely large, but that it seems
                    883:  * better to fulfil a user's request than to fail it simply on the basis that
                    884:  * we want the common case to be fast.
                    885:  *
                    886:  * For now, we'll prefer not to copy, and reduce our code size by just
                    887:  * relying on st_alloc () and st_free () for the worst case... but be aware
                    888:  * that it might be a good idea to set a "realloc mode" in the heap control
                    889:  * block that specifies how we order operations in the case where we break
                    890:  * out the details of new () and disp ().
                    891:  */
                    892: 
                    893: #if    __USE_PROTO__
                    894: __VOID__ * (st_realloc) (_ST_HEAP_CONTROL_P q, __VOID__ * a, size_t newsize
                    895:                         ST_FREE_SIZE (size_t oldsize))
                    896: #else
                    897: __VOID__ *
                    898: #ifdef USE_ST_SIZE
                    899: st_realloc __ARGS ((q, a, newsize, oldsize))
                    900: size_t                 oldsize;
                    901: #else
                    902: st_realloc __ARGS ((q, a, newsize))
                    903: #endif
                    904: _ST_HEAP_CONTROL_P     q;
                    905: __VOID__             * a;
                    906: size_t                 newsize;
                    907: #endif
                    908: {
                    909:        int             bucket, cntl, prev_reduce, next_bucket, next_first;
                    910:        int             delta;          /* block size change in words */
                    911:        _ST_ADDR_T      prev, addr, next;
                    912: 
                    913:        addr = _ST_HEAP_NEXT_RAW (_ST_PTR2ADDR (q, a), -1);
                    914: 
                    915:        cntl = _ST_BLOCK_CONTROL (q, addr);
                    916: 
                    917:        if (_ST_BLOCK_FREE (cntl))
                    918:                return (_ST_ADDR_T) -1; /* Block is free ! */
                    919: 
                    920:        cntl = _ST_BLOCK_SIZE (cntl);   /* work with free units */
                    921: 
                    922: 
                    923:        /*
                    924:         * Before we begin, let's convert newsize (and optionally oldsize)
                    925:         * to word counts from byte counts. This may mean that both round
                    926:         * to the same word count and we don't need to do anything (big win!)
                    927:         */
                    928: 
                    929:        newsize = _ST_BYTE2WORD (newsize);
                    930: 
                    931: #ifdef USE_ST_SIZE
                    932:        if (cntl != _ST_BYTE2WORD (oldsize))
                    933:                return (_ST_ADDR_T) -2; /* Block size mismatch */
                    934: #endif
                    935:        if ((delta = newsize - cntl) == 0)
                    936:                return a;               /* Already done! What service! */
                    937: 
                    938:        bucket = _ST_HEAP_BUCKET (q, addr);
                    939: 
                    940: 
                    941:        /*
                    942:         * Locate the previous block, so that we can see if we can grow
                    943:         * down into it. Note that in the variant system where allocated
                    944:         * blocks are not part of a chain (in order to save space) that by
                    945:         * finding the previous free block we achieve an equivalent result,
                    946:         * since we cannot grow down unless we have an adjacent free block.
                    947:         *
                    948:         * Note also that in the case where allocated blocks are not
                    949:         * chained, finding the previous free block is a prerequisite to
                    950:         * finding the subsequent free block.
                    951:         */
                    952: 
                    953:        if ((prev = st_pred (q, addr, bucket)) == addr)
                    954:                return (_ST_ADDR_T) -3; /* not a valid block */
                    955: 
                    956: 
                    957:        /*
                    958:         * Now that we have performed some sanity checks, look to see how
                    959:         * we should grow the block.
                    960:         */
                    961: 
                    962:        /*
                    963:         * Check the next rightmost block to see if we can grow into it.
                    964:         * If we are shrinking the allocation, this is trivially true.
                    965:         *
                    966:         * To save ourselves a local, we re-use "bucket" here as the amount
                    967:         * of free space in the next rightmost block. We need to keep this
                    968:         * value for the next test so that we can expand both up and down
                    969:         * if necessary.
                    970:         */
                    971: 
                    972:        bucket = 0;
                    973:        prev_reduce = 0;
                    974: 
                    975:        if ((next = _ST_HEAP_NEXT (addr, cntl)) < q->_arena_end &&
                    976:            _ST_BLOCK_FREE (cntl = _ST_BLOCK_CONTROL (q, next)))
                    977:                bucket = cntl;  /* available adjacent words */
                    978: 
                    979:        if (bucket >= delta) {
                    980:                /*
                    981:                 * Whether we are growing or shrinking, we have enough room.
                    982:                 * Set the block's new size, and skip to the common code below
                    983:                 * which adjusts the right edge of a block by "delta" words.
                    984:                 */
                    985: 
                    986:                ASSERT (next < q->_arena_end ||
                    987:                        (next == q->_arena_end && delta < 0));
                    988: 
                    989:                _ST_BLOCK_SET_USED (q, addr, newsize);
                    990: 
                    991: 
                    992:                /*
                    993:                 * Go to the common exit sequence for in-place adjustment,
                    994:                 * after setting "prev" to be the base of the block that we
                    995:                 * will return to the user.
                    996:                 */
                    997: 
                    998:                prev = addr;
                    999: 
                   1000:                goto adjust_right;
                   1001:        }
                   1002: 
                   1003: 
                   1004:        /*
                   1005:         * Check the next leftmost block to see if we should move down. Note
                   1006:         * that we add in the free size of the right block to the calculation
                   1007:         * so that we can expand both down and up to fill space.
                   1008:         *
                   1009:         * Given that we have to copy, however, we'll drop to the bottom of
                   1010:         * the available room.
                   1011:         */
                   1012: 
                   1013:        if (_ST_BLOCK_FREE (cntl = _ST_BLOCK_CONTROL (q, prev)) &&
                   1014:            (cntl + bucket) >= delta) {
                   1015:                /*
                   1016:                 * Well, we have enough space. Now, let's make "delta" equal
                   1017:                 * to the amount by which we need to adjust the block on
                   1018:                 * the right.
                   1019:                 */
                   1020: 
                   1021:                delta -= cntl;
                   1022: 
                   1023: 
                   1024:                /*
                   1025:                 * We set the "prev" block as used now for st_reduced ().
                   1026:                 */
                   1027: 
                   1028:                _ST_BLOCK_SET_USED (q, prev, newsize);
                   1029: 
                   1030: 
                   1031:                /*
                   1032:                 * Since we are vaporising the original block at "addr", we
                   1033:                 * should deal with checking to see if it was the first block
                   1034:                 * in it's bucket, otherwise we may wind up with a dangling
                   1035:                 * pointer. For now, we just point it to the successor to
                   1036:                 * "addr", and if this this does not turn out to be correct,
                   1037:                 * the common code to move the LHS below will do the right
                   1038:                 * thing.
                   1039:                 */
                   1040: 
                   1041:                if (_ST_HEAP_FIRST (q, (bucket = _ST_HEAP_BUCKET (q, addr)))
                   1042:                    == addr)
                   1043:                        _ST_HEAP_FIRST (q, bucket) = next;
                   1044: 
                   1045:                /*
                   1046:                 * If we want to move the block down, then we should update
                   1047:                 * the heap information related to the block we are moving
                   1048:                 * into before we move into it.
                   1049:                 *
                   1050:                 * Everything relating to what happens to the right edge of
                   1051:                 * the block will be dealt with below, so all we have to do
                   1052:                 * here is determine whether to call st_reduced ().
                   1053:                 *
                   1054:                 * We don't actually call st_reduced () here, since until the
                   1055:                 * left edge has been dealt with there can be a temporary
                   1056:                 * loss of block connectivity.
                   1057:                 */
                   1058: 
                   1059:                bucket = _ST_HEAP_BUCKET (q, prev);
                   1060: 
                   1061:                if (_ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) == cntl)
                   1062:                        prev_reduce = 1;
                   1063: 
                   1064:                /*
                   1065:                 * Now, copy the original data from the block at "addr". How
                   1066:                 * much to copy? Re-fetch the block size from the control
                   1067:                 * word for now, although this may have to change to use
                   1068:                 * "oldsize" if this is changed to use a variant algorithm
                   1069:                 * where allocated blocks are not part of a list.
                   1070:                 *
                   1071:                 * How to perform the copy? Each target system will probably
                   1072:                 * have it's own routine for performing a word-aligned, word-
                   1073:                 * counted, upward-only high-speed block copy. Here, we
                   1074:                 * request the services of that routine, with a default
                   1075:                 * provided just prior to this code in case there is no
                   1076:                 * special facility for that purpose.
                   1077:                 *
                   1078:                 * Note that we perform the move now because there is no way
                   1079:                 * that the move can invalidate the block header (if any) in
                   1080:                 * the rightmost block. We know this because the size that we
                   1081:                 * are copying by is the size of the original block, and we
                   1082:                 * are moving down, so we can't write over anything after the
                   1083:                 * original block.
                   1084:                 */
                   1085: 
                   1086:                _ST_BLOCK_COPY (q, _ST_HEAP_NEXT (prev, _ST_AHDR_SIZE),
                   1087:                                _ST_HEAP_NEXT (addr, _ST_AHDR_SIZE),
                   1088:                                _ST_BLOCK_SIZE (_ST_BLOCK_CONTROL (q, addr)) - _ST_AHDR_SIZE);
                   1089: 
                   1090:                /*
                   1091:                 * Now, do the common part.
                   1092:                 */
                   1093: 
                   1094:                ASSERT (_ST_HEAP_NEXT (next, delta) ==
                   1095:                                _ST_HEAP_NEXT (prev, newsize));
                   1096: 
                   1097:                goto adjust_right;
                   1098:        }
                   1099: 
                   1100: 
                   1101:        /*
                   1102:         * We have tried to move the block more-or-less in place, and the
                   1103:         * some of the block and both neighbours does not yield enough space,
                   1104:         * so we try and realloc () the naive way, using st_alloc () and
                   1105:         * st_free (). We re-use these routines and just adjust for the fact
                   1106:         * that they measure sizes in bytes rather than duplicating the code.
                   1107:         *
                   1108:         * Please note that the "prev" returned by st_alloc () has already
                   1109:         * had the adjustment by 1 word to skip over the control block.
                   1110:         */
                   1111: 
                   1112: 
                   1113:        if ((prev = _ST_PTR2ADDR (q, st_alloc (q, _ST_WORD2BYTE (newsize))))
                   1114:            == 0)
                   1115:                return 0;
                   1116: 
                   1117: 
                   1118:        /*
                   1119:         * Do the block copy of the original contents and release them.
                   1120:         * See the discussion above on the block copier.
                   1121:         *
                   1122:         * Note that we pass the address "a" into st_free (), as "addr" is
                   1123:         * adjusted to point at the control word.
                   1124:         */
                   1125: 
                   1126:        _ST_BLOCK_COPY (q, prev, _ST_HEAP_NEXT (addr, _ST_AHDR_SIZE),
                   1127:                        _ST_BLOCK_SIZE (_ST_BLOCK_CONTROL (q, addr)) - _ST_AHDR_SIZE);
                   1128: 
                   1129:        if (st_free (q, a ST_FREE_SIZE (oldsize)) != 0) {
                   1130:                /*
                   1131:                 * What the duece! Throw the bums out on their ears!
                   1132:                 */
                   1133: 
                   1134:                ASSERT (1 == 0);
                   1135:        }
                   1136: 
                   1137: 
                   1138:        return _ST_ADDR2PTR (q, prev);
                   1139: 
                   1140: 
                   1141:        /*
                   1142:         * Perform the necessary adjustments to the index heap for the
                   1143:         * event that is happening on the right-hand side of the original
                   1144:         * block. The block boundary on the right is being moved by "delta"
                   1145:         * words from it's *original* location, ie the block at "next" is
                   1146:         * being either grown by -delta or shrunk by delta, or remaining
                   1147:         * unaffected. If next is being shrunk, then we know at this point
                   1148:         * that "next" is free. If it's being grown, then "next" could be
                   1149:         * either free or allocated, so we may have to create a new block.
                   1150:         */
                   1151: 
                   1152: adjust_right:
                   1153: 
                   1154:        /*
                   1155:         * Common case: fetch the current statistics of the "next" block and
                   1156:         * set us up so that we point at the location of the new block.
                   1157:         */
                   1158: 
                   1159:        if (next < q->_arena_end) {
                   1160: 
                   1161:                cntl = _ST_BLOCK_CONTROL (q, next);
                   1162:                bucket = _ST_HEAP_BUCKET (q, next);
                   1163: 
                   1164:                /*
                   1165:                 * We record whether or not "next" has an _ST_HEAP_FIRST ()
                   1166:                 * pointer looking at it so we can update it below depending
                   1167:                 * on how we move things around.
                   1168:                 */
                   1169: 
                   1170:                next_first = _ST_HEAP_FIRST (q, bucket) == next;
                   1171:        } else {
                   1172:                /*
                   1173:                 * Treat the end of the world as an allocated block.
                   1174:                 */
                   1175: 
                   1176:                cntl = -1;
                   1177:                next_first = bucket = 0;
                   1178:        }
                   1179: 
                   1180:        next = _ST_HEAP_NEXT (next, delta);
                   1181:        next_bucket = _ST_HEAP_BUCKET (q, next);
                   1182: 
                   1183: 
                   1184:        if (delta > 0) {                /* eat into "next", which is free */
                   1185:                /*
                   1186:                 * If we need to, make the _ST_HEAP_FIRST () pointer track
                   1187:                 * the block. Note that there is no harm in this if "next"
                   1188:                 * actually lives a few buckets along, in fact it's required
                   1189:                 * that if there's no blocks in the bucket, _ST_HEAP_FIRST ()
                   1190:                 * must point beyond the end of the bucket.
                   1191:                 */
                   1192: 
                   1193:                if (next_first)
                   1194:                        _ST_HEAP_FIRST (q, bucket) = next;
                   1195: 
                   1196:                /*
                   1197:                 * Grow up into our neighbour. This case is basically
                   1198:                 * identical to the regular allocation algorithm except that
                   1199:                 * we don't have to reserve space for a new control word.
                   1200:                 *
                   1201:                 * The comments in st_alloc () also apply here, see above.
                   1202:                 *
                   1203:                 * Note that since we know that "next" is free, we also know
                   1204:                 * that "cntl" is a positive integer and doesn't need masking.
                   1205:                 */
                   1206: 
                   1207:                if (cntl > delta) {
                   1208:                        /*
                   1209:                         * Create a free block, which may cause a call to
                   1210:                         * st_grown () if it begins in another bucket. Like
                   1211:                         * st_alloc (), we call st_grown () whether or not
                   1212:                         * it needs to propagate size information up the
                   1213:                         * index heap, because we may require that the size
                   1214:                         * of the index heap be doubled as a side-effect of
                   1215:                         * the overflow into the next bucket.
                   1216:                         */
                   1217: 
                   1218:                        _ST_BLOCK_SET_FREE (q, next, cntl - delta);
                   1219: 
                   1220:                        if (next_bucket > bucket)
                   1221:                                st_grown (q, next, next_bucket);
                   1222:                } else {
                   1223:                        /*
                   1224:                         * In this case, "next" has been completely eaten
                   1225:                         * up, so we just let the common code below deal with
                   1226:                         * shrinking the "biggest free".
                   1227:                         *
                   1228:                         * I make lots of gratuitous assertions, don't I?
                   1229:                         */
                   1230: 
                   1231:                        ASSERT (cntl == delta);
                   1232:                }
                   1233: 
                   1234: 
                   1235:                /*
                   1236:                 * Regardless of whether we have created a new block, we
                   1237:                 * should see whether we should adjust the "largest free"
                   1238:                 * for the bucket, according to the usual rules.
                   1239:                 */
                   1240: 
                   1241:                if (_ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) == cntl)
                   1242:                        st_reduced (q, bucket);
                   1243:        } else if (delta < 0) {         /* "-delta" free words below "next" */
                   1244:                /*
                   1245:                 * This code is the inverse of the above, except that we
                   1246:                 * may not be affecting the actual "next" block if it is
                   1247:                 * in use.
                   1248:                 *
                   1249:                 * Note that in this scenario, "next_bucket" has the inverse
                   1250:                 * of the normal relation to "bucket", since it may be below
                   1251:                 * "bucket".
                   1252:                 */
                   1253: 
                   1254:                _ST_BLOCK_SET_FREE (q, next, (_ST_BLOCK_FREE (cntl) ? cntl : 0)
                   1255:                                                - delta);
                   1256: 
                   1257:                if (next_bucket < bucket) {
                   1258: 
                   1259:                        _ST_HEAP_FIRST (q, bucket) =
                   1260:                                _ST_HEAP_NEXT (next, _ST_BLOCK_CONTROL (q, next));
                   1261: 
                   1262:                        /*
                   1263:                         * We don't want to call st_reduced () if the original
                   1264:                         * "next" block wasn't free. It won't hurt things,
                   1265:                         * but it wastes time. However, since "cntl" is
                   1266:                         * negative in that case, the test below will always
                   1267:                         * fail in that situation.
                   1268:                         */
                   1269: 
                   1270:                        if (_ST_HEAP_BIGGEST (q, bucket + q->_buckets_inuse) == cntl)
                   1271:                                st_reduced (q, bucket);
                   1272:                }
                   1273: 
                   1274: 
                   1275:                if (_ST_BLOCK_CONTROL (q, next) > _ST_HEAP_BIGGEST (q, next_bucket + q->_buckets_inuse))
                   1276:                        st_grown (q, next, next_bucket);
                   1277:        }
                   1278: 
                   1279:        if (prev_reduce != 0)
                   1280:                st_reduced (q, _ST_HEAP_BUCKET (q, prev));
                   1281: 
                   1282: 
                   1283:        return _ST_ADDR2PTR (q, _ST_HEAP_NEXT (prev, 1));
                   1284: }
                   1285: 
                   1286: 
                   1287: /*
                   1288:  * Initialise an arena.
                   1289:  */
                   1290: 
                   1291: #if    __USE_PROTO__
                   1292: void (st_init) (_ST_HEAP_CONTROL_P q)
                   1293: #else
                   1294: void
                   1295: st_init __ARGS ((q))
                   1296: _ST_HEAP_CONTROL_P     q;
                   1297: #endif
                   1298: {
                   1299:        q->_heap_error = NULL;
                   1300:        q->_buckets_inuse = 1;
                   1301:        q->_words_per_bucket = (q->_arena_size + q->_buckets_maximum - 1)
                   1302:                                        / q->_buckets_maximum;
                   1303: 
                   1304:        _ST_HEAP_BIGGEST (q, 1) = q->_arena_size;
                   1305: 
                   1306:        _ST_HEAP_FIRST (q, 0) = q->_arena_base;
                   1307: 
                   1308:        _ST_BLOCK_SET_FREE (q, q->_arena_base, q->_arena_size);
                   1309: 
                   1310:        /*
                   1311:         * Create dummy sentinel block.
                   1312:         */
                   1313: 
                   1314:        st_alloc (q, 0);
                   1315: 
                   1316:        return;
                   1317: }
                   1318: 
                   1319: 
                   1320: /*
                   1321:  * As above, but a full construction.
                   1322:  */
                   1323: 
                   1324: #if    __USE_PROTO__
                   1325: void (st_ctor) (_ST_HEAP_CONTROL_P q, int segs, size_t arensize,
                   1326:                _ST_ADDR_T arenabase)
                   1327: #else
                   1328: void
                   1329: st_ctor __ARGS ((q, segs, arensize, arenabase))
                   1330: _ST_HEAP_CONTROL_P     q;
                   1331: int                    segs;
                   1332: size_t                 arensize;
                   1333: _ST_ADDR_T             arenabase;
                   1334: #endif
                   1335: {
                   1336:        q->_buckets_maximum = segs;
                   1337:        q->_arena_size = arensize;
                   1338:        q->_arena_base = (_ST_ADDR_T) arenabase;
                   1339:        q->_arena_end = _ST_HEAP_NEXT (arenabase, arensize);
                   1340: 
                   1341:        q->_bucket_biggest = (_ST_WORD_T *) (q + 1);
                   1342:        q->_bucket_first = (_ST_ADDR_T *) (q->_bucket_biggest + segs * 2);
                   1343: 
                   1344:        /*
                   1345:         * Perform regular initialisation.
                   1346:         */
                   1347: 
                   1348:        st_init (q);
                   1349: }
                   1350: 
                   1351: 
                   1352: /*
                   1353:  * Return a size suitable for requesting the largest currently available
                   1354:  * block of memory.
                   1355:  *
                   1356:  * Thanks to the way the index heap is constructed, this is in fact
                   1357:  * trivially implementable.
                   1358:  */
                   1359: 
                   1360: #if    __USE_PROTO__
                   1361: size_t (st_maxavail) (_ST_HEAP_CONTROL_P q)
                   1362: #else
                   1363: size_t
                   1364: st_maxavail __ARGS ((q))
                   1365: _ST_HEAP_CONTROL_P     q;
                   1366: #endif
                   1367: {
                   1368:        return _ST_WORD2BYTE (_ST_HEAP_BIGGEST (q, 0));
                   1369: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.