memheap.c 9.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * File      : memheap.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2012, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2012-04-10     Bernard      first implementation
 */
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
#include <rtthread.h>

#ifdef RT_USING_MEMHEAP

/* dynamic pool magic and mask */
#define RT_MEMHEAP_MAGIC		0x1ea01ea0
#define RT_MEMHEAP_MASK		0xfffffffe
#define RT_MEMHEAP_USED		0x01
#define RT_MEMHEAP_FREED		0x00

#define RT_MEMHEAP_IS_USED(i)	((i)->magic & RT_MEMHEAP_USED)
#define RT_MEMHEAP_MINIALLOC	12

#define RT_MEMHEAP_SIZE		RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE)

/*
 * The initialized memory pool will be:
 * +-----------------------------------+--------------------------+
 * | whole freed memory block          | Used Memory Block Tailer |
 * +-----------------------------------+--------------------------+
 *
 * block_list --> whole freed memory block
 *
 * The length of Used Memory Block Tailer is 0, which is prevents block merging across list
 */
40
rt_err_t rt_memheap_init(struct rt_memheap *memheap, const char *name,
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
	void *start_addr,
	rt_uint32_t size)
{
	struct rt_memheap_item *item;

	RT_ASSERT(memheap != RT_NULL);

	/* initialize pool object */
	rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name);

	memheap->start_addr = start_addr;
	memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
    memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);

	/* initialize the free list header */
	item = &(memheap->free_header);
57 58 59 60 61 62
	item->magic = RT_MEMHEAP_MAGIC;
	item->pool_ptr = memheap;
	item->next = RT_NULL;
	item->prev = RT_NULL;
	item->next_free = item;
	item->prev_free = item;
63 64 65 66 67

	/* set the free list to free list header */
	memheap->free_list = item;

	/* initialize the first big memory block */
68 69 70 71 72 73 74 75 76 77 78
	item = (struct rt_memheap_item *)start_addr;
	item->magic = RT_MEMHEAP_MAGIC;
	item->pool_ptr = memheap;
	item->next = RT_NULL;
	item->prev = RT_NULL;
	item->next_free = item;
	item->prev_free = item;

	item->next = (struct rt_memheap_item *)
		((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE);
	item->prev =  item->next;
79 80 81 82 83 84 85 86 87 88 89

	/* block list header */
	memheap->block_list = item;

	/* place the big memory block to free list */
	item->next_free = memheap->free_list->next_free;
	item->prev_free = memheap->free_list;
	memheap->free_list->next_free->prev_free = item;
	memheap->free_list->next_free = item;

	/* move to the end of memory pool to build a small tailer block, which prevents block merging */
90
	item =  item->next;
91
	/* it's a used memory block */
92 93 94 95
	item->magic = RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED;
	item->pool_ptr = memheap;
	item->next = (struct rt_memheap_item *)start_addr;
	item->prev = (struct rt_memheap_item *)start_addr;
96
	/* not in free list */
97
	item->next_free = item->prev_free = RT_NULL;
98

99 100
	RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x",
    		start_addr, size, &(memheap->free_header)));
101

102
	return RT_EOK;
103
}
104
RTM_EXPORT(rt_memheap_init);
105

106
rt_err_t rt_memheap_detach(struct rt_memheap *heap)
107 108 109
{
	rt_object_detach(&(heap->parent));

110 111
	/* Return a successful completion. */
	return RT_EOK;
112
}
113
RTM_EXPORT(rt_memheap_detach);
114

115
void *rt_memheap_alloc(struct rt_memheap *pool_ptr, rt_uint32_t size)
116 117 118 119 120 121 122 123
{
	rt_uint32_t free_size;
	struct rt_memheap_item *header_ptr;

	RT_ASSERT(pool_ptr != RT_NULL);

	/* align allocated size */
	size = RT_ALIGN(size, RT_ALIGN_SIZE);
124 125
	if (size < RT_MEMHEAP_MINIALLOC)
		size = RT_MEMHEAP_MINIALLOC;
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

	RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d", size));

	if (size < pool_ptr->available_size)
	{
		/* search on free list */
		free_size = 0;
		/* get the first free memory block */
		header_ptr = pool_ptr->free_list->next_free;
		while (header_ptr != pool_ptr->free_list && free_size < size)
		{
			/* get current freed memory block size */
			free_size = (rt_uint32_t)(header_ptr->next) - (rt_uint32_t)header_ptr - RT_MEMHEAP_SIZE;

			if (free_size < size)
			{
				/* move to next free memory block */
				header_ptr = header_ptr->next_free;
			}
		}

147
		/* determine if the memory is available. */
148 149
		if (free_size >= size)
		{
150
			/* a block that satisfies the request has been found. */
151

152
			/* determine if the block needs to be split. */
153 154
			if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC))
			{
155
				struct rt_memheap_item *new_ptr;
156

157 158
				/* split the block. */
				new_ptr =  (struct rt_memheap_item *)(((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);
159 160 161 162 163 164 165 166

				RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("split: h[0x%08x] nm[0x%08x] pm[0x%08x] to n[0x%08x]", header_ptr,
					header_ptr->next, header_ptr->prev,
					new_ptr));

				/* mark the new block as a memory block and freed. */
				new_ptr->magic = RT_MEMHEAP_MAGIC;

167
				/* put the pool pointer into the new block. */
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
				new_ptr->pool_ptr = pool_ptr;

				/* break down the block list */
				new_ptr->prev = header_ptr;
				new_ptr->next = header_ptr->next;
				header_ptr->next->prev = new_ptr;
				header_ptr->next = new_ptr;

				/* remove header ptr from free list */
				header_ptr->next_free->prev_free = header_ptr->prev_free;
				header_ptr->prev_free->next_free = header_ptr->next_free;
				header_ptr->next_free = RT_NULL;
				header_ptr->prev_free = RT_NULL;

				/* insert new_ptr to free list */
				new_ptr->next_free = pool_ptr->free_list->next_free;
				new_ptr->prev_free = pool_ptr->free_list;
				pool_ptr->free_list->next_free->prev_free = new_ptr;
				pool_ptr->free_list->next_free = new_ptr;
				RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: nf 0x%08x, pf 0x%08x",
						new_ptr->next_free, new_ptr->prev_free));

				/* decrement the available byte count.  */
				pool_ptr->available_size = pool_ptr->available_size - size - RT_MEMHEAP_SIZE;
			}
			else
			{
				/* decrement the entire free size from the available bytes count.  */
196
				pool_ptr->available_size = pool_ptr->available_size - free_size;
197 198 199 200 201 202 203 204 205 206 207 208

				/* remove header_ptr from free list */
				RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("one block: h[0x%08x], nf 0x%08x, pf 0x%08x", header_ptr,
					header_ptr->next_free, header_ptr->prev_free));

				header_ptr->next_free->prev_free = header_ptr->prev_free;
				header_ptr->prev_free->next_free = header_ptr->next_free;
				header_ptr->next_free = RT_NULL;
				header_ptr->prev_free = RT_NULL;
			}

			/* Mark the allocated block as not available.  */
