Memory 26.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
15 16 17 18 19 20 21 22 23 24 25 26 27
/***************************************************/
/* File   : Memory.c                               */
/*                                                 */
/* Purpose: Granularity-controlled memory handling */
/*          functions for the browser, designed as */
/*          transparent replacements for malloc,   */
/*          calloc, realloc and free (amongst      */
/*          other memory handlers).                */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 29-Nov-96: Created.                    */
/***************************************************/
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

#include <stdlib.h>
#include <string.h>

#include "kernel.h"
#include "flex.h"

#include "wimp.h"
#include "event.h"

#include "svcprint.h"
#include "Global.h"
#include "Utils.h"

#include "Memory.h"

/* Local definitions */

#define MallocGranularity    256
#define FlexGranularityShift 12

#define Flex_256_Point       8192
#define Flex_4K_Point        16384

/*************************************************/
/* memory_malloc()                               */
/*                                               */
/* Works as malloc, but only allocates data in   */
/* chunks of MallocGranularity (see Memory.h)    */
/* bytes. It always adds 4 bytes to the amount   */
/* requested after rounding to the block size,   */
/* and stores the actual exact requested size    */
/* in these bytes after mallocing the block. The */
/* pointer that is returned is 4 bytes on from   */
/* the actual size store so that the caller      */
/* can treat the routines exactly like malloc.   */
/* This does mean that a mixture of these        */
65 66
/* routines and malloc etc. must *NEVER BE USED* */
/* on the same blocks of data!                   */
67
/*                                               */
68
/* Parameters: As for malloc.                    */
69
/*                                               */
70
/* Returns:    As for malloc, in effect.         */
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
/*************************************************/

void * memory_malloc(size_t size)
{
  size_t s;
  void * r;

  /* Get the rounded up size of the block to allocate, plus */
  /* the four bytes for the requested size store.           */

  s = (((size / MallocGranularity) + 1) * MallocGranularity) + 4;

  /* Allocate the block */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("memory_malloc: %d bytes asked for, giving %d\n",size,s);
  #endif

  r = malloc(s);

  #ifdef TRACE
    if (tl & (1u<<12))
    {
      if (r) Printf("memory_malloc: Success, block is %p\n",r);
      else Printf("memory_malloc: Failure\n");
    }
  #endif

  /* Assuming the allocation was successful, return the  */
  /* pointer to the block plus 4 to skip the size store, */
  /* after storing the exact requested size in those     */
  /* four bytes. Else return NULL.                       */

  if (r)
  {
    #ifdef TRACE
      malloccount += s;
108
      if (tl & (1u<<13)) Printf("** malloccount (memory_malloc): \0211%d\0217\n",malloccount);
109 110
    #endif

111
    *((int *) r) = size;
112 113 114 115 116 117 118 119 120
    return ((void *) (((int) r) + 4));
  }
  else return NULL;
}

/*************************************************/
/* memory_calloc()                               */
/*                                               */
/* Works as calloc, but with the added design    */
121
/* rationale of memory_malloc.                   */
122
/*                                               */
123
/* Parameters: As for calloc.                    */
124
/*                                               */
125
/* Returns:    As for calloc, in effect.         */
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/*************************************************/

void * memory_calloc(size_t n, size_t size)
{
  size_t s;
  void * r;

  /* On entry, 'n' holds a number of objects, and 'size' */
  /* holds the size of each object. Set 'size' to now    */
  /* hold the total amount required for all the objects. */

  size = n * size;

  /* Get the rounded up size of the block to allocate, plus */
  /* the four bytes for the requested size store.           */

  s = (((size / MallocGranularity) + 1) * MallocGranularity) + 4;

  /* Allocate the block */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("memory_calloc: %d bytes asked for, giving %d\n",size,s);
  #endif

  r = malloc(s);

  #ifdef TRACE
    if (tl & (1u<<12))
    {
      if (r) Printf("memory_calloc: Success, block is %p\n",r);
      else Printf("memory_calloc: Failure\n");
    }
  #endif

  /* Assuming the allocation was successful, return the  */
  /* pointer to the block plus 4 to skip the size store, */
  /* after storing the exact requested size in those     */
  /* four bytes and zeroing the rest of the block.  Else */
  /* return NULL.                                        */

  if (r)
  {
    #ifdef TRACE
      malloccount += s;
170
      if (tl & (1u<<13)) Printf("** malloccount (memory_calloc): \0211%d\0217\n",malloccount);
171 172
    #endif

173
    *((int *) r) = size;
174 175 176 177 178 179 180 181 182 183
    memset((void *) (((int) r) + 4), 0, s - 4);
    return ((void *) (((int) r) + 4));
  }
  else return NULL;
}

