summaryrefslogtreecommitdiffstats
path: root/memory/alloc/free.c
blob: e2cc0600292e053512caf6feb1354afee0049ee8 (plain) (blame)
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
/*
 * Copyright (c) Jon Santmyer.
 * This source file is released under the LGPL Version 3 as detailed in
 * the LICENSE file provided in the following repository:
 * https://git.jonsantmyer.com/heap/
 * */

#include "malloc.h"
#include "assert.h"

bucket_t*
__bucket_from_obj(heap_cache_t *cache, bucket_obj_t *obj)
{
    assert(cache != NULL);
    assert(cache->head != NULL);
    /* Buckets are always aligned to the nearest page boundary, and that the 
     * metadata is always below the object. We use this fact to quickly find 
     * owning buckets.*/
    uintptr_t objptr = (uintptr_t)obj;
    uintptr_t bktptr = objptr & ~0xFFF;
    while(1) {
        /* If the page boundary is below the head, we are below the heap memory
         * region and can stop.*/
        if(bktptr < (uintptr_t)cache->head) return NULL;

        /* At each page boundary below the object, check for the heap checksum.
         * If it is found, we can assume that we found a bucket.*/
        bucket_t *bucket = (bucket_t*)bktptr;
        if(OBJ_VALID(bucket)) {
            if(bucket->limit > objptr) return NULL;
            return bucket;
        }

        bktptr -= BUCKET_ALIGN;
    }
}

void
__free_impl(heap_cache_t *cache, void *ptr)
{
    DBGPRINT("free: asking to free ptr %p\n", ptr);
    assert(cache != NULL);
    /* free(NULL) does nothing.*/
    if(ptr == NULL) return;

    /* Make sure the pointer actually comes from the heap.*/
    assert((uintptr_t)ptr < (uintptr_t)cache->tail);
    assert((uintptr_t)ptr > (uintptr_t)cache->head);
    bucket_obj_t *obj = OBJ_FROM_PTR(ptr);
    assert(OBJ_VALID(obj));

    /* Mask the taken bit in the object to mark it free.*/
    DBGPRINT("free: masking taken bit in object %p (was %i, now ", 
            obj, obj->size_taken);
    obj->size_taken &= ~1;
    DBGPRINT("%i)\n", obj->size_taken);

    /* If the buckets' lastfree is greater than this new free object, this 
     * object will be the lastfree.*/
    bucket_t *bucket = __bucket_from_obj(cache, obj);
    if(bucket != NULL && bucket->firstfree > obj)
        bucket->firstfree = obj;

    /* Start trying to merge this free object with the next object.
     * If the object after is taken, do nothing.*/
    bucket_obj_t *after = OBJ_AFTER(obj);    
    if((uintptr_t)after > bucket->limit) return;
    assert(OBJ_VALID(after));
    if(OBJ_TAKEN(after)) return;
    
    /* Merge with the next object.
     * from:
     * [meta| -objw-][meta| -afterw-]
     * to:
     * [meta| ---------objw---------]*/
    DBGPRINT("free: obj %p merging with adjacent free object %p (was %i, now \n", 
            obj, after, obj->size_taken);
    obj->size_taken += sizeof(bucket_obj_t) + after->size_taken;
    DBGPRINT("%i)\n", obj->size_taken);
}