209
			header_ptr->magic |= RT_MEMHEAP_USED;
210 211 212

			/* Return a memory address to the caller.  */
			RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("am: m[0x%08x], h[0x%08x], size: %d",
213 214 215
					(void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE), header_ptr, size);

			return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE));
216 217 218 219 220 221 222 223
		}
	}

	RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n"));

    /* Return the completion status.  */
    return RT_NULL;
}
224
RTM_EXPORT(rt_memheap_alloc);
225

226
void rt_memheap_free(void *ptr)
227 228 229 230 231 232
{
	struct rt_memheap *pool_ptr;
	struct rt_memheap_item *header_ptr, *new_ptr;
	rt_uint32_t insert_header;

	/* set initial status as OK */
233 234 235
	insert_header = 1;
	new_ptr = RT_NULL;
	header_ptr = (struct rt_memheap_item *)((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
236 237 238 239 240 241 242 243 244

	RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: m[0x%08x], h[0x%08x]", ptr, header_ptr));

	/* check magic */
	RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);

	/* get pool ptr */
	pool_ptr = header_ptr->pool_ptr;

245 246
	/* Mark the memory as available. */
	header_ptr->magic &= ~RT_MEMHEAP_USED;
247

248 249 250 251
	/* Adjust the available number of bytes. */
	pool_ptr->available_size =  pool_ptr->available_size +
			((rt_uint32_t)(header_ptr->next) -
			(rt_uint32_t)header_ptr) - RT_MEMHEAP_SIZE;
252

253 254 255 256
	/* Determine if the block can be merged with the previous neighbor. */
	if (!RT_MEMHEAP_IS_USED(header_ptr->prev))
	{
		RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: left node 0x%08x", header_ptr->prev));
257

258 259
		/* adjust the available number of bytes. */
		pool_ptr->available_size = pool_ptr->available_size + RT_MEMHEAP_SIZE;
260

261 262 263
		/* yes, merge block with previous neighbor. */
		(header_ptr->prev)->next = header_ptr->next;
		(header_ptr->next)->prev = header_ptr->prev;
264

265 266 267 268 269
		/* move header pointer to previous. */
		header_ptr = header_ptr->prev;
		/* don't insert header to free list */
		insert_header = 0;
	}
270

271 272 273 274 275
	/* determine if the block can be merged with the next neighbor. */
	if (!RT_MEMHEAP_IS_USED(header_ptr->next))
	{
		/* adjust the available number of bytes. */
		pool_ptr->available_size =  pool_ptr->available_size + RT_MEMHEAP_SIZE;
276

277 278
		/* merge block with next neighbor. */
		new_ptr =  header_ptr->next;
279

280 281
		RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: right node 0x%08x, nf 0x%08x, pf 0x%08x",
				new_ptr, new_ptr->next_free, new_ptr->prev_free));
282

283 284
		new_ptr->next->prev = header_ptr;
		header_ptr->next = new_ptr->next;
285 286 287 288

		/* remove new ptr from free list */
		new_ptr->next_free->prev_free = new_ptr->prev_free;
		new_ptr->prev_free->next_free = new_ptr->next_free;
289
	}
290 291 292 293 294 295 296 297 298 299 300 301 302

	if (insert_header)
	{
		/* no left merge, insert to free list */
		header_ptr->next_free = pool_ptr->free_list->next_free;
		header_ptr->prev_free = pool_ptr->free_list;
		pool_ptr->free_list->next_free->prev_free = header_ptr;
		pool_ptr->free_list->next_free = header_ptr;

		RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("insert to free list: nf 0x%08x, pf 0x%08x",
				header_ptr->next_free, header_ptr->prev_free));
	}
}
303
RTM_EXPORT(rt_memheap_free);
304 305

#endif