xref: /original-bsd/lib/libc/stdlib/malloc.c (revision 4b2c5e10)
1 #ifndef lint
2 static char sccsid[] = "@(#)malloc.c	4.1 (Berkeley) 06/22/83";
3 #endif
4 #ifdef debug
5 #define ASSERT(p) if(!(p))botch("p");else
6 botch(s)
7 char *s;
8 {
9 	printf("assertion botched: %s\n",s);
10 	abort();
11 }
12 #else
13 #define ASSERT(p)
14 #endif
15 
16 /*	avoid break bug */
17 #ifdef pdp11
18 #define GRANULE 64
19 #else
20 #define GRANULE 0
21 #endif
22 /*	C storage allocator
23  *	circular first-fit strategy
24  *	works with noncontiguous, but monotonically linked, arena
25  *	each block is preceded by a ptr to the (pointer of)
26  *	the next following block
27  *	blocks are exact number of words long
28  *	aligned to the data type requirements of ALIGN
29  *	pointers to blocks must have BUSY bit 0
30  *	bit in ptr is 1 for busy, 0 for idle
31  *	gaps in arena are merely noted as busy blocks
32  *	last block of arena (pointed to by alloct) is empty and
33  *	has a pointer to first
34  *	idle blocks are coalesced during space search
35  *
36  *	a different implementation may need to redefine
37  *	ALIGN, NALIGN, BLOCK, BUSY, INT
38  *	where INT is integer type to which a pointer can be cast
39 */
40 #define INT int
41 #define ALIGN int
42 #define NALIGN 1
43 #define WORD sizeof(union store)
44 #define BLOCK 1024	/* a multiple of WORD*/
45 #define BUSY 1
46 #define NULL 0
47 #define testbusy(p) ((INT)(p)&BUSY)
48 #define setbusy(p) (union store *)((INT)(p)|BUSY)
49 #define clearbusy(p) (union store *)((INT)(p)&~BUSY)
50 
51 union store { union store *ptr;
52 	      ALIGN dummy[NALIGN];
53 	      int calloc;	/*calloc clears an array of integers*/
54 };
55 
56 static	union store allocs[2];	/*initial arena*/
57 static	union store *allocp;	/*search ptr*/
58 static	union store *alloct;	/*arena top*/
59 static	union store *allocx;	/*for benefit of realloc*/
60 char	*sbrk();
61 
62 char *
63 malloc(nbytes)
64 unsigned nbytes;
65 {
66 	register union store *p, *q;
67 	register nw;
68 	static temp;	/*coroutines assume no auto*/
69 
70 	if(allocs[0].ptr==0) {	/*first time*/
71 		allocs[0].ptr = setbusy(&allocs[1]);
72 		allocs[1].ptr = setbusy(&allocs[0]);
73 		alloct = &allocs[1];
74 		allocp = &allocs[0];
75 	}
76 	nw = (nbytes+WORD+WORD-1)/WORD;
77 	ASSERT(allocp>=allocs && allocp<=alloct);
78 	ASSERT(allock());
79 	for(p=allocp; ; ) {
80 		for(temp=0; ; ) {
81 			if(!testbusy(p->ptr)) {
82 				while(!testbusy((q=p->ptr)->ptr)) {
83 					ASSERT(q>p&&q<alloct);
84 					p->ptr = q->ptr;
85 				}
86 				if(q>=p+nw && p+nw>=p)
87 					goto found;
88 			}
89 			q = p;
90 			p = clearbusy(p->ptr);
91 			if(p>q)
92 				ASSERT(p<=alloct);
93 			else if(q!=alloct || p!=allocs) {
94 				ASSERT(q==alloct&&p==allocs);
95 				return(NULL);
96 			} else if(++temp>1)
97 				break;
98 		}
99 		temp = ((nw+BLOCK/WORD)/(BLOCK/WORD))*(BLOCK/WORD);
100 		q = (union store *)sbrk(0);
101 		if(q+temp+GRANULE < q) {
102 			return(NULL);
103 		}
104 		q = (union store *)sbrk(temp*WORD);
105 		if((INT)q == -1) {
106 			return(NULL);
107 		}
108 		ASSERT(q>alloct);
109 		alloct->ptr = q;
110 		if(q!=alloct+1)
111 			alloct->ptr = setbusy(alloct->ptr);
112 		alloct = q->ptr = q+temp-1;
113 		alloct->ptr = setbusy(allocs);
114 	}
115 found:
116 	allocp = p + nw;
117 	ASSERT(allocp<=alloct);
118 	if(q>allocp) {
119 		allocx = allocp->ptr;
120 		allocp->ptr = p->ptr;
121 	}
122 	p->ptr = setbusy(allocp);
123 	return((char *)(p+1));
124 }
125 
126 /*	freeing strategy tuned for LIFO allocation
127 */
128 free(ap)
129 register char *ap;
130 {
131 	register union store *p = (union store *)ap;
132 
133 	ASSERT(p>clearbusy(allocs[1].ptr)&&p<=alloct);
134 	ASSERT(allock());
135 	allocp = --p;
136 	ASSERT(testbusy(p->ptr));
137 	p->ptr = clearbusy(p->ptr);
138 	ASSERT(p->ptr > allocp && p->ptr <= alloct);
139 }
140 
141 /*	realloc(p, nbytes) reallocates a block obtained from malloc()
142  *	and freed since last call of malloc()
143  *	to have new size nbytes, and old content
144  *	returns new location, or 0 on failure
145 */
146 
147 char *
148 realloc(p, nbytes)
149 register union store *p;
150 unsigned nbytes;
151 {
152 	register union store *q;
153 	union store *s, *t;
154 	register unsigned nw;
155 	unsigned onw;
156 
157 	if(testbusy(p[-1].ptr))
158 		free((char *)p);
159 	onw = p[-1].ptr - p;
160 	q = (union store *)malloc(nbytes);
161 	if(q==NULL || q==p)
162 		return((char *)q);
163 	s = p;
164 	t = q;
165 	nw = (nbytes+WORD-1)/WORD;
166 	if(nw<onw)
167 		onw = nw;
168 	while(onw--!=0)
169 		*t++ = *s++;
170 	if(q<p && q+nw>=p)
171 		(q+(q+nw-p))->ptr = allocx;
172 	return((char *)q);
173 }
174 
175 #ifdef debug
176 allock()
177 {
178 #ifdef longdebug
179 	register union store *p;
180 	int x;
181 	x = 0;
182 	for(p= &allocs[0]; clearbusy(p->ptr) > p; p=clearbusy(p->ptr)) {
183 		if(p==allocp)
184 			x++;
185 	}
186 	ASSERT(p==alloct);
187 	return(x==1|p==allocp);
188 #else
189 	return(1);
190 #endif
191 }
192 #endif
193