mindex.c (5469B)
1 #ifndef _GNU_SOURCE 2 #define _GNU_SOURCE 3 #endif 4 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include "mindex.h" 9 10 #ifndef MINDEX_SPARE 11 #define MINDEX_SPARE 16 12 #endif 13 14 int mindex_signal_found = 1; 15 int mindex_signal_over = 2; 16 int mindex_signal_under = 4; 17 int mindex_signal_empty = 8; 18 19 struct mindex_find_response { 20 int signal; 21 int index; 22 void *value; // NULL = not found 23 }; 24 25 struct mindex_t * mindex_init(int (*compare)(const void *a, const void *b, void *udata), void (*purge)(void *item, void *udata), void *udata) { 26 struct mindex_t *mindex = malloc(sizeof(struct mindex_t)); 27 mindex->compare = compare; 28 mindex->purge = purge; 29 mindex->udata = udata; 30 mindex->items = malloc(MINDEX_SPARE * sizeof(void*)); 31 mindex->length = 0; 32 mindex->max = MINDEX_SPARE; 33 return mindex; 34 } 35 36 struct mindex_find_response * mindex_find(const struct mindex_t *mindex, const void *pattern, void **items, int length) { 37 int acc, idx, newlength; 38 struct mindex_find_response *resp; 39 40 // Nothing to search = done 41 if (length <= 0) { 42 resp = malloc(sizeof(struct mindex_find_response)); 43 resp->signal = mindex_signal_empty; 44 resp->index = 0; 45 resp->value = NULL; 46 return resp; 47 } 48 49 // Make the actual comparison, splitting the array in 2 50 idx = length / 2; 51 acc = mindex->compare(pattern, items[idx], mindex->udata); 52 53 // Pattern = below middlepoint 54 if (acc < 0) { 55 56 // Limit deeper search to only the left side 57 newlength = idx; 58 59 // Nothing to our left, item not found 60 if (newlength <= 0) { 61 resp = malloc(sizeof(struct mindex_find_response)); 62 resp->signal = mindex_signal_under; 63 resp->index = 0; 64 resp->value = NULL; 65 return resp; 66 } 67 68 // Limit deeper search to only the left side 69 newlength = idx; 70 71 // Run the find on items below 72 resp = mindex_find(mindex, pattern, items, newlength); 73 74 // Surpress "over" signal 75 resp->signal &= ~mindex_signal_over; 76 77 return resp; 78 } 79 80 // Pattern = above middlepoint 81 if (acc > 0) { 82 83 // Calculate new index to check 84 newlength = length - 1 - idx; 85 86 // Nothing to our right, item not found 87 if (newlength <= 0) { 88 resp = malloc(sizeof(struct mindex_find_response)); 89 resp->signal = mindex_signal_over; 90 resp->index = length; 91 resp->value = NULL; 92 return resp; 93 } 94 95 // Get stats on the right-side of our pivot & translate response index 96 resp = mindex_find(mindex, pattern, &(items[idx+1]), newlength); 97 resp->index += idx + 1; 98 99 // Surpress "under" signal 100 resp->signal &= ~mindex_signal_under; 101 102 return resp; 103 } 104 105 // Found = win 106 resp = malloc(sizeof(struct mindex_find_response)); 107 resp->signal = mindex_signal_found; 108 resp->index = idx; 109 resp->value = items[idx]; 110 return resp; 111 } 112 113 void mindex_set(struct mindex_t *mindex, void *item) { 114 size_t movesize; 115 116 // Ensure we have space to insert a new item 117 if (mindex->max == mindex->length) { 118 mindex->max = mindex->max * 2; 119 mindex->items = realloc(mindex->items, mindex->max * sizeof(void*)); 120 } 121 122 // Find the index to insert (or already-occupied pattern) 123 struct mindex_find_response *resp = mindex_find(mindex, item, mindex->items, mindex->length); 124 125 // Item already in there, notify we skipped insertion 126 if (resp->signal & mindex_signal_found) { 127 mindex->items[resp->index] = item; 128 if (mindex->purge) mindex->purge(resp->value, mindex->udata); 129 free(resp); 130 return; 131 } 132 133 // Move items above aside for the insertion 134 // Essentially single-step insertion sort 135 movesize = (mindex->length - resp->index) * sizeof(void*); 136 memmove(&(mindex->items[resp->index + 1]), &(mindex->items[resp->index]), movesize); 137 mindex->items[resp->index] = item; 138 mindex->length++; 139 140 // Aanndd.. We're done 141 free(resp); 142 } 143 144 void * mindex_get(struct mindex_t *mindex, const void *pattern) { 145 struct mindex_find_response *resp = mindex_find(mindex, pattern, mindex->items, mindex->length); 146 void *value = resp->value; 147 free(resp); 148 return value; 149 } 150 151 void * mindex_nth(struct mindex_t *mindex, int index) { 152 if (index >= mindex->length) { 153 return NULL; 154 } 155 return mindex->items[index]; 156 } 157 158 void * mindex_rand(struct mindex_t *mindex) { 159 if (!mindex->length) { 160 return NULL; 161 } 162 return mindex->items[rand() % mindex->length]; 163 } 164 165 void mindex_delete(struct mindex_t *mindex, const void *pattern) { 166 struct mindex_find_response *resp = mindex_find(mindex, pattern, mindex->items, mindex->length); 167 168 // Not found = done 169 if (!(resp->signal & mindex_signal_found)) { 170 free(resp); 171 return; 172 } 173 174 // Call user's purge method 175 void *item = mindex->items[resp->index]; 176 if (mindex->purge) mindex->purge(item, mindex->udata); 177 178 // Move everything on it's right to it 179 void *dst = &(mindex->items[resp->index]); 180 void *src = &(mindex->items[resp->index+1]); 181 memmove(dst, src, (mindex->length - resp->index - 1) * sizeof(void*)); 182 183 // Update our size tracker 184 mindex->length--; 185 186 // TODO: reduce memory usage? 187 188 // Done 189 free(resp); 190 } 191 192 size_t mindex_length(struct mindex_t *mindex) { 193 return mindex->length; 194 } 195 196 void mindex_free(struct mindex_t *mindex) { 197 198 // Step 1, purge all entries 199 int i; 200 if (mindex->purge) { 201 for (i=0; i < mindex->length; i++) { 202 mindex->purge(mindex->items[i], mindex->udata); 203 } 204 } 205 206 // Step 2, free the list 207 free(mindex->items); 208 209 // Step 3, free the mindex itself 210 free(mindex); 211 212 // Done 213 }