/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 | } |