Initial revision
This commit is contained in:
parent
56a28240ff
commit
3788350d7c
38
modules/src/malloc/Makefile
Normal file
38
modules/src/malloc/Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
EMHOME = ../../..
|
||||
INSTALL = $(EMHOME)/modules/install
|
||||
COMPARE = $(EMHOME)/modules/compare
|
||||
CFLAGS = -O -I$(EMHOME)/modules/h
|
||||
|
||||
MALLOCSRC = READ_ME size_type.h param.h impl.h check.h log.h phys.h \
|
||||
mal.c log.c phys.c check.c
|
||||
|
||||
all: malloc.o
|
||||
|
||||
install: all
|
||||
$(INSTALL) lib/malloc.o
|
||||
|
||||
cmp: all
|
||||
$(COMPARE) lib/malloc.o
|
||||
|
||||
malloc1.c: $(MALLOCSRC) Makefile add_file
|
||||
rm -f malloc1.c
|
||||
for i in $(MALLOCSRC) ; do add_file $$i >> malloc1.c ; done
|
||||
|
||||
malloc.c: malloc1.c
|
||||
cclash -l7 -c malloc1.c > clashes
|
||||
cid -Fclashes < malloc1.c > malloc.c
|
||||
|
||||
pr:
|
||||
@pr Makefile add_file $(MALLOCSRC)
|
||||
|
||||
opr:
|
||||
make pr | opr
|
||||
|
||||
clean:
|
||||
rm -f *.o clashes malloc1.c size_type.h getsize malloc.c
|
||||
|
||||
size_type.h: getsize
|
||||
getsize > size_type.h
|
||||
|
||||
getsize: getsize.o
|
||||
$(CC) -o getsize getsize.o
|
27
modules/src/malloc/READ_ME
Normal file
27
modules/src/malloc/READ_ME
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
PROGRAM
|
||||
malloc(), free(), realloc()
|
||||
AUTHOR
|
||||
Dick Grune, Free University, Amsterdam
|
||||
Modified by Ceriel Jacobs, Free University, Amsterdam,
|
||||
to make it faster
|
||||
VERSION
|
||||
$Header$
|
||||
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.
|
||||
*/
|
14
modules/src/malloc/add_file
Executable file
14
modules/src/malloc/add_file
Executable file
|
@ -0,0 +1,14 @@
|
|||
echo ''
|
||||
echo '/**********************************************************/'
|
||||
echo '/*'
|
||||
echo '/* ' This was file $1
|
||||
echo '/*'
|
||||
echo '/**********************************************************/'
|
||||
echo ''
|
||||
cat $1 |
|
||||
sed '
|
||||
/#include[ ].*"/d
|
||||
s/^public/private/
|
||||
s/^publicdata/static/
|
||||
'
|
||||
echo ''
|
297
modules/src/malloc/check.c
Normal file
297
modules/src/malloc/check.c
Normal file
|
@ -0,0 +1,297 @@
|
|||
#include <stdio.h>
|
||||
#include "param.h"
|
||||
#include "impl.h"
|
||||
#include "check.h"
|
||||
#include "phys.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifdef CHECK /* otherwise this whole file is skipped */
|
||||
|
||||
private acquire_malout(), check_ml_last();
|
||||
private dump_all_mallinks(), dump_free_list(), dump_mallink(), print_loop();
|
||||
private working_on();
|
||||
private unsigned int checksum();
|
||||
static FILE *malout;
|
||||
|
||||
public mallink *free_list_entry();
|
||||
|
||||
#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(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 = %ld\n", (long)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() {
|
||||
static char buf[BUFSIZ];
|
||||
|
||||
if (!malout) {
|
||||
malout = fopen("mal.out", "w");
|
||||
setbuf(malout, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
dump_all_mallinks() {
|
||||
mallink *ml;
|
||||
|
||||
for_all_mallinks (ml) {
|
||||
if (print_loop(ml))
|
||||
return;
|
||||
dump_mallink((char *)0, ml);
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
dump_free_list(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, "%ld ", ml);
|
||||
}
|
||||
fprintf(malout, "<\n");
|
||||
}
|
||||
|
||||
private int
|
||||
print_loop(ml) 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(s, ml) char *s; mallink *ml; {
|
||||
acquire_malout();
|
||||
if (s)
|
||||
fprintf(malout, "%s: ", s);
|
||||
fprintf(malout, "@: %ld;", (long)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: %ld;", (long)_log_prev_of(ml));
|
||||
fprintf(malout, " l_n: %ld;", (long)_log_next_of(ml));
|
||||
}
|
||||
fprintf(malout, " p_s: %ld;", (long)prev_size_of(ml));
|
||||
fprintf(malout, " t_s: %ld;", (long)_this_size_of(ml));
|
||||
fprintf(malout, " sz: %ld;", (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
|
||||
|
||||
public
|
||||
check_mallinks(s) char *s; {
|
||||
mallink *ml;
|
||||
unsigned int 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 %ld 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 %ld", s, ml);
|
||||
if ( !first_mallink(ml) &&
|
||||
phys_next_of(phys_prev_of(ml)) != ml
|
||||
)
|
||||
Error("downward chain bad at %ld", s, ml);
|
||||
if (free_of(ml)) {
|
||||
if (stat == VRIJ)
|
||||
Error("free mallink at %ld 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 %ld 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 %ld occurs in 2 free_lists",
|
||||
s, ml);
|
||||
default:
|
||||
Error("unknown mallink %ld in free_list",
|
||||
s, ml);
|
||||
}
|
||||
if (size_of(ml) < size)
|
||||
Error("size of mallink %ld too small", s, ml);
|
||||
if (size_of(ml) >= 2*size)
|
||||
Error("size of mallink %ld 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 %ld is in no free_list", s, ml);
|
||||
set_mark(ml, CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
private
|
||||
check_ml_last(s) char *s; {
|
||||
if (ml_last && _this_size_of(ml_last) == 0)
|
||||
Error("size of ml_last == 0, at %ld", s, ml_last);
|
||||
}
|
||||
|
||||
private unsigned int
|
||||
checksum(ml) mallink *ml; {
|
||||
unsigned int sum = 0;
|
||||
|
||||
if (free_of(ml)) {
|
||||
sum += (unsigned int)_log_prev_of(ml);
|
||||
sum += (unsigned int)_log_next_of(ml);
|
||||
}
|
||||
sum += (unsigned int)prev_size_of(ml);
|
||||
sum += (unsigned int)_this_size_of(ml);
|
||||
return sum;
|
||||
}
|
||||
|
||||
public
|
||||
calc_checksum(ml) mallink *ml; {
|
||||
set_checksum(ml, checksum(ml));
|
||||
}
|
||||
|
||||
#define N_COLOUR 10
|
||||
static mallink *off_colour[N_COLOUR];
|
||||
|
||||
public
|
||||
started_working_on(ml) 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 %ld", "started_working_on", ml);
|
||||
}
|
||||
|
||||
public
|
||||
stopped_working_on(ml) 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 %ld", "stopped_working_on", ml);
|
||||
}
|
||||
|
||||
private int
|
||||
working_on(ml) mallink *ml; {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < N_COLOUR; i++)
|
||||
if (off_colour[i] == ml)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public
|
||||
check_work_empty(s) 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);
|
||||
}
|
||||
|
||||
public int
|
||||
Error(fmt, s, ml) char *fmt, *s; mallink *ml; {
|
||||
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
|
15
modules/src/malloc/check.h
Normal file
15
modules/src/malloc/check.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifdef CHECK
|
||||
|
||||
public check_mallinks(), calc_checksum(), check_work_empty();
|
||||
public started_working_on(), stopped_working_on();
|
||||
|
||||
#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
|
19
modules/src/malloc/getsize.c
Normal file
19
modules/src/malloc/getsize.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* find out if a pointer-sized integer, preferably unsigned,
|
||||
must be declared as an unsigned int or a long
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
main()
|
||||
{
|
||||
if (sizeof(unsigned int) == sizeof(char *)) {
|
||||
puts("typedef unsigned int size_type;");
|
||||
return 0;
|
||||
}
|
||||
if (sizeof(long) == sizeof(char *)) {
|
||||
puts("typedef long size_type;");
|
||||
return 0;
|
||||
}
|
||||
fputs(stderr, "funny pointer size\n");
|
||||
return 1;
|
||||
}
|
6
modules/src/malloc/global.c
Normal file
6
modules/src/malloc/global.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "param.h"
|
||||
#include "impl.h"
|
||||
|
||||
/* The only global data item:
|
||||
*/
|
||||
mallink *ml_last; /* link to the world */
|
73
modules/src/malloc/impl.h
Normal file
73
modules/src/malloc/impl.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* 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 != 1 && ALIGNMENT != 2 && ALIGNMENT != 4 && ALIGNMENT != 8 &&\
|
||||
ALIGNMENT != 16
|
||||
ALIGNMENT must be a (small) power of two !!!
|
||||
#endif
|
||||
#define align(n) (((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))
|
||||
|
||||
union _inf {
|
||||
union _inf *ptr;
|
||||
unsigned int 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() (unsigned int) \
|
||||
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 block_of_mallink(ml) ((char *)ml)
|
||||
#define mallink_of_block(addr) ((mallink *)addr)
|
||||
|
||||
#define public extern
|
||||
#define publicdata
|
||||
#ifndef EXTERN
|
||||
#define private static
|
||||
#define privatedata static
|
||||
#else def EXTERN
|
||||
#define private extern
|
||||
#define privatedata
|
||||
#endif EXTERN
|
||||
|
||||
#ifdef ASSERT
|
||||
public m_assert();
|
||||
#define assert(b) (!(b) ? m_assert(__FILE__, __LINE__) : 0)
|
||||
#else ndef ASSERT
|
||||
#define assert(b) 0
|
||||
#endif ASSERT
|
128
modules/src/malloc/log.c
Normal file
128
modules/src/malloc/log.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
#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];
|
||||
|
||||
public
|
||||
link_free_chunk(ml)
|
||||
register mallink *ml;
|
||||
{
|
||||
/* The free chunk ml is inserted in its proper logical
|
||||
chain.
|
||||
*/
|
||||
register mallink **mlp = &free_list[-1];
|
||||
register unsigned int n = size_of(ml);
|
||||
register mallink *ml1;
|
||||
|
||||
assert(n < (1 << 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(ml)
|
||||
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 unsigned int n = size_of(ml);
|
||||
|
||||
assert(n < (1 << 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(class, n)
|
||||
unsigned int 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(class)
|
||||
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(i) {
|
||||
/* To allow maldump.c access to log.c's private data.
|
||||
*/
|
||||
return free_list[i];
|
||||
}
|
||||
#endif CHECK
|
12
modules/src/malloc/log.h
Normal file
12
modules/src/malloc/log.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* Algorithms to manipulate the doubly-linked lists of free
|
||||
chunks.
|
||||
*/
|
||||
|
||||
public link_free_chunk(), unlink_free_chunk();
|
||||
public mallink *first_present(), *search_free_list();
|
||||
|
||||
#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))
|
293
modules/src/malloc/mal.c
Normal file
293
modules/src/malloc/mal.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
#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
|
||||
#else
|
||||
#define SBRK sbrk
|
||||
#define ILL_BREAK (char *)(-1) /* funny failure value */
|
||||
#endif
|
||||
extern char *SBRK();
|
||||
#ifdef STORE
|
||||
#define MAX_STORE 32
|
||||
private do_free(), sell_out();
|
||||
privatedata mallink *store[MAX_STORE];
|
||||
#endif STORE
|
||||
|
||||
char *
|
||||
malloc(n)
|
||||
register unsigned int n;
|
||||
{check_mallinks("malloc entry");{
|
||||
register mallink *ml;
|
||||
register int min_class;
|
||||
|
||||
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);
|
||||
check_mallinks("malloc fast exit");
|
||||
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 unsigned int n1 = n;
|
||||
|
||||
assert(n1 < (1 << LOG_MAX_SIZE));
|
||||
min_class = 0;
|
||||
|
||||
while (n1 >= MIN_SIZE) {
|
||||
n1 >>= 1;
|
||||
min_class++;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_class >= MAX_FLIST)
|
||||
return (char *) 0; /* we don't deal in blocks that big */
|
||||
ml = first_present(min_class);
|
||||
if (ml == MAL_NULL) {
|
||||
/* Try and extend */
|
||||
register char *p;
|
||||
#define GRABSIZE 4096 /* Power of 2 */
|
||||
register unsigned int req =
|
||||
(n+mallink_size()+GRABSIZE-1)&~(GRABSIZE-1);
|
||||
|
||||
if (!ml_last) {
|
||||
/* first align SBRK() */
|
||||
|
||||
p = SBRK(0);
|
||||
SBRK((int) (align((size_type) p) - (size_type) p));
|
||||
}
|
||||
|
||||
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 (char *) 0;
|
||||
started_working_on(ml);
|
||||
unlink_free_chunk(ml);
|
||||
check_mallinks("suitable_chunk, forced");
|
||||
#ifdef STORE
|
||||
}
|
||||
else started_working_on(ml);
|
||||
#endif STORE
|
||||
}
|
||||
else {
|
||||
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");
|
||||
return block_of_mallink(ml);
|
||||
}}
|
||||
|
||||
free(addr)
|
||||
char *addr;
|
||||
{check_mallinks("free entry");{
|
||||
register mallink *ml = mallink_of_block(addr);
|
||||
#ifdef STORE
|
||||
|
||||
if (free_of(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;
|
||||
check_mallinks("free fast exit");
|
||||
}
|
||||
else {
|
||||
do_free(ml);
|
||||
check_mallinks("free exit");
|
||||
}
|
||||
}}
|
||||
|
||||
private
|
||||
do_free(ml)
|
||||
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(char *) != 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.
|
||||
*/
|
||||
}
|
||||
}}
|
||||
|
||||
char *
|
||||
realloc(addr, n)
|
||||
char *addr;
|
||||
register unsigned int n;
|
||||
{check_mallinks("realloc entry");{
|
||||
register mallink *ml = mallink_of_block(addr), *ph_next;
|
||||
register unsigned int size;
|
||||
|
||||
if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n);
|
||||
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 */
|
||||
char *new;
|
||||
register char *l1, *l2 = addr;
|
||||
|
||||
stopped_working_on(ml);
|
||||
if (!(new = l1 = malloc(n))) return (char *) 0; /* no way */
|
||||
while (size--) *l1++ = *l2++;
|
||||
free(addr);
|
||||
check_work_empty("mv_realloc");
|
||||
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");
|
||||
return addr;
|
||||
}}
|
||||
|
||||
/* Auxiliary routines */
|
||||
|
||||
#ifdef STORE
|
||||
private
|
||||
sell_out() {
|
||||
/* 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);
|
||||
do_free(ml);
|
||||
ml = *stp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif STORE
|
||||
|
||||
#ifdef ASSERT
|
||||
public
|
||||
m_assert(fn, ln)
|
||||
char *fn;
|
||||
{
|
||||
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
|
51
modules/src/malloc/param.h
Normal file
51
modules/src/malloc/param.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#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.
|
||||
*/
|
||||
|
||||
/*# define 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.
|
||||
*/
|
||||
|
||||
/*# define 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.
|
||||
*/
|
||||
|
||||
/*# define 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.
|
||||
*/
|
||||
|
||||
# define 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
|
93
modules/src/malloc/phys.c
Normal file
93
modules/src/malloc/phys.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
#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(p, n)
|
||||
char *p;
|
||||
unsigned int 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 *)(p + mallink_size()); /* bump 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(ml, size)
|
||||
register mallink *ml;
|
||||
unsigned int 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);
|
||||
|
||||
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(ml1, ml2)
|
||||
register mallink *ml1, *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;
|
||||
}
|
61
modules/src/malloc/phys.h
Normal file
61
modules/src/malloc/phys.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* Algorithms to manipulate the doubly-linked list of physical
|
||||
chunks.
|
||||
*/
|
||||
publicdata mallink *ml_last;
|
||||
|
||||
#define __free_of(ml) ((size_type)_phys_prev_of(ml) & 01)
|
||||
#define __phys_prev_of(ml) (mallink *)((size_type)_phys_prev_of(ml) & ~01)
|
||||
#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 + __free_of(ml)))
|
||||
|
||||
#ifdef CHECK
|
||||
public Error();
|
||||
#define phys_prev_of(ml) (mallink *) \
|
||||
(first_mallink(ml) ? \
|
||||
(char *)Error("phys_prev_of first_mallink %ld", "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) = (unsigned int)((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) | 01 : \
|
||||
(size_type) _phys_prev_of(ml) & ~01))
|
||||
#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(), combine_chunks();
|
||||
public mallink *create_chunk();
|
Loading…
Reference in a new issue