/*************************************************/
/* memory_realloc()                              */
/*                                               */
/* Works as realloc, but with the added design   */
184 185 186 187
/* rationale of memory_malloc. In this case,     */
/* should the rounding up of the requested sizes */
/* mean that the requested size this time fits   */
/* within the actual amount allocated, no        */
188
/* reallocation will be done, thus speeding      */
189 190
/* things up a bit. It also helps cut down on    */
/* fragmentation. This applies both for growing  */
191 192
/* and shrinking the size of a block.            */
/*                                               */
193
/* Parameters: As for realloc.                   */
194
/*                                               */
195
/* Returns:    As for realloc, in effect.        */
196 197 198 199 200 201 202 203 204
/*************************************************/

void * memory_realloc(void * ptr, size_t size)
{
  size_t s, c;
  void * r;

  /* Work out what the current size must be, based on the   */
  /* first four bytes of the block; these contain the exact */
205
  /* size last passed to memory_alloc or memory_calloc.     */
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

  r = (void *) (((int) ptr) - 4); /* r holds the real block pointer now */
  s = *((int *) r);               /* s now holds the last requested size */

  c = (((s / MallocGranularity) + 1) * MallocGranularity) + 4;

  /* Get the rounded up new size plus 4 bytes of size store */

  s = (((size / MallocGranularity) + 1) * MallocGranularity) + 4;

  /* If these two sizes are different, we need to do a realloc. */
  /* If the realloc fails we return NULL anyway, so there's no  */
  /* problem with doing 'pointer = realloc (same pointer, ...)' */

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("memory_realloc: Block is %p.\n"
                          "                Will proceed if old size %d <> new size %d\n",r,c,s);
  #endif

  if (s != c)
  {
    #ifdef TRACE
      if (tl & (1u<<12)) Printf("memory_realloc: %d bytes asked for, giving %d\n",size,s);
    #endif

    r = realloc(r,s);

    #ifdef TRACE
      if (tl & (1u<<12))
      {
        if (r) Printf("memory_realloc: Success\n");
        else Printf("memory_realloc: Failure\n");
      }
    #endif

    /* If the realloc succeeded write the new exact requested size */
    /* into the first word and return the pointer plus 4, as in    */
243
    /* memory_malloc; else return NULL.                            */
244 245 246 247 248

    if (r)
    {
      #ifdef TRACE
        malloccount += (s - c);
249
        if (tl & (1u<<13)) Printf("** malloccount (memory_realloc): \0211%d\0217\n",malloccount);
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
      #endif

      * ((int *) r) = size;
      return ((void *) (((int) r) + 4));
    }
    else return NULL;
  }

  /* If the two sizes were the same, no realloc is needed so */
  /* just give the original pointer back again.              */

  else return ptr;
}

/*************************************************/
/* memory_alloc_and_set()                        */
/*                                               */
267 268
/* mallocs an area of memory and sets it to      */
/* contain a specific character using memset.    */
269 270 271
/* Does *NOT* given an error if the malloc fails */
/* - this is left to the caller to handle.       */
/*                                               */
272 273 274 275 276
/* Parameters: The amount of memory to allocate, */
/*             in bytes;                         */
/*                                               */
/*             The value to fill the block with  */
/*             (from 0 to 255).                  */
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
/*                                               */
/* Returns:    Pointer to the claimed memory, or */
/*             NULL if the claim failed          */
/*************************************************/

