summaryrefslogtreecommitdiffstats
path: root/bitmap.c
blob: cdc1b46c0d11ae6b52ce0384ff15143ba6fcbcc5 (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
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
158
159
160
161
162
163
164
165
166
167
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include "bitmap.h"

#define BITMAP_MASK_FULL (~((bmcell_t)0))

#define BITMAP_CELL_SIZE (sizeof(bmcell_t) * 8)
#define BITMAP_CELL_SHIFT 6
#define BITMAP_SUBCELL_SHIFT 3

#define BITMAP_TEST(map, i) (map[i >> BITMAP_CELL_SHIFT] & (((bmcell_t)1) << i))
#define BITMAP_SET(map, i)  (map[i >> BITMAP_CELL_SHIFT] |= (((bmcell_t)1) << i))
#define BITMAP_USET(map, i) (map[i >> BITMAP_CELL_SHIFT] &= ~(((bmcell_t)1) << i))

void
bmvl_setup(struct bitmap_vl *bm, size_t bits)
{
    bm->head.length = bm->head.bits_free = bits;
    bm->head.last_free = 0;
    for(size_t i = 0; i < (bits / BITMAP_BITS_PER_CELL); i++) {
        bm->cells[i] = 0;
    }
}

bool 
__bm_test(bmcell_t *cells, size_t i)
{
    return BITMAP_TEST(cells, i) != 0;
}

bm_signed_t
__bm_take(struct bitmap_head *head, bmcell_t *cells)
{
    /*Definitely OOM*/
    if(head->bits_free == 0) return -1;
    /*If last_free is already free, we can just return that*/
    if(BITMAP_TEST(cells, head->last_free) == 0) {
        BITMAP_SET(cells, head->last_free);
        return head->last_free++;
    }

    /*Search all cells after last_free if last_free was taken. 
     * It could mean we're OOM, or that we've missed a gap somewhere*/
    for(size_t cell = (head->last_free >> BITMAP_CELL_SHIFT);
            cell < (head->length >> BITMAP_CELL_SHIFT);
            cell++)
    {
        /*A full bitmask means that every bit in the cell is taken*/
        if(cells[cell] == BITMAP_MASK_FULL) continue;

        /*We sub-divide the search into bytes, so that we can take advantage
         * of mask searches*/
        uint8_t *subcells = (uint8_t*)(&cells[cell]);
        for(uint8_t subcell = 0; subcell < 8; subcell++) {
            /*If the byte is full, we search the next byte*/
            if(subcells[subcell] == 0xFF) continue;
            /*A per-bit search is necessary to find the exact free bit*/
            for(uint8_t bit = 0; bit < 8; bit++) {
                if(subcells[subcell] & (1 << bit)) continue;
                /*Mark the bit as taken*/
                subcells[subcell] |= ((bmcell_t)1 << bit);

                /*Because we're operating on pow2, we can use bitshifts to
                 * simplify 2^x.
                 * Since subcell and bit can only be 1-7, we can OR it into
                 * the shifted cell value*/
                head->last_free =
                    (cell << BITMAP_CELL_SHIFT) |
                    (subcell << BITMAP_SUBCELL_SHIFT) |
                    bit;
                /*This returns the last_free, then increments so that the next
                 * call can (possibly) get a free value*/
                head->bits_free--;
                return head->last_free++;
            }
        }
    }
    /*Getting here means the bitmap couldn't find a free bit and we are
     * presumably OOM*/
    return -1;
}

bm_signed_t
__bm_take_contig(struct bitmap_head *head, bmcell_t *cells, size_t l)
{
    /*If the bitmap doesn't have enough free bits for the span, it will never
     * have enough contiguous bits*/
    if(head->bits_free < l) return -1;
    /*Simul completion flag and utility value for calculating offsets for return
     * value*/
    size_t left = l;
    for(size_t cell = (head->last_free >> BITMAP_CELL_SHIFT);
            cell < (head->length >> BITMAP_CELL_SHIFT);
            cell++)
    {
        /*Return case for breakout when we find the contig span*/
        if(left == 0) break;
        /*If the bitmask is full, there won't be room to fit the requested span*/
        if(cells[cell] == BITMAP_MASK_FULL) {
            left = l;
            continue;
        }
        /*If the bitmask is 0, there are atleast BITMAP_CELL_SIZE contig bits
         * in the bitmap starting here*/
        if(cells[cell] == 0) {
            /*Only continue the loop if there aren't enough bits in this current
             * cell.*/
            if(left > BITMAP_CELL_SIZE) {
                left -= BITMAP_CELL_SIZE;
                continue;
            }
            /*Because there was room for the rest of the span, we can return
             * this address without worrying about fetching other bits.
             * The cell index is subtracted by the inverse of left*/
            head->last_free = 
                (cell << BITMAP_CELL_SHIFT) - (l - left);
            /*Mark left so that we break out of this loop and call the proper
             * return function*/
            left = 0;
            break;
        }
        /*Divide search into bytes for faster search of bmcell_t structure*/
        uint8_t *subcells = (uint8_t*)(&cells[cell]);
        for(uint8_t subcell = 0; subcell < 8; subcell++) {
            if(left == 0) break;
            /*Subcell is full, we refill left and stop searching this subcell*/
            if(subcells[subcell] == 0xFF) {
                left = l;
                continue;
            }
            /*Since the subcell is empty, there are atleast 8 contig bits*/
            if(subcells[subcell] == 0) {
                /*If we need more than 8 bits, we subtract 8 from left and
                 * continue*/
                if(left > 8) {
                    left -= 8;
                    continue;
                }
                /*Otherwise, we return the index of this particular subcell
                 * minus the inverse of left*/
                head->last_free = 
                    ((cell << BITMAP_CELL_SHIFT) |
                     (subcell << BITMAP_SUBCELL_SHIFT)) -
                    (l - left);
                /*Mark left for proper exit and return*/
                left = 0;
                break;
            }
            /*Search the entire subcell for free bits.*/
            for(uint8_t bit = 0; bit < 8; bit++) {
                /*When we find a taken bit, and there are still bits to fetch,
                 * then the block will never be contiguous and we need to
                 * start again*/
                if(subcells[subcell] & (1 << bit)) {
                    left = l;
                    continue;
                }
                left--;
                if(left == 0) {
                    /*When we have found all the bits we need, we return the
                     * index to the start of the contig span*/
                    head->last_free =
                        ((cell << BITMAP_CELL_SHIFT) |
                         (subcell << BITMAP_SUBCELL_SHIFT) |
                         bit) -
                        (l - left);
                    break;
                }
            }
        }
    }
    /*We didn't find a contiguous span. Either the bitmap is fragmented or
     * there's not enough free space to fit the span*/
    if(left != 0) return -1;

    /*Mark all of the bits in the span as taken*/
    for(size_t i = 0; i < l; i++) {
        BITMAP_SET(cells, (head->last_free + i));
    }
    /*Setup to return last_free, while incrementing last_free by l*/
    head->last_free += l;
    head->bits_free -= l;
    return head->last_free - l;
    
}

void
__bm_reserve(struct bitmap_head *head, bmcell_t *cells, size_t i)
{
    /*Take the bit at i and update the bits_free counter*/
    BITMAP_SET(cells, i);
    head->bits_free--;
}

void
__bm_reserve_contig(
        struct bitmap_head *head, 
        bmcell_t *cells, 
        size_t i, 
        size_t l)
{
    /*For every bit between i and i+l, mark as reserved*/
    head->bits_free -= l;
    for(; l != 0; l--, i++) {
        BITMAP_SET(cells, i);
    }
}

void
__bm_release(struct bitmap_head *head, bmcell_t *cells, size_t i)
{
    /*Release the bit at i, update last_free, and update the bits_free counter*/
    BITMAP_USET(cells, i);
    if(head->last_free > i) head->last_free = i;
    head->bits_free++;
}

void 
__bm_release_contig(
        struct bitmap_head *head, 
        bmcell_t *cells, 
        size_t i, 
        size_t l)
{
    /*Update the last_free and bits_free counters*/
    head->bits_free += i;
    if(head->last_free > i) head->last_free = i;
    /*For every bit between i and i+l, mark as free*/
    for(; l != 0; l--, i++) {
        BITMAP_USET(cells, i);
    }
}