|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.