mindex.c

In-memory ordered store and fetch library
git clone git://git.finwo.net/lib/mindex.c
Log | Files | Refs | README | LICENSE

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 }