Coverage Report

Created: 2024-05-03 06:05

/builds/xfbs/passgen/src/container/stack.c
Line
Count
Source
1
#include "passgen/container/stack.h"
2
#include "passgen/assert.h"
3
#include <stdlib.h>
4
#include <string.h>
5
6
static const size_t passgen_stack_bin_size = 32;
7
static const size_t passgen_stack_cap_init = 4;
8
9
103k
static size_t passgen_stack_bins(const passgen_stack *stack) {
10
103k
    return (stack->len + stack->bin_size - 1) / stack->bin_size;
11
103k
}
12
13
170k
static size_t passgen_stack_bin(const passgen_stack *stack, size_t pos) {
14
170k
    return pos / stack->bin_size;
15
170k
}
16
17
227k
static size_t passgen_stack_bin_count(const passgen_stack *stack, size_t len) {
18
227k
    (void) stack;
19
227k
    size_t power = 1;
20
366k
    while(power < len) {
21
139k
        power *= 2;
22
139k
    }
23
227k
    return power;
24
227k
}
25
26
// Initialize this stack.
27
103k
void passgen_stack_init(passgen_stack *stack, size_t element_size) {
28
103k
    memset(stack, 0, sizeof(*stack));
29
103k
    stack->element_size = element_size;
30
103k
    stack->bin_size = passgen_stack_bin_size;
31
103k
}
32
33
// Delete the memory for this stack.
34
103k
void passgen_stack_free(passgen_stack *stack) {
35
103k
    size_t bins = passgen_stack_bins(stack);
36
203k
    for(size_t i = 0; i < bins; 
i++99.9k
) {
37
99.9k
        free(stack->data[i]);
38
99.9k
    }
39
103k
40
103k
    if(stack->len) {
41
99.9k
        free(stack->data);
42
99.9k
    }
43
103k
44
103k
    memset(stack, 0, sizeof(*stack));
45
103k
}
46
47
// Execute a function for every item on the stack.
48
void passgen_stack_foreach(
49
    const passgen_stack *stack,
50
3
    void (*func)(void *element)) {
51
3
    size_t full_bins = stack->len / stack->bin_size;
52
10
    for(size_t i = 0; i < full_bins; 
i++7
) {
53
7
        void *bin = stack->data[i];
54
231
        for(size_t j = 0; j < stack->bin_size; 
j++224
) {
55
224
            func(bin + j * stack->element_size);
56
224
        }
57
7
    }
58
3
    size_t last_bin_items = stack->len % stack->bin_size;
59
33
    for(size_t i = 0; i < last_bin_items; 
i++30
) {
60
30
        void *bin = stack->data[full_bins];
61
30
        func(bin + i * stack->element_size);
62
30
    }
63
3
}
64
65
// Push a new value onto this stack. If a non-NULL pointer is passed, it is
66
// copied to the new value. A pointer to the value on the stack is returned.
67
169k
void *passgen_stack_push(passgen_stack *stack, void *value) {
68
169k
    size_t new_bin = passgen_stack_bin(stack, stack->len);
69
169k
70
169k
    // initially allocate data
71
169k
    if(!new_bin) {
72
168k
        if(!stack->len) {
73
100k
            stack->data = calloc(1, sizeof(void *));
74
100k
            stack->data[0] =
75
100k
                calloc(passgen_stack_cap_init, stack->element_size);
76
100k
        }
77
168k
        if(passgen_stack_bin_count(stack, stack->len) == stack->len) {
78
57.4k
            size_t new_size = passgen_stack_bin_count(stack, stack->len + 1);
79
57.4k
            stack->data[0] =
80
57.4k
                realloc(stack->data[0], new_size * stack->element_size);
81
57.4k
        }
82
168k
    } else {
83
1.02k
        size_t max_bin = passgen_stack_bin(stack, stack->len - 1);
84
1.02k
85
1.02k
        // do we need to increase the bins?
86
1.02k
        if(new_bin != max_bin) {
87
33
            size_t current_bins = passgen_stack_bin_count(stack, new_bin);
88
33
            size_t needed_bins = passgen_stack_bin_count(stack, new_bin + 1);
89
33
            if(current_bins != needed_bins) {
90
16
                void **new_data =
91
16
                    realloc(stack->data, needed_bins * sizeof(void *));
92
16
                passgen_assert(new_data);
93
16
                stack->data = new_data;
94
16
            }
95
33
            stack->data[new_bin] = calloc(stack->bin_size, stack->element_size);
96
33
        }
97
1.02k
    }
98
169k
99
169k
    size_t offset = stack->len % stack->bin_size;
100
169k
    void *data = stack->data[new_bin] + offset * stack->element_size;
101
169k
102
169k
    stack->len += 1;
103
169k
    if(value) {
104
1.24k
        memcpy(data, value, stack->element_size);
105
1.24k
    }
106
169k
107
169k
    return data;
108
169k
}
109
110
// Get the nth element on the stack. If the position is invalid, returns NULL.
111
1.02M
void *passgen_stack_get(const passgen_stack *stack, size_t pos) {
112
1.02M
    if(stack->len <= pos) {
113
687
        return NULL;
114
687
    }
115
1.02M
116
1.02M
    size_t bin = pos / stack->bin_size;
117
1.02M
    size_t offset = pos % stack->bin_size;
118
1.02M
    return stack->data[bin] + offset * stack->element_size;
119
1.02M
}
120
121
// Pop the largest element off the stack. If a non-NULL pointer is passed,
122
// the element's memory is copied into that value, and the passed pointer
123
// is returned. If the stack is empty, a NULL pointer is returned.
124
1.23k
void *passgen_stack_pop(passgen_stack *stack, void *element) {
125
1.23k
    if(!stack->len) {
126
1
        return NULL;
127
1
    }
128
1.23k
129
1.23k
    if(element) {
130
254
        void *item = passgen_stack_top(stack);
131
254
        memcpy(element, item, stack->element_size);
132
254
    }
133
1.23k
134
1.23k
    if(stack->len == 1) {
135
115
        free(stack->data[0]);
136
115
        stack->data[0] = NULL;
137
115
        free(stack->data);
138
115
        stack->data = NULL;
139
1.12k
    } else if(stack->len <= stack->bin_size) {
140
934
        size_t entry_size = passgen_stack_bin_count(stack, stack->len);
141
934
        size_t new_size = passgen_stack_bin_count(stack, stack->len - 1);
142
934
        if(entry_size != new_size) {
143
876
            stack->data[0] =
144
876
                realloc(stack->data[0], new_size * stack->element_size);
145
876
        }
146
934
    } else {
147
189
        size_t max_bin = passgen_stack_bin(stack, stack->len - 1);
148
189
        size_t new_bin = passgen_stack_bin(stack, stack->len - 2);
149
189
        if(max_bin != new_bin) {
150
6
            free(stack->data[max_bin]);
151
6
            stack->data[max_bin] = NULL;
152
6
        }
153
189
    }
154
1.23k
155
1.23k
    stack->len -= 1;
156
1.23k
157
1.23k
    return element;
158
1.23k
}
159
160
// Returns the topmost element of the stack, or NULL if the stack is empty.
161
913k
void *passgen_stack_top(const passgen_stack *stack) {
162
913k
    return passgen_stack_get(stack, stack->len > 0 ? 
stack->len - 1913k
:
0687
);
163
913k
}