void * memory_alloc_and_set(size_t s, int f)
{
  void * p;

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("memory_alloc_and_set: malloc %d, initialise to %d\n",s,f);
  #endif

  p = malloc(s);
  if (p) memset(p,f,s);

  #ifdef TRACE
    if (tl & (1u<<12))
    {
      if (p) Printf("memory_alloc_and_set: Success, block is %p\n",p);
      else   Printf("memory_alloc_and_set: Failure\n");
    }
  #endif

  return p;
}

/*************************************************/
/* memory_free()                                 */
/*                                               */
/* Works as free() but subtracts four from the   */
/* passed pointer first, assuming that the block */
309
/* was originally allocated with memory_alloc.   */
310 311 312
/* The contents of the pointer *ARE NOT ZEROED*  */
/* (unlike with, say, flex_free).                */
/*                                               */
313
/* Parameters: As for free.                      */
314 315 316 317 318 319 320
/*************************************************/

void memory_free(void * ptr)
{
  #ifdef TRACE
    if (tl & (1u<<12)) Printf("memory_free: Freeing block %p\n",(void *) (((int) ptr) - 4));
    malloccount -= memory_granular_size(ptr);
321
    if (tl & (1u<<13)) Printf("** malloccount (memory_free): \0212%d\0217\n",malloccount);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
  #endif

  free ((void *) (((int) ptr) - 4));
}

/*************************************************/
/* memory_size()                                 */
/*                                               */
/* Returns the size of a memory_malloced block   */
/* of memory. There's no alloc-style equivalent. */
/* The amount is the originally requested size,  */
/* and not the actual 'granular' size.           */
/*                                               */
/* Parameters: The pointer to the block, as      */
/*             returned by memory_malloc and     */
/*             memory_realloc (for example).     */
338 339 340 341 342
/*                                               */
/* Returns:    The size in bytes as an int. The  */
/*             function has no way of knowing if */
/*             the pointer was sensible, but     */
/*             will return zero if it's null.    */
343 344 345 346 347 348 349 350 351 352 353 354 355 356
/*************************************************/

int memory_size(void * ptr)
{
  void * r;

  r = (void *) (((int) ptr) - 4); /* r holds the real block pointer now */

  return *((int *) r); /* Return the size */
}

/*************************************************/
/* memory_granular_size()                        */
/*                                               */
357 358
/* As memory_size, but returns the granular size */
/* (i.e. the real amount allocated).             */
359
/*                                               */
360
/* Parameters: As memory_size.                   */
361
/*                                               */
362 363
/* Returns:    As memory_size, but the real size */
/*             rather than the requested size.   */
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
/*************************************************/

int memory_granular_size(void * ptr)
{
  void * r;
  int    s;

  r = (void *) (((int) ptr) - 4); /* r holds the real block pointer now */
  s = *((int *) r);               /* s now holds the last requested size */

  /* Get the rounded up new size plus 4 bytes of size store */

  return (((s / MallocGranularity) + 1) * MallocGranularity) + 4; /* Return the granular size */
}

/*************************************************/
/* memory_set_chunk_size()                       */
/*                                               */
/* Sets the size of a particular block of memory */
/* identified by int chunk, where chunk is:      */
/*                                               */
/* 1:  CK_FURL (fetching URL)                    */
/* 2:  CK_DURL (display URL)                     */
/* 3:  CK_CHIL (frames/children array)           */
/* 4:  CK_NAME (window name for targetted links) */
/* 5:  CK_LINE (line structures array)           */
/* 6:  CK_LDAT (chunk structures array)          */
/* 7:  CK_FWID (frame widths array)              */
/* 8:  CK_FHEI (frame heights array)             */
/* 9:  CK_LINV (lines, variable granularity)     */
/* 10: CK_LDAV (chunks, variable granularity)    */
/* 11: CK_STAT (status_content array)            */
396
/* 12: CK_OBJB (OBJECT, APPLET, EMBED structs)   */
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             for which the memory is to be     */
/*             allocated;                        */
/*                                               */
/*             Pointer to a reformat_cell struct */
/*             in which some allocations will be */
/*             made (e.g. CK_LINE, CK_LINV);     */
/*                                               */
/*             The chunk identifier (see above); */
/*                                               */
/*             The amount of memory to allocate, */
/*             in bytes.                         */
/*                                               */
/* Assumes:    The reformat_cell pointer may be  */
/*             NULL, in which case the 'cell'    */
/*             field of the browser_data struct  */
/*             will be used.                     */
/*************************************************/

