apop_vtables.c 3.08 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
#include <stdlib.h>
#include <string.h>
#include "apop_internal.h" //just for OMP_critical

#ifdef _OPENMP
#include <omp.h>
#define lock omp_set_lock(&v->mutex);
#define unlock omp_unset_lock(&v->mutex);
#else
#define lock 
#define unlock 
#endif

14
/** \cond doxy_ignore */
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
typedef struct {
    size_t hash;
    void *fn;
} apop_vtable_elmt_s;

typedef struct {
    char const *name;
    unsigned long int hashed_name;
    int elmt_ct;
    apop_vtable_elmt_s *elmts;
#ifdef _OPENMP
    omp_lock_t mutex;
#endif
} apop_vtable_s;

apop_vtable_s *vtable_list;
int ignore_me;
32
/** \endcond */ //End of Doxygen ignore.
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

//The Dan J Bernstein string hashing algorithm.
static unsigned long apop_settings_hash(char const *str){
    unsigned long int hash = 5381;
    char c;
    while ((c = *str++)) hash = hash*33 + c;
    return hash;
}

static apop_vtable_s *find_tab(unsigned long h, int *ctr){
    apop_vtable_s *v = vtable_list;
    *ctr = 0;
    for ( ; v->hashed_name; (*ctr)++, v++) if (v->hashed_name== h) break;
    return v;
}

//return 0 = found; removed
//return 1 = not found; no-op
int apop_vtable_drop(char const *tabname, unsigned long hash){
    if (!vtable_list) return 1;
    unsigned long h = apop_settings_hash(tabname);
    apop_vtable_s *v = find_tab(h, &ignore_me);

    lock
    for (int i=0; i< v->elmt_ct; i++)
        if (hash == v->elmts[i].hash) {
            memmove(v->elmts+i, v->elmts+i+1, sizeof(apop_vtable_elmt_s)*(v->elmt_ct-i));
            v->elmt_ct--;
            unlock
            return 0;
        }
    unlock
    return 1;
}

int apop_vtable_add(char const *tabname, void *fn_in, unsigned long hash){
    if (!vtable_list){vtable_list = calloc(1, sizeof(apop_vtable_s));}

    unsigned long h = apop_settings_hash(tabname);
    int ctr;
    apop_vtable_s *v;


    //add a table if need be.
    OMP_critical (new_vtable)
    {
    v = find_tab(h, &ctr);

    if (!v->hashed_name){
        vtable_list = realloc(vtable_list, (ctr+2)* sizeof(apop_vtable_s));
        vtable_list[ctr] = (apop_vtable_s){.elmts=calloc(1, sizeof(apop_vtable_elmt_s))};
        vtable_list[ctr+1] = (apop_vtable_s){ };
        #ifdef _OPENMP
            omp_init_lock(&vtable_list[ctr].mutex);
            omp_set_lock(&vtable_list[ctr].mutex);
        #endif
        vtable_list[ctr].name = tabname;
        vtable_list[ctr].hashed_name = h;
        v = vtable_list+ctr;
        unlock
    }
    }

    lock
    //If this hash is already present, don't re-add. 
    for (int i=0; i< v->elmt_ct; i++) if (hash == v->elmts[i].hash) {unlock; return 0;}

    //insert
    v->elmts = realloc(v->elmts, (++(v->elmt_ct))* sizeof(apop_vtable_elmt_s));
    v->elmts[v->elmt_ct-1] = (apop_vtable_elmt_s){.hash=hash, .fn=fn_in};
    unlock
    return 0;
}

void *apop_vtable_get(char const *tabname, unsigned long hash){
    if (!vtable_list) return NULL;
    unsigned long thash = apop_settings_hash(tabname);
    apop_vtable_s *v = find_tab(thash, &ignore_me);
    if (!v->hashed_name) return NULL;

    lock
    for (int i=0; i< v->elmt_ct; i++)
        if (hash == v->elmts[i].hash) {unlock; return v->elmts[i].fn;}
    unlock
    return NULL;
}