Replaced the funky and hard-to-compile ACK malloc with a much smaller
and simpler one stolen from K&R. libc builds now.
This commit is contained in:
parent
3ce4e53aa9
commit
a200a2fb53
|
@ -1,19 +1,4 @@
|
||||||
local headers = {}
|
include("plat/build.lua")
|
||||||
|
|
||||||
local function addheader(dir, list)
|
|
||||||
for _, f in ipairs(list) do
|
|
||||||
local b = basename(f)
|
|
||||||
headers[dir..b] = f
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addheader("", filenamesof("./headers/*.h"))
|
|
||||||
addheader("sys/", filenamesof("./headers/sys/*.h"))
|
|
||||||
|
|
||||||
acklibrary {
|
|
||||||
name = "headers",
|
|
||||||
hdrs = headers
|
|
||||||
}
|
|
||||||
|
|
||||||
tabgen {
|
tabgen {
|
||||||
name = "ctype_tab",
|
name = "ctype_tab",
|
||||||
|
@ -49,14 +34,15 @@ for _, plat in ipairs(vars.plats) do
|
||||||
"+ctype_files",
|
"+ctype_files",
|
||||||
"+ctype_tab",
|
"+ctype_tab",
|
||||||
"./ctype/*.c",
|
"./ctype/*.c",
|
||||||
"./assert/*.c",
|
|
||||||
"./errno/*.c",
|
"./errno/*.c",
|
||||||
"./locale/*.c",
|
"./locale/*.c",
|
||||||
|
"./malloc/*.c",
|
||||||
"./math/*.c",
|
"./math/*.c",
|
||||||
"./misc/environ.c", -- don't build everything here as it's all obsolete
|
"./misc/environ.c", -- don't build everything here as it's all obsolete
|
||||||
"./setjmp/*.c",
|
"./setjmp/*.c",
|
||||||
"./setjmp/*.e",
|
"./setjmp/*.e",
|
||||||
"./signal/*.c",
|
"./signal/*.c",
|
||||||
|
"./assert/*.c",
|
||||||
"./stdio/*.c",
|
"./stdio/*.c",
|
||||||
"./stdlib/*.c",
|
"./stdlib/*.c",
|
||||||
"./string/*.c",
|
"./string/*.c",
|
||||||
|
@ -65,7 +51,7 @@ for _, plat in ipairs(vars.plats) do
|
||||||
},
|
},
|
||||||
hdrs = {}, -- must be empty
|
hdrs = {}, -- must be empty
|
||||||
deps = {
|
deps = {
|
||||||
"+headers",
|
"lang/cem/libcc.ansi/headers+headers",
|
||||||
"plat/"..plat.."+headers",
|
"plat/"..plat.."+headers",
|
||||||
},
|
},
|
||||||
vars = { plat = plat }
|
vars = { plat = plat }
|
||||||
|
@ -83,6 +69,7 @@ for _, plat in ipairs(vars.plats) do
|
||||||
installable {
|
installable {
|
||||||
name = "pkg_"..plat,
|
name = "pkg_"..plat,
|
||||||
map = {
|
map = {
|
||||||
|
"lang/cem/libcc.ansi/headers+pkg",
|
||||||
["$(PLATIND)/"..plat.."/c-ansi.o"] = "+crt_"..plat,
|
["$(PLATIND)/"..plat.."/c-ansi.o"] = "+crt_"..plat,
|
||||||
["$(PLATIND)/"..plat.."/libc.a"] = "+lib_"..plat,
|
["$(PLATIND)/"..plat.."/libc.a"] = "+lib_"..plat,
|
||||||
}
|
}
|
||||||
|
|
26
lang/cem/libcc.ansi/headers/build.lua
Normal file
26
lang/cem/libcc.ansi/headers/build.lua
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
include("plat/build.lua")
|
||||||
|
|
||||||
|
local headers = {}
|
||||||
|
local installmap = {}
|
||||||
|
|
||||||
|
local function addheader(dir, list)
|
||||||
|
for _, f in ipairs(list) do
|
||||||
|
local b = basename(f)
|
||||||
|
headers[dir..b] = f
|
||||||
|
installmap[concatpath("$(PLATIND)/include/ansi/", dir, b)] = f
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addheader("", filenamesof("./*.h"))
|
||||||
|
addheader("sys/", filenamesof("./sys/*.h"))
|
||||||
|
|
||||||
|
acklibrary {
|
||||||
|
name = "headers",
|
||||||
|
hdrs = headers
|
||||||
|
}
|
||||||
|
|
||||||
|
installable {
|
||||||
|
name = "pkg",
|
||||||
|
map = installmap
|
||||||
|
}
|
||||||
|
|
151
lang/cem/libcc.ansi/malloc/malloc.c
Normal file
151
lang/cem/libcc.ansi/malloc/malloc.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
block_t __mem_root = {&__mem_root, 0};
|
||||||
|
block_t* __mem_freelist = &__mem_root;
|
||||||
|
|
||||||
|
/* Pulls more memory from the system. */
|
||||||
|
|
||||||
|
static block_t* brkmore(size_t nb)
|
||||||
|
{
|
||||||
|
uintptr_t bytes;
|
||||||
|
block_t* p;
|
||||||
|
|
||||||
|
if (nb < BRKSIZE)
|
||||||
|
nb = BRKSIZE;
|
||||||
|
bytes = nb * sizeof(block_t);
|
||||||
|
|
||||||
|
/* Danger, will robinson! sbrk's parameter is *signed*... but malloc() takes a
|
||||||
|
* size_t. */
|
||||||
|
|
||||||
|
if (bytes > INTPTR_MAX)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p = sbrk(bytes);
|
||||||
|
if (p == (block_t*)-1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Add it to the free list by pretending it's a used block and freeing it. */
|
||||||
|
|
||||||
|
p->size = nb;
|
||||||
|
free(p + 1);
|
||||||
|
return __mem_freelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* malloc(size_t size)
|
||||||
|
{
|
||||||
|
block_t* p;
|
||||||
|
block_t* prev;
|
||||||
|
size_t nblocks;
|
||||||
|
|
||||||
|
/* Add on space for the header; make sure we allocate a round number
|
||||||
|
* of blocks; avoid overflow. */
|
||||||
|
nblocks = BLOCKCOUNT(size);
|
||||||
|
if (nblocks < size)
|
||||||
|
return NULL;
|
||||||
|
nblocks /= sizeof(block_t);
|
||||||
|
|
||||||
|
prev = __mem_freelist;
|
||||||
|
p = prev->next;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (p->size == nblocks)
|
||||||
|
{
|
||||||
|
/* We found a hole of exactly the right size. Unlink and return it.
|
||||||
|
* The size field is already set. */
|
||||||
|
prev->next = p->next;
|
||||||
|
__mem_freelist = prev;
|
||||||
|
return (void*) (p+1);
|
||||||
|
}
|
||||||
|
else if (p->size > nblocks)
|
||||||
|
{
|
||||||
|
/* We found a hole bigger than we need. We shrink the hole and return
|
||||||
|
* what's left. */
|
||||||
|
p->size -= nblocks;
|
||||||
|
p += p->size; /* p now points at our new block */
|
||||||
|
p->size = nblocks;
|
||||||
|
__mem_freelist = prev;
|
||||||
|
return (void*) (p+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == __mem_freelist)
|
||||||
|
{
|
||||||
|
/* Uh-oh --- we've gone right round the ring and haven't found
|
||||||
|
* anything. Get more memory from the system and keep going. */
|
||||||
|
p = brkmore(nblocks);
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = p;
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void *ptr)
|
||||||
|
{
|
||||||
|
block_t* h = BLOCKOF(ptr);
|
||||||
|
block_t* p;
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* __mem_freelist points into an ordered ring of free blocks. First,
|
||||||
|
* we run around the ring until we find the last block before this one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
p = __mem_freelist;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* Is h between p and the block after p? If so, h needs to be inserted
|
||||||
|
* after p, so stop here. */
|
||||||
|
if ((p < h) && (h < p->next))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Is p the last block before the end of the address space? */
|
||||||
|
if (p >= p->next)
|
||||||
|
{
|
||||||
|
/* Is h after p? (That is, will it become the new last block?) */
|
||||||
|
if (p < h)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Is h going to become the new *first* block? */
|
||||||
|
if (h < p->next)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we can, merge the next block onto the end of h. */
|
||||||
|
|
||||||
|
if ((h + h->size) == p->next)
|
||||||
|
{
|
||||||
|
h->size += p->next->size;
|
||||||
|
h->next = p->next->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Otherwise, insert h before p->next. */
|
||||||
|
h->next = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now try to merge h onto the end of p. */
|
||||||
|
|
||||||
|
if ((p + p->size) == h)
|
||||||
|
{
|
||||||
|
p->size += h->size;
|
||||||
|
p->next = h->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Okay, we couldn't do the merge. Fix up the linked list. */
|
||||||
|
p->next = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ...and update the ring pointer. */
|
||||||
|
__mem_freelist = p;
|
||||||
|
}
|
||||||
|
|
25
lang/cem/libcc.ansi/malloc/malloc.h
Normal file
25
lang/cem/libcc.ansi/malloc/malloc.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* This is an ANSI C version of the classic K&R memory allocator, with
|
||||||
|
* some improvements stolen from the Fuzix libc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MALLOC_H
|
||||||
|
#define MALLOC_H
|
||||||
|
|
||||||
|
typedef struct block_s {
|
||||||
|
struct block_s* next;
|
||||||
|
size_t size; /* in sizeof(block_t) units */
|
||||||
|
} block_t;
|
||||||
|
|
||||||
|
extern block_t __mem_root;
|
||||||
|
extern block_t* __mem_first_free;
|
||||||
|
|
||||||
|
#define BLOCKOF(p) (((block_t*)(p)) - 1)
|
||||||
|
|
||||||
|
/* Smallest amount to allocate from brk */
|
||||||
|
#define BRKSIZE (512 / sizeof(block_t))
|
||||||
|
|
||||||
|
#define BLOCKCOUNT(bytes) \
|
||||||
|
(bytes + sizeof(block_t) + sizeof(block_t) - 1)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
41
lang/cem/libcc.ansi/malloc/realloc.c
Normal file
41
lang/cem/libcc.ansi/malloc/realloc.c
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "malloc.h"
|
||||||
|
|
||||||
|
void* realloc(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
block_t* h;
|
||||||
|
size_t nblocks;
|
||||||
|
void* newptr;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
return malloc(size);
|
||||||
|
|
||||||
|
h = BLOCKOF(ptr);
|
||||||
|
|
||||||
|
nblocks = BLOCKCOUNT(size);
|
||||||
|
/* Overflow check. */
|
||||||
|
if (nblocks < size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Shrinking the block? Don't bother doing anything (it's never worth it). */
|
||||||
|
if (nblocks <= h->size)
|
||||||
|
return ptr;
|
||||||
|
|
||||||
|
/* Allocate and copy. */
|
||||||
|
|
||||||
|
newptr = malloc(size);
|
||||||
|
if (!newptr)
|
||||||
|
return NULL;
|
||||||
|
memcpy(newptr, ptr, h->size * sizeof(block_t));
|
||||||
|
free(ptr);
|
||||||
|
return newptr;
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
READ_ME
|
|
||||||
add_file
|
|
||||||
check.c
|
|
||||||
check.h
|
|
||||||
global.c
|
|
||||||
impl.h
|
|
||||||
log.c
|
|
||||||
log.h
|
|
||||||
mal.c
|
|
||||||
param.h
|
|
||||||
phys.c
|
|
||||||
phys.h
|
|
||||||
size_type.h
|
|
|
@ -1,20 +0,0 @@
|
||||||
DIR = .
|
|
||||||
MALLOC_C = $(DIR)/malloc.c
|
|
||||||
MALLOC_O = $(DIR)/malloc.o
|
|
||||||
|
|
||||||
MALLOCSRC = READ_ME size_type.h param.h impl.h check.h log.h phys.h \
|
|
||||||
mal.c log.c phys.c check.c
|
|
||||||
|
|
||||||
$(MALLOC_C): $(MALLOCSRC) Makefile add_file
|
|
||||||
rm -f $(MALLOC_C)
|
|
||||||
for i in $(MALLOCSRC) ; do add_file $$i >> $(MALLOC_C) ; done
|
|
||||||
rm -f $(MALLOC_O)
|
|
||||||
|
|
||||||
pr:
|
|
||||||
@pr Makefile add_file $(MALLOCSRC)
|
|
||||||
|
|
||||||
opr:
|
|
||||||
make pr | opr
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o malloc.c
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
PROGRAM
|
|
||||||
malloc(), free(), realloc()
|
|
||||||
AUTHOR
|
|
||||||
Dick Grune, Free University, Amsterdam
|
|
||||||
Modified by Ceriel Jacobs, Free University, Amsterdam,
|
|
||||||
to make it faster
|
|
||||||
VERSION
|
|
||||||
$Id$
|
|
||||||
DESCRIPTION
|
|
||||||
This is an independent rewrite of the malloc/free package; it is
|
|
||||||
fast and efficient. Free blocks are kept in doubly linked lists,
|
|
||||||
list N holding blocks with sizes between 2**N and 2**(N+1)-1.
|
|
||||||
Consequently neither malloc nor free have to do any searching:
|
|
||||||
the cost of a call of malloc() (or free()) is constant, however
|
|
||||||
many blocks you have got.
|
|
||||||
|
|
||||||
If you switch on the NON_STANDARD macro (see param.h) every block
|
|
||||||
costs 2 pointers overhead (otherwise it's 4).
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
There is an organisational problem here: during devellopment
|
|
||||||
I want the package divided into modules, which implies external
|
|
||||||
names for the communication. The only external names I want in
|
|
||||||
the finished product are malloc, realloc and free. This requires
|
|
||||||
some hanky-panky.
|
|
||||||
*/
|
|
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "/**********************************************************/"
|
|
||||||
echo "/* This was file $1 */"
|
|
||||||
echo "/**********************************************************/"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
cat $1 |
|
|
||||||
sed '
|
|
||||||
/#include[ ].*"/d
|
|
||||||
s/^public/private/
|
|
||||||
s/^publicdata/static/
|
|
||||||
'
|
|
||||||
echo ''
|
|
|
@ -1,303 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef CHECK /* otherwise this whole file is skipped */
|
|
||||||
|
|
||||||
/* ??? check these later */
|
|
||||||
private acquire_malout(void), check_ml_last(const char *s);
|
|
||||||
private dump_all_mallinks(void), dump_free_list(int i);
|
|
||||||
private dump_mallink(const char *s, mallink *ml), print_loop(mallink *ml);
|
|
||||||
private working_on(mallink *ml);
|
|
||||||
private size_type checksum(mallink *ml);
|
|
||||||
static FILE *malout;
|
|
||||||
|
|
||||||
private mallink *free_list_entry(int i);
|
|
||||||
|
|
||||||
#define for_free_list(i,p) \
|
|
||||||
for (p = free_list_entry(i); p; p = log_next_of(p))
|
|
||||||
|
|
||||||
#define for_all_mallinks(ml) /* backwards! */ \
|
|
||||||
for (ml = ml_last; ml; \
|
|
||||||
ml = first_mallink(ml) ? MAL_NULL : phys_prev_of(ml))
|
|
||||||
|
|
||||||
/* Maldump */
|
|
||||||
|
|
||||||
static int pr_cnt = 0;
|
|
||||||
|
|
||||||
maldump(int n) {
|
|
||||||
/* Dump pertinent info in pseudo-readable format;
|
|
||||||
abort afterwards if n != 0.
|
|
||||||
*/
|
|
||||||
static int dumping = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (dumping)
|
|
||||||
return;
|
|
||||||
dumping++;
|
|
||||||
acquire_malout();
|
|
||||||
fprintf(malout,
|
|
||||||
">>>>>>>>>>>>>>>> DUMP OF ALL MALLINKS <<<<<<<<<<<<<<<<");
|
|
||||||
fprintf(malout, " ml_last = %p\n", ml_last);
|
|
||||||
if (++pr_cnt == 100) pr_cnt = 0;
|
|
||||||
dump_all_mallinks();
|
|
||||||
fprintf(malout,
|
|
||||||
">>>>>>>>>>>>>>>> DUMP OF FREE_LISTS <<<<<<<<<<<<<<<<\n");
|
|
||||||
if (++pr_cnt == 100) pr_cnt = 0;
|
|
||||||
for (i = 0; i < MAX_FLIST; i++)
|
|
||||||
dump_free_list(i);
|
|
||||||
fprintf(malout,
|
|
||||||
">>>>>>>>>>>>>>>> END OF DUMP <<<<<<<<<<<<<<<<\n");
|
|
||||||
fclose(malout);
|
|
||||||
dumping--;
|
|
||||||
if (n)
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
acquire_malout(void) {
|
|
||||||
static char buf[BUFSIZ];
|
|
||||||
|
|
||||||
if (!malout) {
|
|
||||||
malout = freopen("mal.out", "w", stderr);
|
|
||||||
setbuf(malout, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
dump_all_mallinks(void) {
|
|
||||||
mallink *ml;
|
|
||||||
|
|
||||||
for_all_mallinks (ml) {
|
|
||||||
if (print_loop(ml))
|
|
||||||
return;
|
|
||||||
dump_mallink((char *)0, ml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
dump_free_list(int i) {
|
|
||||||
mallink *ml = free_list_entry(i);
|
|
||||||
|
|
||||||
if (!ml)
|
|
||||||
return;
|
|
||||||
fprintf(malout, "%2d: ", i);
|
|
||||||
for_free_list(i, ml) {
|
|
||||||
if (print_loop(ml))
|
|
||||||
return;
|
|
||||||
fprintf(malout, "%p ", ml);
|
|
||||||
}
|
|
||||||
fprintf(malout, "<\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int
|
|
||||||
print_loop(mallink *ml) {
|
|
||||||
if (print_of(ml) == pr_cnt) {
|
|
||||||
fprintf(malout, "... PRINT LOOP\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
set_print(ml, pr_cnt);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
dump_mallink(const char *s, mallink *ml) {
|
|
||||||
acquire_malout();
|
|
||||||
if (s)
|
|
||||||
fprintf(malout, "%s: ", s);
|
|
||||||
fprintf(malout, "@: %p;", ml);
|
|
||||||
if (ml && checksum_of(ml) != checksum(ml))
|
|
||||||
fprintf(malout, ">>>> CORRUPTED <<<<");
|
|
||||||
if (!ml) {
|
|
||||||
fprintf(malout, "\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (free_of(ml)) {
|
|
||||||
fprintf(malout, " l_p: %p;", _log_prev_of(ml));
|
|
||||||
fprintf(malout, " l_n: %p;", _log_next_of(ml));
|
|
||||||
}
|
|
||||||
fprintf(malout, " p_s: %p;", prev_size_of(ml));
|
|
||||||
fprintf(malout, " t_s: %p;", _this_size_of(ml));
|
|
||||||
fprintf(malout, " sz: %lu;", (unsigned long) size_of(ml));
|
|
||||||
fprintf(malout, " fr: %d;", free_of(ml));
|
|
||||||
fprintf(malout, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check_mallinks() checks the total data structure as accessible
|
|
||||||
through free_list[] and ml_last. All check_sums should be OK,
|
|
||||||
except those held in the small array off_colour. This is a
|
|
||||||
trick to allow to continue checking even when a few mallinks
|
|
||||||
are temporarily out of order.
|
|
||||||
Check_mallinks() tests for a lot of internal consistency.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Some arbitrary constants */
|
|
||||||
#define IN_ML_LAST 93
|
|
||||||
#define IN_FREE_LIST 57 /* and in ml_last */
|
|
||||||
#define CLEAR 21
|
|
||||||
|
|
||||||
#define VRIJ 1
|
|
||||||
#define BEZET 2
|
|
||||||
|
|
||||||
private
|
|
||||||
check_mallinks(const char *s) {
|
|
||||||
mallink *ml;
|
|
||||||
size_type size;
|
|
||||||
int i;
|
|
||||||
char stat;
|
|
||||||
|
|
||||||
check_ml_last(s);
|
|
||||||
stat = BEZET;
|
|
||||||
for_all_mallinks(ml) {
|
|
||||||
if (checksum_of(ml) != checksum(ml))
|
|
||||||
Error("mallink info at %p corrupted", s, ml);
|
|
||||||
if (working_on(ml)) {
|
|
||||||
stat = BEZET;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( !last_mallink(ml) &&
|
|
||||||
phys_prev_of(phys_next_of(ml)) != ml
|
|
||||||
)
|
|
||||||
Error("upward chain bad at %p", s, ml);
|
|
||||||
if ( !first_mallink(ml) &&
|
|
||||||
phys_next_of(phys_prev_of(ml)) != ml
|
|
||||||
)
|
|
||||||
Error("downward chain bad at %p", s, ml);
|
|
||||||
if (free_of(ml)) {
|
|
||||||
if (stat == VRIJ)
|
|
||||||
Error("free mallink at %p follows free mallink",
|
|
||||||
s, ml);
|
|
||||||
stat = VRIJ;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stat = BEZET;
|
|
||||||
set_mark(ml, IN_ML_LAST);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, size = MIN_SIZE; i < MAX_FLIST; i++, size *= 2) {
|
|
||||||
for_free_list(i, ml) {
|
|
||||||
if (working_on(ml))
|
|
||||||
continue;
|
|
||||||
if (!free_of(ml))
|
|
||||||
Error("occupied mallink %p occurs in free_list", s, ml);
|
|
||||||
switch (mark_of(ml)) {
|
|
||||||
case IN_ML_LAST:
|
|
||||||
set_mark(ml, IN_FREE_LIST);
|
|
||||||
break;
|
|
||||||
case IN_FREE_LIST:
|
|
||||||
Error("mallink %p occurs in 2 free_lists",
|
|
||||||
s, ml);
|
|
||||||
default:
|
|
||||||
Error("unknown mallink %p in free_list",
|
|
||||||
s, ml);
|
|
||||||
}
|
|
||||||
if (size_of(ml) < size)
|
|
||||||
Error("size of mallink %p too small", s, ml);
|
|
||||||
if (size_of(ml) >= 2*size)
|
|
||||||
Error("size of mallink %p too large", s, ml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for_all_mallinks (ml) {
|
|
||||||
if (working_on(ml))
|
|
||||||
continue;
|
|
||||||
if (free_of(ml) && mark_of(ml) != IN_FREE_LIST)
|
|
||||||
Error("free mallink %p is in no free_list", s, ml);
|
|
||||||
set_mark(ml, CLEAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
check_ml_last(const char *s) {
|
|
||||||
if (ml_last && _this_size_of(ml_last) == 0)
|
|
||||||
Error("size of ml_last == 0, at %p", s, ml_last);
|
|
||||||
}
|
|
||||||
|
|
||||||
private size_type
|
|
||||||
checksum(mallink *ml) {
|
|
||||||
size_type sum = 0;
|
|
||||||
|
|
||||||
if (free_of(ml)) {
|
|
||||||
sum += (size_type)_log_prev_of(ml);
|
|
||||||
sum += (size_type)_log_next_of(ml);
|
|
||||||
}
|
|
||||||
sum += (size_type)prev_size_of(ml);
|
|
||||||
sum += (size_type)_this_size_of(ml);
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
calc_checksum(mallink *ml) {
|
|
||||||
set_checksum(ml, checksum(ml));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define N_COLOUR 10
|
|
||||||
static mallink *off_colour[N_COLOUR];
|
|
||||||
|
|
||||||
private
|
|
||||||
started_working_on(mallink *ml) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < N_COLOUR; i++)
|
|
||||||
if (off_colour[i] == MAL_NULL) {
|
|
||||||
off_colour[i] = ml;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Error("out of off_colour array at %p", "started_working_on", ml);
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
stopped_working_on(mallink *ml) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < N_COLOUR; i++)
|
|
||||||
if (off_colour[i] == ml) {
|
|
||||||
off_colour[i] = MAL_NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Error("stopped working on mallink %p", "stopped_working_on", ml);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int
|
|
||||||
working_on(mallink *ml) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < N_COLOUR; i++)
|
|
||||||
if (off_colour[i] == ml)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private
|
|
||||||
check_work_empty(const char *s) {
|
|
||||||
int i;
|
|
||||||
int cnt = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < N_COLOUR; i++)
|
|
||||||
if (off_colour[i] != MAL_NULL)
|
|
||||||
cnt++;
|
|
||||||
if (cnt != 0)
|
|
||||||
Error("off_colour not empty", s, MAL_NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int
|
|
||||||
Error(const char *fmt, const char *s, mallink *ml) {
|
|
||||||
static int already_called = 0;
|
|
||||||
|
|
||||||
if (already_called++) return 0;
|
|
||||||
setbuf(stdout, (char *) 0);
|
|
||||||
printf("%s: ", s);
|
|
||||||
printf(fmt, (long)ml);
|
|
||||||
printf("\n");
|
|
||||||
acquire_malout();
|
|
||||||
fprintf(malout, "%s: ", s);
|
|
||||||
fprintf(malout, fmt, (long)ml);
|
|
||||||
fprintf(malout, "\n");
|
|
||||||
fflush(stdout);
|
|
||||||
maldump(1);
|
|
||||||
return 0; /* to satisfy lint */
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CHECK */
|
|
|
@ -1,21 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#ifdef CHECK
|
|
||||||
|
|
||||||
public check_mallinks(const char *s), calc_checksum(mallink *ml);
|
|
||||||
public check_work_empty(const char *s);
|
|
||||||
public started_working_on(mallink *ml), stopped_working_on(mallink *ml);
|
|
||||||
|
|
||||||
#else /* ifndef CHECK */
|
|
||||||
|
|
||||||
#define maldump(n) abort()
|
|
||||||
#define check_mallinks(s) 0
|
|
||||||
#define calc_checksum(ml) 0
|
|
||||||
#define started_working_on(ml) 0
|
|
||||||
#define stopped_working_on(ml) 0
|
|
||||||
#define check_work_empty(s) 0
|
|
||||||
|
|
||||||
#endif /* CHECK */
|
|
|
@ -1,11 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include "param.h"
|
|
||||||
#include "impl.h"
|
|
||||||
|
|
||||||
/* The only global data item:
|
|
||||||
*/
|
|
||||||
mallink *ml_last; /* link to the world */
|
|
|
@ -1,84 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
/* This file essentially describes how the mallink info block
|
|
||||||
is implemented.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define MIN_SIZE (1<<LOG_MIN_SIZE)
|
|
||||||
#define MAX_FLIST (LOG_MAX_SIZE - LOG_MIN_SIZE)
|
|
||||||
#if ALIGNMENT != 4 && ALIGNMENT != 8 && ALIGNMENT != 16
|
|
||||||
#error ALIGNMENT must be 4, 8 or 16
|
|
||||||
#elif ALIGNMENT % _EM_LSIZE
|
|
||||||
/* since calloc() does it's initialization in longs */
|
|
||||||
#error ALIGNMENT must be a multiple of the long size
|
|
||||||
#endif
|
|
||||||
#define align(n) (((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
|
|
||||||
|
|
||||||
union _inf {
|
|
||||||
union _inf *ptr;
|
|
||||||
size_type ui;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef union _inf mallink;
|
|
||||||
#define MAL_NULL ((mallink *)0)
|
|
||||||
|
|
||||||
/* Access macros; only these macros know where to find values.
|
|
||||||
They are also lvalues.
|
|
||||||
*/
|
|
||||||
#ifndef NON_STANDARD
|
|
||||||
#define OFF_SET 0
|
|
||||||
#else /* def NON_STANDARD */
|
|
||||||
#define OFF_SET 2
|
|
||||||
#endif /* NON_STANDARD */
|
|
||||||
|
|
||||||
#define _log_prev_of(ml) ((ml)[-1+OFF_SET]).ptr
|
|
||||||
#define _log_next_of(ml) ((ml)[-2+OFF_SET]).ptr
|
|
||||||
#define _phys_prev_of(ml) ((ml)[-3+OFF_SET]).ptr
|
|
||||||
#define _this_size_of(ml) ((ml)[-4+OFF_SET]).ui
|
|
||||||
#ifndef CHECK
|
|
||||||
#define N_WORDS 4
|
|
||||||
#else /* ifdef CHECK */
|
|
||||||
#define _checksum_of(ml) ((ml)[-5+OFF_SET]).ui
|
|
||||||
#define _print_of(ml) ((ml)[-6+OFF_SET]).ui
|
|
||||||
#define _mark_of(ml) ((ml)[-7+OFF_SET]).ui
|
|
||||||
#define N_WORDS 7
|
|
||||||
#endif /* CHECK */
|
|
||||||
|
|
||||||
#define mallink_size() (size_t) \
|
|
||||||
align((N_WORDS - OFF_SET) * sizeof (mallink))
|
|
||||||
|
|
||||||
#ifdef CHECK
|
|
||||||
#define set_mark(ml,e) (_mark_of(ml) = (e))
|
|
||||||
#define mark_of(ml) (_mark_of(ml))
|
|
||||||
|
|
||||||
#define set_checksum(ml,e) (_checksum_of(ml) = (e))
|
|
||||||
#define checksum_of(ml) (_checksum_of(ml))
|
|
||||||
#endif /* CHECK */
|
|
||||||
|
|
||||||
#define new_mallink(ml) ( _log_prev_of(ml) = 0, \
|
|
||||||
_log_next_of(ml) = 0, \
|
|
||||||
_phys_prev_of(ml) = 0, \
|
|
||||||
_this_size_of(ml) = 0 )
|
|
||||||
|
|
||||||
#define block_of_mallink(ml) ((void *)ml)
|
|
||||||
#define mallink_of_block(addr) ((mallink *)addr)
|
|
||||||
|
|
||||||
#define public extern
|
|
||||||
#define publicdata extern
|
|
||||||
#ifndef EXTERN
|
|
||||||
#define private static
|
|
||||||
#define privatedata static
|
|
||||||
#else /* def EXTERN */
|
|
||||||
#define private extern
|
|
||||||
#define privatedata
|
|
||||||
#endif /* EXTERN */
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
|
||||||
public m_assert(const char *fn, int ln);
|
|
||||||
#define assert(b) (!(b) ? m_assert(__FILE__, __LINE__) : 0)
|
|
||||||
#else /* ndef ASSERT */
|
|
||||||
#define assert(b) 0
|
|
||||||
#endif /* ASSERT */
|
|
|
@ -1,129 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include "param.h"
|
|
||||||
#include "impl.h"
|
|
||||||
#include "check.h"
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
/* Logical manipulations.
|
|
||||||
The chunks are properly chained in the physical chain.
|
|
||||||
*/
|
|
||||||
|
|
||||||
privatedata mallink *free_list[MAX_FLIST+1];
|
|
||||||
|
|
||||||
public
|
|
||||||
link_free_chunk(register mallink *ml)
|
|
||||||
{
|
|
||||||
/* The free chunk ml is inserted in its proper logical
|
|
||||||
chain.
|
|
||||||
*/
|
|
||||||
register mallink **mlp = &free_list[0];
|
|
||||||
register size_type n = size_of(ml);
|
|
||||||
register mallink *ml1;
|
|
||||||
|
|
||||||
assert(n < (1L << LOG_MAX_SIZE));
|
|
||||||
|
|
||||||
do {
|
|
||||||
n >>= 1;
|
|
||||||
mlp++;
|
|
||||||
}
|
|
||||||
while (n >= MIN_SIZE);
|
|
||||||
|
|
||||||
ml1 = *--mlp;
|
|
||||||
set_log_prev(ml, MAL_NULL);
|
|
||||||
set_log_next(ml, ml1);
|
|
||||||
calc_checksum(ml);
|
|
||||||
if (ml1) {
|
|
||||||
/* link backwards
|
|
||||||
*/
|
|
||||||
set_log_prev(ml1, ml);
|
|
||||||
calc_checksum(ml1);
|
|
||||||
}
|
|
||||||
*mlp = ml;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
unlink_free_chunk(register mallink *ml)
|
|
||||||
{
|
|
||||||
/* Unlinks a free chunk from (the middle of) the
|
|
||||||
logical chain.
|
|
||||||
*/
|
|
||||||
register mallink *next = log_next_of(ml);
|
|
||||||
register mallink *prev = log_prev_of(ml);
|
|
||||||
|
|
||||||
if (!prev) {
|
|
||||||
/* it is the first in the chain */
|
|
||||||
register mallink **mlp = &free_list[-1];
|
|
||||||
register size_type n = size_of(ml);
|
|
||||||
|
|
||||||
assert(n < (1L << LOG_MAX_SIZE));
|
|
||||||
do {
|
|
||||||
n >>= 1;
|
|
||||||
mlp++;
|
|
||||||
}
|
|
||||||
while (n >= MIN_SIZE);
|
|
||||||
*mlp = next;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_log_next(prev, next);
|
|
||||||
calc_checksum(prev);
|
|
||||||
}
|
|
||||||
if (next) {
|
|
||||||
set_log_prev(next, prev);
|
|
||||||
calc_checksum(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public mallink *
|
|
||||||
search_free_list(int class, size_t n)
|
|
||||||
{
|
|
||||||
/* Searches the free_list[class] for a chunk of at least size n;
|
|
||||||
since it is searching a slightly undersized list,
|
|
||||||
such a block may not be there.
|
|
||||||
*/
|
|
||||||
register mallink *ml;
|
|
||||||
|
|
||||||
for (ml = free_list[class]; ml; ml = log_next_of(ml))
|
|
||||||
if (size_of(ml) >= n)
|
|
||||||
return ml;
|
|
||||||
return MAL_NULL; /* nothing found */
|
|
||||||
}
|
|
||||||
|
|
||||||
public mallink *
|
|
||||||
first_present(int class)
|
|
||||||
{
|
|
||||||
/* Find the index i in free_list[] such that:
|
|
||||||
i >= class && free_list[i] != MAL_NULL.
|
|
||||||
Return MAL_NULL if no such i exists;
|
|
||||||
Otherwise, return the first block of this list, after
|
|
||||||
unlinking it.
|
|
||||||
*/
|
|
||||||
register mallink **mlp, *ml;
|
|
||||||
|
|
||||||
for (mlp = &free_list[class]; mlp < &free_list[MAX_FLIST]; mlp++) {
|
|
||||||
if ((ml = *mlp) != MAL_NULL) {
|
|
||||||
|
|
||||||
*mlp = log_next_of(ml); /* may be MAL_NULL */
|
|
||||||
if (*mlp) {
|
|
||||||
/* unhook backward link
|
|
||||||
*/
|
|
||||||
set_log_prev(*mlp, MAL_NULL);
|
|
||||||
calc_checksum(*mlp);
|
|
||||||
}
|
|
||||||
return ml;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MAL_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CHECK
|
|
||||||
public mallink *
|
|
||||||
free_list_entry(int i) {
|
|
||||||
/* To allow maldump.c access to log.c's private data.
|
|
||||||
*/
|
|
||||||
return free_list[i];
|
|
||||||
}
|
|
||||||
#endif /* CHECK */
|
|
|
@ -1,26 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
/* Algorithms to manipulate the doubly-linked lists of free
|
|
||||||
chunks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public link_free_chunk(mallink *ml), unlink_free_chunk(mallink *ml);
|
|
||||||
public mallink *first_present(int class);
|
|
||||||
public mallink *search_free_list(int class, size_t n);
|
|
||||||
|
|
||||||
#ifdef STORE
|
|
||||||
#define in_store(ml) ((size_type)_phys_prev_of(ml) & STORE_BIT)
|
|
||||||
#define set_store(ml, e) \
|
|
||||||
(_phys_prev_of(ml) = (mallink *) \
|
|
||||||
((e) ? (size_type) _phys_prev_of(ml) | STORE_BIT : \
|
|
||||||
(size_type) _phys_prev_of(ml) & ~STORE_BIT))
|
|
||||||
#endif
|
|
||||||
#define set_log_prev(ml,e) (_log_prev_of(ml) = (e))
|
|
||||||
#define log_prev_of(ml) (mallink *) (_log_prev_of(ml))
|
|
||||||
|
|
||||||
#define set_log_next(ml,e) (_log_next_of(ml) = (e))
|
|
||||||
#define log_next_of(ml) (mallink *) (_log_next_of(ml))
|
|
||||||
|
|
|
@ -1,385 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "param.h"
|
|
||||||
#include "impl.h"
|
|
||||||
#include "check.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "phys.h"
|
|
||||||
|
|
||||||
/* Malloc space is traversed by N doubly-linked lists of chunks, each
|
|
||||||
containing a couple of house-keeping data addressed as a
|
|
||||||
'mallink' and a piece of useful space, called the block.
|
|
||||||
The N lists are accessed through their starting pointers in
|
|
||||||
free_list[]. Free_list[n] points to a list of chunks between
|
|
||||||
2**(n+LOG_MIN_SIZE) and 2**(n+LOG_MIN_SIZE+1)-1, which means
|
|
||||||
that the smallest chunk is 2**LOG_MIN_SIZE (== MIN_SIZE).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef SYSTEM
|
|
||||||
#include <system.h>
|
|
||||||
#define SBRK sys_break
|
|
||||||
extern void *SBRK(int incr);
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#define SBRK sbrk
|
|
||||||
#define ILL_BREAK (void *)(-1) /* funny failure value */
|
|
||||||
#endif
|
|
||||||
#ifdef STORE
|
|
||||||
#define MAX_STORE 32
|
|
||||||
private do_free(mallink *ml), sell_out(void);
|
|
||||||
privatedata mallink *store[MAX_STORE];
|
|
||||||
#endif /* STORE */
|
|
||||||
|
|
||||||
void *
|
|
||||||
malloc(register size_t n)
|
|
||||||
{check_mallinks("malloc entry");{
|
|
||||||
register mallink *ml;
|
|
||||||
register int min_class;
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
|
|
||||||
#ifdef STORE
|
|
||||||
if (n <= MAX_STORE*MIN_SIZE) {
|
|
||||||
/* look in the store first */
|
|
||||||
register mallink **stp = &store[(n >> LOG_MIN_SIZE) - 1];
|
|
||||||
|
|
||||||
if (ml = *stp) {
|
|
||||||
*stp = log_next_of(ml);
|
|
||||||
set_store(ml, 0);
|
|
||||||
check_mallinks("malloc fast exit");
|
|
||||||
assert(! in_store(ml));
|
|
||||||
return block_of_mallink(ml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* STORE */
|
|
||||||
|
|
||||||
check_work_empty("malloc, entry");
|
|
||||||
|
|
||||||
/* Acquire a chunk of at least size n if at all possible;
|
|
||||||
Try everything.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
/* Inline substitution of "smallest".
|
|
||||||
*/
|
|
||||||
register size_t n1 = n;
|
|
||||||
|
|
||||||
assert(n1 < (1L << LOG_MAX_SIZE));
|
|
||||||
min_class = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
n1 >>= 1;
|
|
||||||
min_class++;
|
|
||||||
} while (n1 >= MIN_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (min_class >= MAX_FLIST)
|
|
||||||
return NULL; /* we don't deal in blocks that big */
|
|
||||||
ml = first_present(min_class);
|
|
||||||
if (ml == MAL_NULL) {
|
|
||||||
/* Try and extend */
|
|
||||||
register void *p;
|
|
||||||
#define GRABSIZE 4096 /* Power of 2 */
|
|
||||||
register size_t req =
|
|
||||||
((MIN_SIZE<<min_class)+ mallink_size() + GRABSIZE - 1) &
|
|
||||||
~(GRABSIZE-1);
|
|
||||||
|
|
||||||
if (!ml_last) {
|
|
||||||
/* first align SBRK() */
|
|
||||||
|
|
||||||
p = SBRK(0);
|
|
||||||
SBRK((int) (align((size_type) p) - (size_type) p));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SBRK takes an int; sorry ... */
|
|
||||||
if ((int) req < 0) {
|
|
||||||
p = ILL_BREAK;
|
|
||||||
} else {
|
|
||||||
p = SBRK((int)req);
|
|
||||||
}
|
|
||||||
if (p == ILL_BREAK) {
|
|
||||||
req = n + mallink_size();
|
|
||||||
if ((int) req >= 0) p = SBRK((int)req);
|
|
||||||
}
|
|
||||||
if (p == ILL_BREAK) {
|
|
||||||
/* Now this is bad. The system will not give us
|
|
||||||
more memory. We can only liquidate our store
|
|
||||||
and hope it helps.
|
|
||||||
*/
|
|
||||||
#ifdef STORE
|
|
||||||
sell_out();
|
|
||||||
ml = first_present(min_class);
|
|
||||||
if (ml == MAL_NULL) {
|
|
||||||
#endif /* STORE */
|
|
||||||
/* In this emergency we try to locate a suitable
|
|
||||||
chunk in the free_list just below the safe
|
|
||||||
one; some of these chunks may fit the job.
|
|
||||||
*/
|
|
||||||
ml = search_free_list(min_class - 1, n);
|
|
||||||
if (!ml) /* really out of space */
|
|
||||||
return NULL;
|
|
||||||
started_working_on(ml);
|
|
||||||
unlink_free_chunk(ml);
|
|
||||||
check_mallinks("suitable_chunk, forced");
|
|
||||||
#ifdef STORE
|
|
||||||
}
|
|
||||||
else started_working_on(ml);
|
|
||||||
#endif /* STORE */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert((size_type)p == align((size_type)p));
|
|
||||||
ml = create_chunk(p, req);
|
|
||||||
}
|
|
||||||
check_mallinks("suitable_chunk, extended");
|
|
||||||
}
|
|
||||||
else started_working_on(ml);
|
|
||||||
|
|
||||||
/* we have a chunk */
|
|
||||||
set_free(ml, 0);
|
|
||||||
calc_checksum(ml);
|
|
||||||
check_mallinks("suitable_chunk, removed");
|
|
||||||
n += mallink_size();
|
|
||||||
if (n + MIN_SIZE <= size_of(ml)) {
|
|
||||||
truncate(ml, n);
|
|
||||||
}
|
|
||||||
stopped_working_on(ml);
|
|
||||||
check_mallinks("malloc exit");
|
|
||||||
check_work_empty("malloc exit");
|
|
||||||
#ifdef STORE
|
|
||||||
assert(! in_store(ml));
|
|
||||||
#endif
|
|
||||||
return block_of_mallink(ml);
|
|
||||||
}}
|
|
||||||
|
|
||||||
void
|
|
||||||
free(void *addr)
|
|
||||||
{check_mallinks("free entry");{
|
|
||||||
register mallink *ml;
|
|
||||||
|
|
||||||
if (addr == NULL) {
|
|
||||||
check_mallinks("free(0) very fast exit");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ml = mallink_of_block(addr);
|
|
||||||
#ifdef STORE
|
|
||||||
|
|
||||||
if (free_of(ml) || in_store(ml))
|
|
||||||
return; /* user frees free block */
|
|
||||||
if (size_of(ml) <= MAX_STORE*MIN_SIZE) {
|
|
||||||
/* return to store */
|
|
||||||
mallink **stp = &store[(size_of(ml) >> LOG_MIN_SIZE) - 1];
|
|
||||||
|
|
||||||
set_log_next(ml, *stp);
|
|
||||||
*stp = ml;
|
|
||||||
set_store(ml, 1);
|
|
||||||
calc_checksum(ml);
|
|
||||||
check_mallinks("free fast exit");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
do_free(ml);
|
|
||||||
check_mallinks("free exit");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
|
|
||||||
private
|
|
||||||
do_free(register mallink *ml)
|
|
||||||
{{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STORE
|
|
||||||
if (free_of(ml)) return;
|
|
||||||
#endif /* STORE */
|
|
||||||
started_working_on(ml);
|
|
||||||
set_free(ml, 1);
|
|
||||||
calc_checksum(ml);
|
|
||||||
if (! last_mallink(ml)) {
|
|
||||||
register mallink *next = phys_next_of(ml);
|
|
||||||
|
|
||||||
if (free_of(next)) coalesce_forw(ml, next);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! first_mallink(ml)) {
|
|
||||||
register mallink *prev = phys_prev_of(ml);
|
|
||||||
|
|
||||||
if (free_of(prev)) {
|
|
||||||
coalesce_backw(ml, prev);
|
|
||||||
ml = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
link_free_chunk(ml);
|
|
||||||
stopped_working_on(ml);
|
|
||||||
check_work_empty("free");
|
|
||||||
|
|
||||||
/* Compile-time checks on param.h */
|
|
||||||
switch (0) {
|
|
||||||
case MIN_SIZE < OFF_SET * sizeof(mallink): break;
|
|
||||||
case 1: break;
|
|
||||||
/* If this statement does not compile due to duplicate case
|
|
||||||
entry, the minimum size block cannot hold the links for
|
|
||||||
the free blocks. Either raise LOG_MIN_SIZE or switch
|
|
||||||
off NON_STANDARD.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
switch(0) {
|
|
||||||
case sizeof(void *) != sizeof(size_type): break;
|
|
||||||
case 1: break;
|
|
||||||
/* If this statement does not compile due to duplicate
|
|
||||||
case entry, size_type is not defined correctly.
|
|
||||||
Redefine and compile again.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
|
|
||||||
void *
|
|
||||||
realloc(void *addr, register size_t n)
|
|
||||||
{check_mallinks("realloc entry");{
|
|
||||||
register mallink *ml, *ph_next;
|
|
||||||
register size_type size;
|
|
||||||
|
|
||||||
if (addr == NULL) {
|
|
||||||
/* Behave like most Unix realloc's when handed a
|
|
||||||
null-pointer
|
|
||||||
*/
|
|
||||||
return malloc(n);
|
|
||||||
}
|
|
||||||
if (n == 0) {
|
|
||||||
free(addr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ml = mallink_of_block(addr);
|
|
||||||
if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
|
|
||||||
#ifdef STORE
|
|
||||||
if (in_store(ml)) {
|
|
||||||
register mallink *stp = store[(size_of(ml) >> LOG_MIN_SIZE) - 1];
|
|
||||||
mallink *stp1 = NULL;
|
|
||||||
while (ml != stp) {
|
|
||||||
stp1 = stp;
|
|
||||||
stp = log_next_of(stp);
|
|
||||||
}
|
|
||||||
stp = log_next_of(stp);
|
|
||||||
if (! stp1) store[(size_of(ml) >> LOG_MIN_SIZE) - 1] = stp;
|
|
||||||
else set_log_next(stp1, stp);
|
|
||||||
set_store(ml, 0);
|
|
||||||
calc_checksum(ml);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (free_of(ml)) {
|
|
||||||
unlink_free_chunk(ml);
|
|
||||||
set_free(ml, 0); /* user reallocs free block */
|
|
||||||
}
|
|
||||||
started_working_on(ml);
|
|
||||||
size = size_of(ml);
|
|
||||||
if ( /* we can simplify the problem by adding the next chunk: */
|
|
||||||
n > size &&
|
|
||||||
!last_mallink(ml) &&
|
|
||||||
(ph_next = phys_next_of(ml), free_of(ph_next)) &&
|
|
||||||
n <= size + mallink_size() + size_of(ph_next)
|
|
||||||
) {
|
|
||||||
/* add in the physically next chunk */
|
|
||||||
unlink_free_chunk(ph_next);
|
|
||||||
combine_chunks(ml, ph_next);
|
|
||||||
size = size_of(ml);
|
|
||||||
check_mallinks("realloc, combining");
|
|
||||||
}
|
|
||||||
if (n > size) { /* this didn't help */
|
|
||||||
void *new;
|
|
||||||
register char *l1, *l2 = addr;
|
|
||||||
|
|
||||||
stopped_working_on(ml);
|
|
||||||
if (!(new = l1 = malloc(n))) return NULL; /* no way */
|
|
||||||
while (size--) *l1++ = *l2++;
|
|
||||||
free(addr);
|
|
||||||
check_work_empty("mv_realloc");
|
|
||||||
#ifdef STORE
|
|
||||||
assert(! in_store(mallink_of_block(new)));
|
|
||||||
#endif
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
/* it helped, but maybe too well */
|
|
||||||
n += mallink_size();
|
|
||||||
if (n + MIN_SIZE <= size_of(ml)) {
|
|
||||||
truncate(ml, n);
|
|
||||||
}
|
|
||||||
stopped_working_on(ml);
|
|
||||||
check_mallinks("realloc exit");
|
|
||||||
check_work_empty("realloc");
|
|
||||||
#ifdef STORE
|
|
||||||
assert(! in_store(ml));
|
|
||||||
#endif
|
|
||||||
return addr;
|
|
||||||
}}
|
|
||||||
|
|
||||||
void *
|
|
||||||
calloc(size_t nmemb, size_t size)
|
|
||||||
{check_mallinks("calloc entry");{
|
|
||||||
long *l1, *l2;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (size == 0) return NULL;
|
|
||||||
if (nmemb == 0) return NULL;
|
|
||||||
|
|
||||||
/* Check for overflow on the multiplication. The peephole-optimizer
|
|
||||||
* will eliminate all but one of the possibilities.
|
|
||||||
*/
|
|
||||||
if (sizeof(size_t) == sizeof(int)) {
|
|
||||||
if (UINT_MAX / size < nmemb) return NULL;
|
|
||||||
} else if (sizeof(size_t) == sizeof(long)) {
|
|
||||||
if (ULONG_MAX / size < nmemb) return NULL;
|
|
||||||
} else return NULL; /* can't happen, can it ? */
|
|
||||||
|
|
||||||
n = size * nmemb;
|
|
||||||
if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
|
|
||||||
if (n >= (1L << LOG_MAX_SIZE)) return NULL;
|
|
||||||
l1 = (long *) malloc(n);
|
|
||||||
l2 = l1 + (n / sizeof(long)); /* n is at least long aligned */
|
|
||||||
while ( l2 != l1 ) *--l2 = 0;
|
|
||||||
check_mallinks("calloc exit");
|
|
||||||
check_work_empty("calloc exit");
|
|
||||||
return (void *)l1;
|
|
||||||
}}
|
|
||||||
/* Auxiliary routines */
|
|
||||||
|
|
||||||
#ifdef STORE
|
|
||||||
private
|
|
||||||
sell_out(void) {
|
|
||||||
/* Frees all block in store.
|
|
||||||
*/
|
|
||||||
register mallink **stp;
|
|
||||||
|
|
||||||
for (stp = &store[0]; stp < &store[MAX_STORE]; stp++) {
|
|
||||||
register mallink *ml = *stp;
|
|
||||||
|
|
||||||
while (ml) {
|
|
||||||
*stp = log_next_of(ml);
|
|
||||||
set_store(ml, 0);
|
|
||||||
do_free(ml);
|
|
||||||
ml = *stp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif /* STORE */
|
|
||||||
|
|
||||||
#ifdef ASSERT
|
|
||||||
public
|
|
||||||
m_assert(const char *fn, int ln)
|
|
||||||
{
|
|
||||||
char ch;
|
|
||||||
|
|
||||||
while (*fn)
|
|
||||||
write(2, fn++, 1);
|
|
||||||
write(2, ": malloc assert failed in line ", 31);
|
|
||||||
ch = (ln / 100) + '0'; write(2, &ch, 1); ln %= 100;
|
|
||||||
ch = (ln / 10) + '0'; write(2, &ch, 1); ln %= 10;
|
|
||||||
ch = (ln / 1) + '0'; write(2, &ch, 1);
|
|
||||||
write(2, "\n", 1);
|
|
||||||
maldump(1);
|
|
||||||
}
|
|
||||||
#endif /* ASSERT */
|
|
|
@ -1,56 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include "size_type.h"
|
|
||||||
|
|
||||||
# define NON_STANDARD /* If defined, the contents of a block
|
|
||||||
will NOT be left undisturbed after it
|
|
||||||
is freed, as opposed to what it says
|
|
||||||
in the manual (malloc(2)).
|
|
||||||
Setting this option reduces the memory
|
|
||||||
overhead considerably. I personally
|
|
||||||
consider the specified behaviour an
|
|
||||||
artefact of the original
|
|
||||||
implementation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
# undef ASSERT /* If defined, some inexpensive tests
|
|
||||||
will be made to ensure the
|
|
||||||
correctness of some sensitive data.
|
|
||||||
It often turns an uncontrolled crash
|
|
||||||
into a controlled one.
|
|
||||||
*/
|
|
||||||
|
|
||||||
# undef CHECK /* If defined, extensive and expensive
|
|
||||||
tests will be done, inculding a
|
|
||||||
checksum on the mallinks (chunk
|
|
||||||
information blocks). The resulting
|
|
||||||
information will be printed on a file
|
|
||||||
called mal.out .
|
|
||||||
Additionally a function
|
|
||||||
maldump(n) int n;
|
|
||||||
will be defined, which will dump
|
|
||||||
pertinent info in pseudo-readable
|
|
||||||
form; it aborts afterwards if n != 0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
# undef EXTERN /* If defined, all static names will
|
|
||||||
become extern, which is a help in
|
|
||||||
using adb(1) or prof(1)
|
|
||||||
*/
|
|
||||||
|
|
||||||
# define STORE /* If defined, separate free lists will
|
|
||||||
be kept of chunks with small sizes,
|
|
||||||
to speed things up a little.
|
|
||||||
*/
|
|
||||||
|
|
||||||
# undef SYSTEM /* If defined, the system module is used.
|
|
||||||
Otherwise, "sbrk" is called directly.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ALIGNMENT 8
|
|
||||||
/* alignment common to all types */
|
|
||||||
#define LOG_MIN_SIZE 3
|
|
||||||
#define LOG_MAX_SIZE 24
|
|
|
@ -1,96 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "param.h"
|
|
||||||
#include "impl.h"
|
|
||||||
#include "check.h"
|
|
||||||
#include "phys.h"
|
|
||||||
|
|
||||||
/* Physical manipulations.
|
|
||||||
The blocks concerned are not in any logical chain.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public mallink *
|
|
||||||
create_chunk(void *p, size_t n)
|
|
||||||
{
|
|
||||||
/* The newly acquired piece of memory at p, of length n,
|
|
||||||
is turned into a free chunk, properly chained in the
|
|
||||||
physical chain.
|
|
||||||
The address of the chunk is returned.
|
|
||||||
*/
|
|
||||||
register mallink *ml;
|
|
||||||
/* All of malloc memory is followed by a virtual chunk, the
|
|
||||||
mallink of which starts mallink_size() bytes past the last
|
|
||||||
byte in memory.
|
|
||||||
Its use is prevented by testing for ml == ml_last first.
|
|
||||||
*/
|
|
||||||
register mallink *last = ml_last;
|
|
||||||
|
|
||||||
assert(!last || p == (char *)phys_next_of(last) - mallink_size());
|
|
||||||
ml = (mallink *)((char *)p + mallink_size()); /* bump ml */
|
|
||||||
new_mallink(ml);
|
|
||||||
started_working_on(ml);
|
|
||||||
set_free(ml, 1);
|
|
||||||
set_phys_prev(ml, last);
|
|
||||||
ml_last = ml;
|
|
||||||
|
|
||||||
set_phys_next(ml, (mallink *)((char *)ml + n));
|
|
||||||
calc_checksum(ml);
|
|
||||||
assert(size_of(ml) + mallink_size() == n);
|
|
||||||
if (last && free_of(last)) {
|
|
||||||
coalesce_backw(ml, last);
|
|
||||||
ml = last;
|
|
||||||
}
|
|
||||||
check_mallinks("create_chunk, phys. linked");
|
|
||||||
return ml;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
truncate(register mallink *ml, size_t size)
|
|
||||||
{
|
|
||||||
/* The chunk ml is truncated.
|
|
||||||
The chunk at ml is split in two.
|
|
||||||
The remaining part is then freed.
|
|
||||||
*/
|
|
||||||
register mallink *new = (mallink *)((char *)ml + size);
|
|
||||||
register mallink *ph_next = phys_next_of(ml);
|
|
||||||
|
|
||||||
new_mallink(new);
|
|
||||||
set_free(new, 1);
|
|
||||||
set_phys_prev(new, ml);
|
|
||||||
set_phys_next(new, ph_next);
|
|
||||||
calc_checksum(new);
|
|
||||||
if (! last_mallink(ml)) {
|
|
||||||
set_phys_prev(ph_next, new);
|
|
||||||
calc_checksum(ph_next);
|
|
||||||
if (free_of(ph_next)) coalesce_forw(new, ph_next);
|
|
||||||
}
|
|
||||||
else ml_last = new;
|
|
||||||
set_phys_next(ml, new);
|
|
||||||
calc_checksum(ml);
|
|
||||||
|
|
||||||
started_working_on(new);
|
|
||||||
link_free_chunk(new);
|
|
||||||
stopped_working_on(new);
|
|
||||||
check_mallinks("truncate");
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
combine_chunks(register mallink *ml1, register mallink *ml2)
|
|
||||||
{
|
|
||||||
/* The chunks ml1 and ml2 are combined.
|
|
||||||
*/
|
|
||||||
register mallink *ml3 = phys_next_of(ml2);
|
|
||||||
|
|
||||||
set_phys_next(ml1, ml3);
|
|
||||||
calc_checksum(ml1);
|
|
||||||
if (!last_mallink(ml2)) {
|
|
||||||
set_phys_prev(ml3, ml1);
|
|
||||||
calc_checksum(ml3);
|
|
||||||
}
|
|
||||||
if (ml_last == ml2)
|
|
||||||
ml_last = ml1;
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/* $Id$ */
|
|
||||||
/*
|
|
||||||
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
||||||
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
||||||
*/
|
|
||||||
/* Algorithms to manipulate the doubly-linked list of physical
|
|
||||||
chunks.
|
|
||||||
*/
|
|
||||||
publicdata mallink *ml_last;
|
|
||||||
|
|
||||||
#define FREE_BIT 01
|
|
||||||
#ifdef STORE
|
|
||||||
#define STORE_BIT 02
|
|
||||||
#define BITS (FREE_BIT|STORE_BIT)
|
|
||||||
#else
|
|
||||||
#define BITS (FREE_BIT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define __bits(ml) ((int)((size_type)_phys_prev_of(ml) & BITS))
|
|
||||||
#define __free_of(ml) ((int)((size_type)_phys_prev_of(ml) & FREE_BIT))
|
|
||||||
#define __phys_prev_of(ml) ((mallink *)((size_type)_phys_prev_of(ml) & ~BITS))
|
|
||||||
#define prev_size_of(ml) ((char *)(ml) - \
|
|
||||||
(char *)__phys_prev_of(ml) - \
|
|
||||||
mallink_size() \
|
|
||||||
)
|
|
||||||
#define set_phys_prev(ml,e) \
|
|
||||||
(_phys_prev_of(ml) = (mallink *) ((char *)e + __bits(ml)))
|
|
||||||
|
|
||||||
#ifdef CHECK
|
|
||||||
public Error(const char *fmt, const char *s, mallink *ml);
|
|
||||||
#define phys_prev_of(ml) (mallink *) \
|
|
||||||
(first_mallink(ml) ? \
|
|
||||||
(char *)Error("phys_prev_of first_mallink %p", "somewhere", ml) : \
|
|
||||||
(char *)__phys_prev_of(ml) \
|
|
||||||
)
|
|
||||||
#else /* ndef CHECK */
|
|
||||||
#define phys_prev_of(ml) __phys_prev_of(ml)
|
|
||||||
#endif /* CHECK */
|
|
||||||
|
|
||||||
#define first_mallink(ml) (int) (__phys_prev_of(ml) == 0)
|
|
||||||
#define last_mallink(ml) (int) ((ml) == ml_last)
|
|
||||||
|
|
||||||
/* There is an ambiguity in the semantics of phys_next_of: sometimes
|
|
||||||
one wants it to return MAL_NULL if there is no next chunk, at
|
|
||||||
other times one wants the address of the virtual chunk at the
|
|
||||||
end of memory. The present version returns the address of the
|
|
||||||
(virtual) chunk and relies on the user to test last_mallink(ml)
|
|
||||||
first.
|
|
||||||
*/
|
|
||||||
#define size_of(ml) (_this_size_of(ml) - mallink_size())
|
|
||||||
#define set_phys_next(ml,e) \
|
|
||||||
(_this_size_of(ml) = (size_type)((char *)(e) - (char *)(ml)))
|
|
||||||
#define phys_next_of(ml) (mallink *) ((char *)(ml) + _this_size_of(ml))
|
|
||||||
|
|
||||||
#define set_free(ml,e) \
|
|
||||||
(_phys_prev_of(ml) = (mallink *) \
|
|
||||||
((e) ? (size_type) _phys_prev_of(ml) | FREE_BIT : \
|
|
||||||
(size_type) _phys_prev_of(ml) & ~FREE_BIT))
|
|
||||||
#define free_of(ml) (__free_of(ml))
|
|
||||||
|
|
||||||
#define coalesce_forw(ml,nxt) ( unlink_free_chunk(nxt), \
|
|
||||||
combine_chunks((ml), (nxt)))
|
|
||||||
|
|
||||||
#define coalesce_backw(ml,prv) ( unlink_free_chunk(prv), \
|
|
||||||
stopped_working_on(ml), \
|
|
||||||
combine_chunks((prv), (ml)), \
|
|
||||||
started_working_on(prv))
|
|
||||||
|
|
||||||
#ifdef CHECK
|
|
||||||
#define set_print(ml,e) (_print_of(ml) = (e))
|
|
||||||
#define print_of(ml) (_print_of(ml))
|
|
||||||
#endif /* CHECK */
|
|
||||||
|
|
||||||
public truncate(mallink *ml, size_t size);
|
|
||||||
public combine_chunks(register mallink *ml1, register mallink *ml2);
|
|
||||||
public mallink *create_chunk(void *p, size_t n);
|
|
|
@ -1,8 +0,0 @@
|
||||||
#if _EM_WSIZE == _EM_PSIZE
|
|
||||||
typedef unsigned int size_type;
|
|
||||||
#elif _EM_LSIZE == _EM_PSIZE
|
|
||||||
typedef unsigned long size_type;
|
|
||||||
#else
|
|
||||||
#error funny pointer size
|
|
||||||
#endif
|
|
||||||
#include <stdlib.h>
|
|
Loading…
Reference in a new issue