_kernel_oserror * memory_set_chunk_size(browser_data * b, reformat_cell * cell, int chunk, int size)
{
  int    oldsize = 0;
  int    success = 1;
  void * pointer = NULL;

  if (!cell) cell = b->cell;

  #ifdef TRACE
    if (tl & (1u<<7)) Printf("memory_set_chunk_size: Data * %p, cell * %p, chunk %d, size %d\n",b,cell,chunk,size);
  #endif

  /* Go through all the possible chunks, dealing with each case individually. */
  /* Though some of the code will be duplicated, this individual handling     */
  /* allows greater flexibility in future - various blocks could have the way */
  /* they are dealt with changed radically without too much trouble at this   */
  /* end of things.                                                           */

  switch (chunk)
  {
437
    case CK_OBJB:
438 439 440 441 442 443
    {
      /* Set oldsize to the block size of the requested bit of memory,    */
      /* giving an immediate exit error if the chunk ID isn't recognised. */
      /* Note that oldsize will be zero if the block hasn't been          */
      /* allocated already.                                               */

444
      if (b->odata) oldsize = flex_size((flex_ptr) &b->odata);
445 446 447 448
      else if (size == 0) return NULL;

      /* If the requested size is different from the current size... */

449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
      if (oldsize != size)
      {
        if (!size)
        {
          #ifdef TRACE
            flexcount -= oldsize;
            if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
          #endif

          flex_free((flex_ptr) &b->odata); /* If size = 0, free the block */
        }
        else
        {
          /* If the block exists, resize it. Otherwise allocate it anew. */

          if (oldsize) success = flex_extend((flex_ptr) &b->odata, size);
          else success = flex_alloc((flex_ptr) &(b->odata), size);

          #ifdef TRACE
            if (success)
            {
              flexcount += size - oldsize;
              if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
            }
          #endif
        }
      }

      break;
    }
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820

    case CK_FURL:
    {
      if (!size)
      {
        /* If setting the size to zero, want to free the block */

        if (b->urlfdata)
        {
          memory_free((void *) b->urlfdata);
          b->urlfdata = NULL;
        }
      }
      else
      {
        /* If there is a non-null pointer, reallocate the block. */
        /* Store the new pointer in 'pointer', knowing this may  */
        /* be NULL if the realloc failed. Otherwise, malloc a    */
        /* new block again storing the pointer in 'pointer'.     */

        if (b->urlfdata) pointer = memory_realloc((void *) b->urlfdata, size);
        else pointer = memory_malloc(size);

        /* If 'pointer' isn't null, set b->urlfdata to point to      */
        /* the new block. If the pointer *is* null, then b->urlfdata */
        /* is left alone. This is what we want - if it pointed to    */
        /* an old block that was realloced, we still want that       */
        /* pointer as the old size block is still malloced; else, it */
        /* will have the default value of NULL.                      */

        if (pointer) b->urlfdata = (char *) pointer;

        /* Failure is flagged through success = 0 (it's default value is 1). */

        else success = 0;
      }

      break;
    }

    /* The rest of the code is basically the same as the stuff above at present. */

    case CK_DURL:
    {
      if (!size)
      {
        if (b->urlddata)
        {
          memory_free((void *) b->urlddata);
          b->urlddata = NULL;
        }
      }
      else
      {
        if (b->urlddata) pointer = memory_realloc((void *) b->urlddata, size);
        else pointer = memory_malloc(size);

        if (pointer) b->urlddata = (char *) pointer;
        else success = 0;
      }

      break;
    }

    case CK_NAME:
    {
      if (!size)
      {
        if (b->window_name)
        {
          free((void *) b->window_name);
          b->window_name = NULL;
        }
      }
      else
      {
        if (b->window_name) pointer = realloc((void *) b->window_name, size);
        else pointer = malloc(size);

        if (pointer) b->window_name = (char *) pointer;
        else success = 0;
      }

      break;
    }

    /* Small arrays - not allocated in any granular fashion, and other */
    /* generally necessary flags/counters allow the array size to be   */
    /* calculated easily. Hence use malloc etc. directly.              */

    case CK_CHIL:
    {
      if (!size)
      {
        if (b->children)
        {
          free((void *) b->children);
          b->children = NULL;
        }
      }
      else
      {
        if (b->children) pointer = realloc((void *) b->children, size);
        else pointer = malloc(size);

        if (pointer) b->children = (browser_data **) pointer;
        else success = 0;
      }

      break;
    }

    case CK_FWID:
    {
      if (!size)
      {
        if (b->frame_widths)
        {
          free((void *) b->frame_widths);
          b->frame_widths = NULL;
        }
      }
      else
      {
        if (b->frame_widths) pointer = realloc((void *) b->frame_widths, size);
        else pointer = malloc(size);

        if (pointer) b->frame_widths = (int *) pointer;
        else success = 0;
      }

      break;
    }

    case CK_FHEI:
    {
      if (!size)
      {
        if (b->frame_heights)
        {
          free((void *) b->frame_heights);
          b->frame_heights = NULL;
        }
      }
      else
      {
        if (b->frame_heights) pointer = realloc((void *) b->frame_heights, size);
        else pointer = malloc(size);

        if (pointer) b->frame_heights = (int *) pointer;
        else success = 0;
      }

      break;
    }

    /* Small block, likely to change size fairly often - so use */
    /* memory_malloc etc. for the granularity of allocation.    */

    case CK_STAT:
    {
      if (!size)
      {
        if (b->status_contents)
        {
          memory_free((void *) b->status_contents);
          b->status_contents = NULL;
        }
      }
      else
      {
        if (b->status_contents) pointer = memory_realloc((void *) b->status_contents, size);
        else pointer = memory_malloc(size);

        if (pointer) b->status_contents = (browser_data **) pointer;
        else success = 0;
      }

      break;
    }

    /* The line and line data code only allocates in blocks of 4096 bytes - */
    /* the shifting by FlexGranularityShift (see Memory.h) bits handles     */
    /* effectively fast division / multiplication by this size to see if    */
    /* the requested allocation exceeds the current block size.             */

    case CK_LINE:
    {
      if (cell->ldata) oldsize = flex_size((flex_ptr) &cell->ldata);
      else if (size == 0) return NULL;

      if (((oldsize >> FlexGranularityShift) != ((size >> FlexGranularityShift) + 1)) || !size)
      {
        if (!size)
        {
          #ifdef TRACE
            flexcount -= oldsize;
            if (tl & (1u<<14)) Printf("**   line flexcount: %d\n",flexcount);
          #endif

          flex_free((flex_ptr) &cell->ldata);
        }
        else
        {
          if (oldsize) success = flex_extend((flex_ptr) &cell->ldata, ((size >> FlexGranularityShift) + 1) << FlexGranularityShift);
          else success = flex_alloc((flex_ptr) &cell->ldata, ((size >> FlexGranularityShift) + 1) << FlexGranularityShift);

          #ifdef TRACE
            if (success)
            {
              flexcount += (flex_size((flex_ptr) &cell->ldata) - oldsize);
              if (tl & (1u<<14)) Printf("**   line flexcount: %d\n",flexcount);
            }
          #endif
        }
      }

      break;
    }

    case CK_LDAT:
    {
      if (cell->cdata) oldsize = flex_size((flex_ptr) &cell->cdata);
      else if (size == 0) return NULL;

      if (((oldsize >> FlexGranularityShift) != ((size >> FlexGranularityShift) + 1)) || !size)
      {
        if (!size)
        {
          #ifdef TRACE
            flexcount -= oldsize;
            if (tl & (1u<<14)) Printf("**   ldat flexcount: %d\n",flexcount);
          #endif

          flex_free((flex_ptr) &cell->cdata);
        }
        else
        {
          if (oldsize) success = flex_extend((flex_ptr) &cell->cdata, ((size >> FlexGranularityShift) + 1) << FlexGranularityShift);
          else success = flex_alloc((flex_ptr) &cell->cdata, ((size >> FlexGranularityShift) + 1) << FlexGranularityShift);

          #ifdef TRACE
            if (success)
            {
              flexcount += (flex_size((flex_ptr) &cell->cdata) - oldsize);
              if (tl & (1u<<14)) Printf("**   ldat flexcount: %d\n",flexcount);
            }
          #endif
        }
      }

      break;
    }

    /* Variable granularity versions of the above static granularity routines */

    case CK_LINV:
    {
      int gshift = 0;

      if (cell->ldata) oldsize = flex_size((flex_ptr) &cell->ldata);
      else if (size == 0) return NULL;

      if (oldsize > Flex_256_Point)  gshift = 8;  /* 256 byte granularity */
      if (oldsize > Flex_4K_Point) gshift = 12; /* 4K granularity */

      if (((oldsize >> gshift) != ((size >> gshift) + 1)) || !size)
      {
        if (!size)
        {
          #ifdef TRACE
            flexcount -= oldsize;
            if (tl & (1u<<14)) Printf("**   line flexcount: %d\n",flexcount);
          #endif

          flex_free((flex_ptr) &cell->ldata);
        }
        else
        {
          if (oldsize) success = flex_extend((flex_ptr) &cell->ldata, ((size >> gshift) + 1) << gshift);
          else success = flex_alloc((flex_ptr) &cell->ldata, ((size >> gshift) + 1) << gshift);

          #ifdef TRACE
            if (success)
            {
              flexcount += (flex_size((flex_ptr) &cell->ldata) - oldsize);
              if (tl & (1u<<14)) Printf("**   line flexcount: %d\n",flexcount);
            }
          #endif
        }
      }

      break;
    }

    case CK_LDAV:
    {
      int gshift = 0;

      if (cell->cdata) oldsize = flex_size((flex_ptr) &cell->cdata);
      else if (size == 0) return NULL;

      if (oldsize > Flex_256_Point)  gshift = 8;  /* 256 byte granularity */
      if (oldsize > Flex_4K_Point) gshift = 12; /* 4K granularity */

      if (((oldsize >> gshift) != ((size >> gshift) + 1)) || !size)
      {
        if (!size)
        {
          #ifdef TRACE
            flexcount -= oldsize;
            if (tl & (1u<<14)) Printf("**   ldat flexcount: %d\n",flexcount);
          #endif

          flex_free((flex_ptr) &cell->cdata);
        }
        else
        {
          if (oldsize) success = flex_extend((flex_ptr) &cell->cdata, ((size >> gshift) + 1) << gshift);
          else success = flex_alloc((flex_ptr) &cell->cdata, ((size >> gshift) + 1) << gshift);

          #ifdef TRACE
            if (success)
            {
              flexcount += (flex_size((flex_ptr) &cell->cdata) - oldsize);
              if (tl & (1u<<14)) Printf("**   ldat flexcount: %d\n",flexcount);
            }
          #endif
        }
      }

      break;
    }

    /* If we reach this stage, something is very wrong - the chunk ID */
    /* hasn't been recognised. This is a fairly good reason to panic  */
    /* and Get Out Now.                                               */

    default:
    {
      erb.errnum = 0;
      StrNCpy0(erb.errmess,
821
               lookup_token("STCUnkwn:Serious internal error - Unknown chunk ID in memory_set_chunk_size; must exit immediately.",
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
                            0,
                            0));
      show_error(&erb);

      break;
    }
  }

  /* If success is 1 at this stage, exit with no error. This will */
  /* be the case if no (re)allocation was needed as the old and   */
  /* requested sizes were the same, as success was initialised to */
  /* a value of 1.                                                */

  #ifdef TRACE
    if (tl & (1u<<7))
    {
      if (success) Printf("memory_set_chunk_size: Successful\n");
      else Printf("memory_set_chunk_size: Exitting with an error\n");
    }
  #endif

  if (success) return NULL;

  /* The allocation failed, so must have run out of memory or something... */

847
  return make_no_cont_memory_error(10);
848
}