summaryrefslogtreecommitdiffhomepage
path: root/dbmalloc.c
blob: e887d36474ad4c3ca232a803a239f402e98f9a7b (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
#include "dbmalloc.h"
#include "dbutil.h"

struct dbmalloc_header {
    unsigned int epoch;
    struct dbmalloc_header *prev;
    struct dbmalloc_header *next;
};

static void put_alloc(struct dbmalloc_header *header);
static void remove_alloc(struct dbmalloc_header *header);

/* end of the linked list */
static struct dbmalloc_header* staple;

unsigned int current_epoch = 0;

void m_malloc_set_epoch(unsigned int epoch) {
    current_epoch = epoch;
}

void m_malloc_free_epoch(unsigned int epoch, int dofree) {
    struct dbmalloc_header* header;
    struct dbmalloc_header* nextheader = NULL;
    struct dbmalloc_header* oldstaple = staple;
    staple = NULL;
    /* free allocations from this epoch, create a new staple-anchored list from
    the remainder */
    for (header = oldstaple; header; header = nextheader)
    {
        nextheader = header->next;
        if (header->epoch == epoch) {
            if (dofree) {
                free(header);
            }
        } else {
            header->prev = NULL;
            header->next = NULL;
            put_alloc(header);
        }
    }
}

static void put_alloc(struct dbmalloc_header *header) {
    assert(header->next == NULL);
    assert(header->prev == NULL);
    if (staple) {
        staple->prev = header;
    }
    header->next = staple;
    staple = header;
}

static void remove_alloc(struct dbmalloc_header *header) {
    if (header->prev) {
        header->prev->next = header->next;
    }
    if (header->next) {
        header->next->prev = header->prev;
    }
    if (staple == header) {
        staple = header->next;
    }
    header->prev = NULL;
    header->next = NULL;
}

static struct dbmalloc_header* get_header(void* ptr) {
    char* bptr = ptr;
    return (struct dbmalloc_header*)&bptr[-sizeof(struct dbmalloc_header)];
}

void * m_malloc(size_t size) {
    char* mem = NULL;
    struct dbmalloc_header* header = NULL;

    if (size == 0 || size > 1e9) {
        dropbear_exit("m_malloc failed");
    }

    size = size + sizeof(struct dbmalloc_header);

    mem = calloc(1, size);
    if (mem == NULL) {
        dropbear_exit("m_malloc failed");
    }
    header = (struct dbmalloc_header*)mem;
    put_alloc(header);
    header->epoch = current_epoch;
    return &mem[sizeof(struct dbmalloc_header)];
}

void * m_calloc(size_t nmemb, size_t size) {
    if (SIZE_T_MAX / nmemb < size) {
        dropbear_exit("m_calloc failed");
    }
    return m_malloc(nmemb*size);
}

void * m_realloc(void* ptr, size_t size) {
    char* mem = NULL;
    struct dbmalloc_header* header = NULL;
    if (size == 0 || size > 1e9) {
        dropbear_exit("m_realloc failed");
    }

    header = get_header(ptr);
    remove_alloc(header);

    size = size + sizeof(struct dbmalloc_header);
    mem = realloc(header, size);
    if (mem == NULL) {
        dropbear_exit("m_realloc failed");
    }

    header = (struct dbmalloc_header*)mem;
    put_alloc(header);
    return &mem[sizeof(struct dbmalloc_header)];
}

void m_free_direct(void* ptr) {
    struct dbmalloc_header* header = NULL;
    if (!ptr) {
        return;
    }
    header = get_header(ptr);
    remove_alloc(header);
    free(header);
}

void * m_strdup(const char * str) {
    char* ret;
    unsigned int len;
    len = strlen(str);

    ret = m_malloc(len+1);
    if (ret == NULL) {
        dropbear_exit("m_strdup failed");
    }
    memcpy(ret, str, len+1);
    return ret;
}