libc: replace malloc() with a classic K&R style malloc algorithm
authorAlan Cox <alan@linux.intel.com>
Fri, 17 Jun 2016 11:56:07 +0000 (12:56 +0100)
committerAlan Cox <alan@linux.intel.com>
Fri, 17 Jun 2016 11:56:07 +0000 (12:56 +0100)
Shorter, simpler and neater. Hopefully correct (or it'll be fun debugging all the
app crashes ;-)). realloc would benefit from being smart about using the adjacent
buffers to grow rather than the dumb free/alloc approach.

Alan

Library/libs/calloc.c
Library/libs/error.c
Library/libs/free.c
Library/libs/malloc-l.h [deleted file]
Library/libs/malloc.c
Library/libs/realloc.c

index 05bc1ba..9d4d291 100644 (file)
@@ -1,19 +1,20 @@
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>\r
- * This file is part of the Linux-8086 C library and is distributed\r
- * under the GNU Library General Public License.\r
- *\r
- * This is a combined alloca/malloc package. It uses a classic algorithm\r
- * and so may be seen to be quite slow compared to more modern routines\r
- * with 'nasty' distributions.\r
- */  \r
-    \r
-#include "malloc-l.h"\r
-\r
-void *calloc(size_t elm, size_t sz) \r
+/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
+ * This file is part of the Linux-8086 C library and is distributed
+ * under the GNU Library General Public License.
+ *
+ * Kept from the old malloc package.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *calloc(size_t elm, size_t sz)
 {
-       register size_t v = elm * sz;
-       register void *ptr = malloc(v);
-       \r
+       /* FIXME: a modern calloc should probably trap overflows and NULL */
+       size_t v = elm * sz;
+       void *ptr = malloc(v);
+
        if (ptr)
                memset(ptr, 0, v);
        return ptr;
index e3281f3..a71a25a 100644 (file)
 #include <paths.h>
 #include <errno.h>
 #include <fcntl.h>
-#include "malloc-l.h"
 
 static uint8_t *__sys_errlist;
 static uint16_t *__sys_errptr;
 static int __sys_nerr;
 static char retbuf[80];
 
+#define ALIGNMENT      16
+
+#define ALIGNUP(s) (((s) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
+
 static void _load_errlist(void)
 {
        struct stat st;
index 021868a..51b7e80 100644 (file)
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
- * This file is part of the Linux-8086 C library and is distributed
- * under the GNU Library General Public License.
- *
- * This is a combined alloca/malloc package. It uses a classic algorithm
- * and so may be seen to be quite slow compared to more modern routines
- * with 'nasty' distributions.
+/*
+ *     This is an ANSI C version of the classic K & R memory allocator. The only
+ *     real difference here is that we handle large allocations and signs correctly
+ *     which the original didn't do portably. Specifically we
+ *     - correctly handle signed sbrk when the largest allocation allowed is
+ *       unsigned
+ *     - catch the case of a malloc close to the full size_t overflowing in the
+ *       nblock computation.
  */
 
-#include "malloc-l.h"
-
-/* Start the alloca with just the dumb version of malloc */
-void *(*__alloca_alloc) __P((size_t)) = __mini_malloc;
-
-/* the free list is a single list of free blocks. __freed_list points to
-   the highest block (highest address) and each block points to the lower
-   block (lower address). last block points to 0 (initial value of
-   _freed_list)
-*/
-mem *__freed_list = 0;
-
-#ifdef VERBOSE
-/* NB: Careful here, stdio may use malloc - so we can't */
-#include <unistd.h>
 #include <stdlib.h>
-#include <string.h>
-static void pstr __P((char *));
-static void phex __P((unsigned));
-static void noise __P((char *, mem *));
-static void pstr(char *str)
-{
-       write(2, str, strlen(str));
-} static void phex(unsigned val)
-{
-       char buf[8];
-       strcpy(buf, "000");
-       ltoa((long) val, buf + 3, 16);
-       pstr(buf + strlen(buf + 4));
-} void __noise(char *y, mem * x)
-{
-       pstr("Malloc ");
-       phex((unsigned) x);
-       pstr(" sz ");
-       phex(x ? (unsigned) m_size(x) : 0);
-       pstr(" nxt ");
-       phex(x ? (unsigned) m_next(x) : 0);
-       pstr(" is ");
-       pstr(y);
-       pstr("\n");
-}
-#endif                         /*  */
-void free(void *ptr)
-{
-       register mem *top, *chk = (mem *) ptr;
-       if (chk == 0)
-               return;         /* free(NULL) - be nice */
-       chk--;
-      try_this:;
-       top = (mem *) sbrk(0);
-       if (m_add(chk, m_size(chk)) >= top) {
-               noise("FREE brk", chk);
-               brk((void *) ((uchar *) top - m_size(chk)));
-
-               /* Adding this code allow free to release blocks in any order;
-                * they can still only be allocated from the top of the heap
-                * tho.
-                */
-#ifdef __MINI_MALLOC__
-                /* FIXME: void * cast appears to be a cc65 bug */
-               if (__alloca_alloc == (void *)__mini_malloc && __freed_list) {
-                       chk = __freed_list;
-                       __freed_list = m_next(__freed_list);
-                       goto try_this;
-               }
-#endif                         /*  */
-       }
-
-       else {                  /* Nope, not sure where this goes, leave it for malloc to deal with */
-
-#ifdef __MINI_MALLOC__
-               /* check if block is already on free list.
-                  if it is, return without doing nothing */
-               top = __freed_list;
-               while (top) {
-                       if (top == chk)
-                               return;
-                       top = m_next(top);
-               }
-
-               /* else add it to free list */
-               if (!__freed_list || chk > __freed_list) {
-
-                       /* null free list or block above free list */
-                       m_next(chk) = __freed_list;
-                       __freed_list = chk;
-               }
-
-               else {
-
-                       /* insert block in free list, ordered by address */
-                       register mem *prev = __freed_list;
-                       top = __freed_list;
-                       while (top && top > chk) {
-                               prev = top;
-                               top = m_next(top);
-                       }
-                       m_next(chk) = top;
-                       m_next(prev) = chk;
-               }
-
-#else                          /*  */
-               m_next(chk) = __freed_list;
-               __freed_list = chk;
-
-#endif                         /*  */
-               noise("ADD LIST", chk);
-       }
-}
+#include <stdint.h>
+#include <unistd.h>
+#include "malloc.h"
 
-void *__mini_malloc(size_t size)
+void free(void *ptr)
 {
-       register mem *ptr;
-       register unsigned int sz;
-
-       /* First time round this _might_ be odd, But we won't do that! */
-#if 0
-       sz = (unsigned int) sbrk(0);
-       if (sz & (sizeof(struct mem_cell) - 1)) {
-               if (sbrk
-                   (sizeof(struct mem_cell) -
-                    (sz & (sizeof(struct mem_cell) - 1))) < 0)
-                       goto nomem;
-       }
-#endif                         /*  */
-       if (size == 0)
-               return 0;
-
-       /* Ensure size is aligned, otherwise our memory nodes become unaligned
-        * and we get hard-to-debug errors on platforms which require
-        * aligned accesses. */
-       size = ALIGNUP(size + sizeof(struct mem_cell));
-
-       /* Minor oops here, sbrk has a signed argument */
-       if (size > (((unsigned) -1) >> 1) - sizeof(struct mem_cell) * 2) {
-             nomem:errno = ENOMEM;
-               return 0;
+       struct memh *mh = MH(ptr), *p;
+
+       if (ptr == NULL)
+               return;
+
+       /* Find the free list block that is just before us */
+       for (p = __mfreeptr; !(p < mh && mh < p->next); p = p->next)
+               if (p >= p->next && (p < mh || mh < p->next))
+                       break;
+       /* Fix up and if we can merge forward */
+       if (mh + mh->size == p->next) {
+               mh->size += p->next->size;
+               mh->next = p->next->next;
+       } else
+               mh->next = p->next;
+       /* Ditto backwards */
+       if (p + p->size == mh) {
+               p->size += mh->size;
+               p->next = mh->next;
+       } else {
+               p->next = mh;
        }
-       size += sizeof(struct mem_cell);        /* Round up and leave space for size field */
-       ptr = (mem *) sbrk(size);
-       if ((int) ptr == -1)
-               return 0;
-       m_size(ptr) = size;
-       noise("CREATE", ptr);
-       return ptr + 1;
+       __mfreeptr = p;
 }
diff --git a/Library/libs/malloc-l.h b/Library/libs/malloc-l.h
deleted file mode 100644 (file)
index b8af513..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>\r
- * This file is part of the Linux-8086 C library and is distributed\r
- * under the GNU Library General Public License.\r
- *\r
- * This is a combined alloca/malloc package. It uses a classic algorithm\r
- * and so may be seen to be quite slow compared to more modern routines\r
- * with 'nasty' distributions.\r
- */\r
-#include <types.h>\r
-#include <malloc.h>\r
-#include <errno.h>\r
-#include <syscalls.h>\r
-#include <string.h>\r
-\r
-#define __MINI_MALLOC__\r
-\r
-union maximally_aligned {\r
-       uint8_t b;\r
-       uint16_t s;\r
-       uint32_t i;\r
-       #if !defined(NO_64BIT)\r
-               uint64_t l;\r
-       #endif\r
-       float f;\r
-       double d;\r
-};\r
-\r
-struct malloc_alignment_tester\r
-{\r
-       char b;\r
-       union maximally_aligned u;\r
-};\r
-\r
-#define ALIGNMENT ((int)&(((struct malloc_alignment_tester*)NULL)->u))\r
-#define ALIGNUP(s) (((s) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))\r
-\r
-#define MCHUNK         512     /* Allocation unit in 'mem' elements */\r
-#undef LAZY_FREE               /* If set frees can be infinitly defered */\r
-#undef MINALLOC        /* 32 */        /* Smallest chunk to alloc in 'mem's */\r
-#undef VERBOSE                         /* Lots of noise, debuging ? */\r
-\r
-#undef malloc\r
-#define MAX_INT ((int)(((unsigned)-1)>>1))\r
-\r
-#ifdef VERBOSE\r
-#define noise __noise\r
-#else\r
-#define noise(y,x)\r
-#endif\r
-\r
-typedef struct mem_cell {\r
-       struct mem_cell *next;              /* A pointer to the next mem */\r
-       unsigned int size;                  /* An int >= sizeof pointer */\r
-       char *depth;                        /* For the alloca hack */\r
-       union maximally_aligned padding[0]; /* Ensures alignment of payload */\r
-} mem;\r
-\r
-#define m_size(p)  ((p)[0].size)               /* For malloc */\r
-#define m_next(p)  ((p)[0].next)               /* For malloc and alloca */\r
-#define m_deep(p)  ((p)[0].depth)              /* For alloca */\r
-#define m_add(x,y) (mem *)((uchar *)x + y)     /* Sum mem* with y bytes */\r
-\r
-extern void *__mini_malloc __P((size_t));\r
-extern void *(*__alloca_alloc) __P((size_t));\r
-extern mem *__freed_list;\r
index 72b6a7d..abe00a4 100644 (file)
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
- * This file is part of the Linux-8086 C library and is distributed
- * under the GNU Library General Public License.
- *
- * This is a combined alloca/malloc package. It uses a classic algorithm
- * and so may be seen to be quite slow compared to more modern routines
- * with 'nasty' distributions.
+/*
+ *     This is an ANSI C version of the classic K & R memory allocator. The only
+ *     real difference here is that we handle large allocations and signs correctly
+ *     which the original didn't do portably. Specifically we
+ *     - correctly handle signed sbrk when the largest allocation allowed is
+ *       unsigned
+ *     - catch the case of a malloc close to the full size_t overflowing in the
+ *       nblock computation.
  */
 
-#include "malloc-l.h"
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include "malloc.h"
 
-/* The chunk_list pointer is either NULL or points to a chunk in a
- * circular list of all the free blocks in memory
- */
-
-#define Static static
-static mem *chunk_list = 0;
-Static mem *__search_chunk __P((unsigned));
-Static void __insert_chunk __P((mem *));
-void *malloc(size_t size)
-{
-       register unsigned sz;
-       register mem *ptr = 0;
-       if (size == 0)
-               return 0;       /* ANSI STD */
-
-       /* Ensure the size is aligned, otherwise our memory nodes become unaligned
-        * and we get hard-to-debug errors on platforms which require aligned
-        * accesses. */
-       sz = ALIGNUP(size + sizeof(struct mem_cell));
-
-#ifdef MINALLOC
-       if (sz < MINALLOC)
-               sz = MINALLOC;
-
-#endif                         /*  */
-#ifdef VERBOSE
-       {
-               static mem arr[2];
-               m_size(arr) = sz;
-               noise("WANTED", arr);
-       }
-#endif                         /*  */
-       __alloca_alloc = malloc;        /* We'll be messing with the heap now TVM */
+struct memh __mroot = { &__mroot, 0 };
 
-#ifdef LAZY_FREE
-       ptr = __search_chunk(sz);
-       if (ptr == 0) {
-
-#endif                         /*  */
-               /* First deal with the freed list */
-               if (__freed_list) {
-                       while (__freed_list) {
-                               ptr = __freed_list;
-                               __freed_list = m_next(__freed_list);
-                               if (m_size(ptr) == sz) {
-
-                                       /* Oh! Well that's lucky ain't it :-) */
-                                       noise("LUCKY MALLOC", ptr);
-                                       return ptr + 1;
-                               }
-                               __insert_chunk(ptr);
-                       }
-                       ptr = m_next(chunk_list);
-                       if (ptr + m_size(ptr) >= (void *) sbrk(0)) {
-
-                               /* Time to free for real */
-                               m_next(chunk_list) = m_next(ptr);
-                               if (ptr == m_next(ptr))
-                                       chunk_list = 0;
-                               free(ptr + 1);
-                       }
-#ifdef LAZY_FREE
-                       ptr = __search_chunk(sz);
-
-#endif                         /*  */
-               }
-#ifndef LAZY_FREE
-               ptr = __search_chunk(sz);
+struct memh *__mfreeptr = &__mroot;
 
-#endif                         /*  */
-               if (ptr == 0) {
-
-#ifdef MCHUNK
-                       unsigned int alloc =
-                           (MCHUNK * ((sz + MCHUNK - 1) / MCHUNK) - 1);
-                       if ((ptr = __mini_malloc(alloc)) != NULL)
-                               __insert_chunk(ptr - 1);
-
-                       else {  /* Oooo, near end of RAM */
-                               unsigned int needed = alloc;
-                               alloc /= 2;
-                               while (alloc > 256 && needed) {
-                                       ptr = __mini_malloc(alloc);
-                                       if (ptr) {
-                                               if (alloc > needed)
-                                                       needed = 0;
-
-                                               else
-                                                       needed -= alloc;
-                                               __insert_chunk(ptr - 1);
-                                       }
-
-                                       else
-                                               alloc /= 2;
-                               }
-                       }
-                       ptr = __search_chunk(sz);
-                       if (ptr == 0)
-#endif                         /*  */
-                       {
-
-#ifndef MCHUNK
-                               ptr = __mini_malloc(size);
-
-#endif                         /*  */
-#ifdef VERBOSE
-                               if (ptr == 0)
-                                       noise("MALLOC FAIL", 0);
-
-                               else
-                                       noise("MALLOC NOW", ptr - 1);
-
-#endif                         /*  */
-                               return ptr;
-                       }
-               }
-#ifdef LAZY_FREE
-       }
-#endif                         /*  */
-#ifdef VERBOSE
-       ptr[1].size = 0x5555;
-
-#endif                         /*  */
-       noise("MALLOC RET\n", ptr);
-       return ptr + 1;
+static struct memh *brkmore(size_t nb)
+{
+       struct memh *p;
+
+       if (nb < BRKSIZE)
+               nb = BRKSIZE;
+
+       /* Here be dragons: sbrk takes a signed value. We can however in C malloc
+          a size_t: We also assume nobody else misaligns the brk boundary, we won't
+          fix it if so - maybe we should ? */
+
+       /* Current end of memory */
+       p = sbrk(0);
+       if (p == (struct memh *) -1)
+               return NULL;
+       /* Overflow catch */
+       if (p + nb < p)
+               return NULL;
+       /* Move our break point. Using brk this way avoids the sign problems */
+       if (brk(p + nb))
+               return NULL;
+       /* Fake it as a used block and free it into the free list */
+       p->size = nb;
+       free(p + 1);
+       return __mfreeptr;
 }
 
-
-/* This function takes a pointer to a block of memory and inserts it into
- * the chain of memory chunks
- */
-Static void __insert_chunk(mem * mem_chunk)
+void *malloc(size_t size)
 {
-       register mem *p1, *p2;
-       if (chunk_list == 0) {  /* Simple case first */
-               m_next(mem_chunk) = chunk_list = mem_chunk;
-               noise("FIRST CHUNK", mem_chunk);
-               return;
-       }
-       p1 = mem_chunk;
-       p2 = chunk_list;
-
-       do {
-               if (p1 > p2) {
-
-                       /* We're at the top of the chain, p1 is higher */
-                       if (m_next(p2) <= p2) {
-                               if (m_add(p2, m_size(p2)) == p1) {
-
-                                       /* Good, stick 'em together */
-                                       noise("INSERT CHUNK", mem_chunk);
-                                       m_size(p2) += m_size(p1);
-                                       noise("JOIN 1", p2);
-                               }
-
-                               else {
-                                       m_next(p1) = m_next(p2);
-                                       m_next(p2) = p1;
-                                       noise("INSERT CHUNK", mem_chunk);
-                                       noise("FROM", p2);
-                               }
-                               return;
-                       }
-                       if (m_next(p2) > p1) {
-
-                               /* In chain, p1 between p2 and next */
-                               m_next(p1) = m_next(p2);
-                               m_next(p2) = p1;
-                               noise("INSERT CHUNK", mem_chunk);
-                               noise("FROM", p2);
-
-                               /* Try to join above */
-                               if (m_add(p1, m_size(p1)) == m_next(p1)) {
-                                       m_size(p1) += m_size(m_next(p1));
-                                       m_next(p1) = m_next(m_next(p1));
-                                       noise("JOIN 2", p1);
-                               }
-
-                               /* Try to join below */
-                               if (m_add(p2, m_size(p2)) == p1) {
-                                       m_size(p2) += m_size(p1);
-                                       m_next(p2) = m_next(p1);
-                                       noise("JOIN 3", p2);
-                               }
-                               chunk_list = p2;        /* Make sure it's valid */
-                               return;
+       struct memh *p, *prev;
+       size_t nblocks;
+
+       nblocks = size + sizeof(struct memh) + sizeof(struct memh) - 1;
+       /* Cheap way to catch overflow */
+       if (nblocks < size)
+               return NULL;
+       nblocks /= sizeof(struct memh);
+
+       prev = __mfreeptr;
+
+       for (p = prev->next;; prev = p, p = p->next) {
+               if (p->size >= nblocks) {
+                       if (p->size == nblocks)
+                               /* We found a hole the right size so unlink it */
+                               prev->next = p->next;
+                       else {
+                               /* Split a hole */
+                               p->size -= nblocks;
+                               /* Move down the block and hand the end of it to the user, to avoid
+                                  having to move the links. Our realloc depends upon this */
+                               p += p->size;
+                               p->size = nblocks;
                        }
+                       /* Ensure its a valid start point and optimal for malloc(x)/free(x) */
+                       __mfreeptr = prev;
+                       return (void *) (p + 1);
                }
-
-               else if (p1 < p2) {
-                       if (m_next(p2) <= p2 && p1 < m_next(p2)) {
-
-                               /* At top of chain, next is bottom of chain,
-                                * p1 is below next
-                                */
-                               m_next(p1) = m_next(p2);
-                               m_next(p2) = p1;
-                               noise("INSERT CHUNK", mem_chunk);
-                               noise("FROM", p2);
-                               chunk_list = p2;
-                               if (m_add(p1, m_size(p1)) == m_next(p1)) {
-                                       if (p2 == m_next(p1))
-                                               chunk_list = p1;
-                                       m_size(p1) += m_size(m_next(p1));
-                                       m_next(p1) = m_next(m_next(p1));
-                                       noise("JOIN 4", p1);
-                               }
-                               return;
-                       }
+               /* We've done one orbit.. */
+               if (p == __mfreeptr) {
+                       if ((p = brkmore(nblocks)) == NULL)
+                               return NULL;
                }
-               chunk_list = p2;        /* Save for search */
-               p2 = m_next(p2);
-       } while (p2 != chunk_list);
-
-       /* If we get here we have a problem, ignore it, maybe it'll go away */
-       noise("DROPPED CHUNK", mem_chunk);
-}
-
-
-/* This function will search for a chunk in memory of at least 'mem_size'
- * when found, if the chunk is too big it'll be split, and pointer to the
- * chunk returned. If none is found NULL is returned.
- */
-Static mem *__search_chunk(unsigned mem_size)
-{
-       register mem *p1, *p2;
-       if (chunk_list == 0)    /* Simple case first */
-               return 0;
-
-       /* Search for a block >= the size we want */
-       p1 = m_next(chunk_list);
-       p2 = chunk_list;
-
-       do {
-               noise("CHECKED", p1);
-               if (m_size(p1) >= mem_size)
-                       break;
-               p2 = p1;
-               p1 = m_next(p1);
-       } while (p2 != chunk_list);
-
-       /* None found, exit */
-       if (m_size(p1) < mem_size)
-               return 0;
-
-       /* If it's exactly right remove it */
-       if (m_size(p1) < mem_size + 2) {
-               noise("FOUND RIGHT", p1);
-               chunk_list = m_next(p2) = m_next(p1);
-               if (chunk_list == p1)
-                       chunk_list = 0;
-               return p1;
        }
-       noise("SPLIT", p1);
-
-       /* Otherwise split it */
-       m_next(p2) = m_add(p1, mem_size);
-       chunk_list = p2;
-       p2 = m_next(p2);
-       m_size(p2) = m_size(p1) - mem_size;
-       m_next(p2) = m_next(p1);
-       m_size(p1) = mem_size;
-       if (chunk_list == p1)
-               chunk_list = p2;
-
-#ifdef VERBOSE
-       p1[1].size = 0xAAAA;
-
-#endif                         /*  */
-       noise("INSERT CHUNK", p2);
-       noise("FOUND CHUNK", p1);
-       noise("LIST IS", chunk_list);
-       return p1;
 }
index f9f0a97..ae97dd0 100644 (file)
@@ -1,29 +1,53 @@
-/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>\r
- * This file is part of the Linux-8086 C library and is distributed\r
- * under the GNU Library General Public License.\r
- *\r
- * This is a combined alloca/malloc package. It uses a classic algorithm\r
- * and so may be seen to be quite slow compared to more modern routines\r
- * with 'nasty' distributions.\r
- */  \r
-    \r
-#include "malloc-l.h"\r
-\r
-void *realloc(void *ptr, size_t size) \r
+/*
+ *     This is an ANSI C version of the classic K & R memory allocator. The only
+ *     real difference here is that we handle large allocations and signs correctly
+ *     which the original didn't do portably. Specifically we
+ *     - correctly handle signed sbrk when the largest allocation allowed is
+ *       unsigned
+ *     - catch the case of a malloc close to the full size_t overflowing in the
+ *       nblock computation.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include "malloc.h"
+
+/*
+ * We cannot just free/malloc because there is a pathalogical case when we free
+ * a block which is merged with the block before and then we allocate some of the
+ * combined block. In that case we will a new header into the middle of the user
+ * bytes.
+ */
+void *realloc(void *ptr, size_t size)
 {
-       void *nptr;
-       unsigned int osize;
-       if (ptr == 0)
+       struct memh *mh = MH(ptr);
+       void *np;
+       size_t nblocks;
+
+       if (size == 0) {
+               free(ptr);
+               return NULL;
+       }
+
+       if (ptr == NULL)
                return malloc(size);
-       \r
-       /* ??? what if I really want to free rest of block ? */ \r
-       if (size <= (osize = (m_size(((mem *) ptr) - 1) - 1) * sizeof(mem)))
+
+       nblocks =
+           (size + sizeof(struct memh) + sizeof(struct memh) -
+            1) / sizeof(struct memh);
+
+       /* If size in mem blocks is sufficiently similar (make this fuzzier ?) */
+       if (nblocks == mh->size)
+               return ptr;
+
+       if (nblocks < mh->size)
                return ptr;
-       if ((nptr = malloc(size)) == NULL)
-               return 0;
-       memcpy(nptr, ptr, osize);
+       np = malloc(size);
+       if (np == NULL)
+               return ptr;
+       memcpy(np, ptr, mh->size * sizeof(struct memh));
        free(ptr);
-       return nptr;
+       return np;
 }
-
-\r