summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/lib/libc/stdlib/malloc.c
blob: b2a3138ebdad0ec48c9e16630bfd8e896d10b174 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/


#include "stddef.h"
#include "stdlib.h"
#include "unistd.h"
#include "malloc_defs.h"


static int clean(void);


/* act points to the end of the initialized heap and the start of uninitialized heap */
static char *act;

/* Pointers to start and end of heap: */
static char *heap_start, *heap_end;


/*
 * Standard malloc function
 */
void *
malloc(size_t size)
{
	char *header;
	void *data;
	size_t blksize;         /* size of memory block including the chunk */

	blksize = size + sizeof(struct chunk);

	/* has malloc been called for the first time? */
	if (act == 0) {
		size_t initsize;
		/* add some space so we have a good initial playground */
		initsize = (blksize + 0x1000) & ~0x0fff;
		/* get initial memory region with sbrk() */
		heap_start = sbrk(initsize);
		if (heap_start == (void*)-1)
			return NULL;
		heap_end = heap_start + initsize;
		act = heap_start;
	}

	header = act;
	data = act + sizeof(struct chunk);

	/* Check if there is space left in the uninitialized part of the heap */
	if (act + blksize > heap_end) {
		//search at begin of heap
		header = heap_start;

		while ((((struct chunk *) header)->inuse != 0
		        || ((struct chunk *) header)->length < size)
		       && header < act) {
			header = header + sizeof(struct chunk)
			         + ((struct chunk *) header)->length;
		}

		// check if heap is full
		if (header >= act) {
			if (clean()) {
				// merging of free blocks succeeded, so try again
				return malloc(size);
			} else if (sbrk(blksize) == heap_end) {
				// succeeded to get more memory, so try again
				heap_end += blksize;
				return malloc(size);
			} else {
				// No more memory available
				return 0;
			}
		}

		// Check if we need to split this memory block into two
		if (((struct chunk *) header)->length > blksize) {
			//available memory is too big
			int alt;

			alt = ((struct chunk *) header)->length;
			((struct chunk *) header)->inuse = 1;
			((struct chunk *) header)->length = size;
			data = header + sizeof(struct chunk);

			//mark the rest of the heap
			header = data + size;
			((struct chunk *) header)->inuse = 0;
			((struct chunk *) header)->length =
			    alt - blksize;
		} else {
			//new memory matched exactly in available memory
			((struct chunk *) header)->inuse = 1;
			data = header + sizeof(struct chunk);
		}

	} else {

		((struct chunk *) header)->inuse = 1;
		((struct chunk *) header)->length = size;

		act += blksize;
	}

	return data;
}


/*
 * Merge free memory blocks in initialized heap if possible
 */
static int
clean(void)
{
	char *header;
	char *firstfree = 0;
	char check = 0;

	header = heap_start;
	//if (act == 0)		// This should never happen
	//	act = heap_end;

	while (header < act) {

		if (((struct chunk *) header)->inuse == 0) {
			if (firstfree == 0) {
				/* First free block in a row, only save address */
				firstfree = header;

			} else {
				/* more than one free block in a row, merge them! */
				((struct chunk *) firstfree)->length +=
				    ((struct chunk *) header)->length +
				    sizeof(struct chunk);
				check = 1;
			}
		} else {
			firstfree = 0;

		}

		header = header + sizeof(struct chunk)
		         + ((struct chunk *) header)->length;

	}

	return check;
}