// cl_malloca(), cl_freea().

// General includes.
#include "cl_sysdep.h"

// Specification.
#include "cl_malloca.h"


// Implementation.

#include "cl_io.h"
#include "cl_abort.h"
#include "cl_offsetof.h"


// The allocated memory blocks form a linked list.
typedef struct malloca_header
               { struct malloca_header * next;
                 long usable_memory[1]; // "long" guarantees alignment
               }
        malloca_header;

// Linked list of memory blocks, the youngest at the beginning, the oldest
// comes last.
malloca_header* cl_malloca_list = NULL;

// cl_malloca(size) returns a memory block of size `size'.
void* cl_malloca (size_t size)
    { var malloca_header* ptr = (malloca_header*)malloc(offsetofa(malloca_header,usable_memory) + size);
      if (!(ptr == NULL))
        { ptr->next = cl_malloca_list;
          cl_malloca_list = ptr;
          return &ptr->usable_memory;
        }
        else
        { fprint(cl_stderr, "\n*** - Virtual memory exhausted.\n");
          cl_abort();
    }   }

// cl_freea(ptr) returns a memory block and all the younger ones.
void cl_freea (void* address)
    { var malloca_header* ptr = (malloca_header*)
        ((uintP)address - offsetofa(malloca_header,usable_memory));
      var malloca_header* p = cl_malloca_list;
      loop
        { var malloca_header* n = p->next;
          free(p);
          if (!(p == ptr))
            { p = n; }
            else
            { cl_malloca_list = n; break; }
        }
    }

// cl_malloca_setstack(ptr);     frees all blocks allocated since ptr.
void cl_malloca_setstack (void* ptr)
    { var malloca_header* p = cl_malloca_list;
      while (p != (malloca_header*)ptr)
        { var malloca_header* n = p->next;
          free(p);
          p = n;
        }
      cl_malloca_list = p;
    }

