ucx/map.c

Tue, 17 Oct 2017 15:15:54 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 17 Oct 2017 15:15:54 +0200
changeset 250
b7d1317b138e
parent 225
a1a068c2c4ef
permissions
-rw-r--r--

updates license

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2017 Olaf Wintermann. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  */
    29 #include <stdlib.h>
    30 #include <string.h>
    32 #include "map.h"
    34 UcxMap *ucx_map_new(size_t size) {
    35     return ucx_map_new_a(NULL, size);
    36 }
    38 UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) {
    39     if(size == 0) {
    40         size = 16;
    41     }
    43     if(!allocator) {
    44         allocator = ucx_default_allocator();
    45     }
    47     UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap));
    48     if (!map) {
    49         return NULL;
    50     }
    52     map->allocator = allocator;
    53     map->map = (UcxMapElement**)alcalloc(
    54             allocator, size, sizeof(UcxMapElement*));
    55     if(map->map == NULL) {
    56         alfree(allocator, map);
    57         return NULL;
    58     }
    59     map->size = size;
    60     map->count = 0;
    62     return map;
    63 }
    65 static void ucx_map_free_elmlist_contents(UcxMap *map) {
    66     for (size_t n = 0 ; n < map->size ; n++) {
    67         UcxMapElement *elem = map->map[n];
    68         if (elem != NULL) {
    69             do {
    70                 UcxMapElement *next = elem->next;
    71                 alfree(map->allocator, elem->key.data);
    72                 alfree(map->allocator, elem);
    73                 elem = next;
    74             } while (elem != NULL);
    75         }
    76     }
    77 }
    79 void ucx_map_free(UcxMap *map) {
    80     ucx_map_free_elmlist_contents(map);
    81     alfree(map->allocator, map->map);
    82     alfree(map->allocator, map);
    83 }
    85 void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
    86     UcxMapIterator iter = ucx_map_iterator(map);
    87     void *val;
    88     UCX_MAP_FOREACH(key, val, iter) {
    89         destr(val);
    90     }
    91 }
    93 void ucx_map_clear(UcxMap *map) {
    94     if (map->count == 0) {
    95         return; // nothing to do
    96     }
    97     ucx_map_free_elmlist_contents(map);
    98     memset(map->map, 0, map->size*sizeof(UcxMapElement*));
    99     map->count = 0;
   100 }
   102 int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to,
   103         copy_func fnc, void *data) {
   104     UcxMapIterator i = ucx_map_iterator(from);
   105     void *value;
   106     UCX_MAP_FOREACH(key, value, i) {
   107         if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) {
   108             return 1;
   109         }
   110     }
   111     return 0;
   112 }
   114 UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data) {
   115     size_t bs = (map->count * 5) >> 1;
   116     UcxMap *newmap = ucx_map_new(bs > map->size ? bs : map->size);
   117     if (!newmap) {
   118         return NULL;
   119     }
   120     ucx_map_copy(map, newmap, fnc, data);
   121     return newmap;
   122 }
   124 int ucx_map_rehash(UcxMap *map) {
   125     size_t load = (map->size * 3) >> 2;
   126     if (map->count > load) {
   127         UcxMap oldmap;
   128         oldmap.map = map->map;
   129         oldmap.size = map->size;
   130         oldmap.count = map->count;
   131         oldmap.allocator = map->allocator;
   133         map->size = (map->count * 5) >> 1;
   134         map->map = (UcxMapElement**)alcalloc(
   135                 map->allocator, map->size, sizeof(UcxMapElement*));
   136         if (!map->map) {
   137             *map = oldmap;
   138             return 1;
   139         }
   140         map->count = 0;
   141         ucx_map_copy(&oldmap, map, NULL, NULL);
   143         /* free the UcxMapElement list of oldmap */
   144         ucx_map_free_elmlist_contents(&oldmap);
   145         alfree(map->allocator, oldmap.map);
   146     }
   147     return 0;
   148 }
   150 int ucx_map_put(UcxMap *map, UcxKey key, void *data) {
   151     UcxAllocator *allocator = map->allocator;
   153     if (key.hash == 0) {
   154         key.hash = ucx_hash((char*)key.data, key.len);
   155     }
   157     size_t slot = key.hash%map->size;
   158     UcxMapElement *restrict elm = map->map[slot];
   159     UcxMapElement *restrict prev = NULL;
   161     while (elm && elm->key.hash < key.hash) {
   162         prev = elm;
   163         elm = elm->next;
   164     }
   166     if (!elm || elm->key.hash != key.hash) {
   167         UcxMapElement *e = (UcxMapElement*)almalloc(
   168                 allocator, sizeof(UcxMapElement));
   169         if (!e) {
   170             return -1;
   171         }
   172         e->key.data = NULL;
   173         if (prev) {
   174             prev->next = e;
   175         } else {
   176             map->map[slot] = e;
   177         }
   178         e->next = elm;
   179         elm = e;
   180     }
   182     if (!elm->key.data) {
   183         void *kd = almalloc(allocator, key.len);
   184         if (!kd) {
   185             return -1;
   186         }
   187         memcpy(kd, key.data, key.len);
   188         key.data = kd;
   189         elm->key = key;
   190         map->count++;
   191     }
   192     elm->data = data;
   194     return 0;
   195 }
   197 void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, _Bool remove) {
   198     if(key.hash == 0) {
   199         key.hash = ucx_hash((char*)key.data, key.len);
   200     }
   202     size_t slot = key.hash%map->size;
   203     UcxMapElement *restrict elm = map->map[slot];
   204     UcxMapElement *restrict pelm = NULL;
   205     while (elm && elm->key.hash <= key.hash) {
   206         if(elm->key.hash == key.hash) {
   207             int n = (key.len > elm->key.len) ? elm->key.len : key.len;
   208             if (memcmp(elm->key.data, key.data, n) == 0) {
   209                 void *data = elm->data;
   210                 if (remove) {
   211                     if (pelm) {
   212                         pelm->next = elm->next;
   213                     } else {
   214                         map->map[slot] = elm->next;
   215                     }
   216                     alfree(map->allocator, elm->key.data);
   217                     alfree(map->allocator, elm);
   218                     map->count--;
   219                 }
   221                 return data;
   222             }
   223         }
   224         pelm = elm;
   225         elm = pelm->next;
   226     }
   228     return NULL;
   229 }
   231 void *ucx_map_get(UcxMap *map, UcxKey key) {
   232     return ucx_map_get_and_remove(map, key, 0);
   233 }
   235 void *ucx_map_remove(UcxMap *map, UcxKey key) {
   236     return ucx_map_get_and_remove(map, key, 1);
   237 }
   239 UcxKey ucx_key(void *data, size_t len) {
   240     UcxKey key;
   241     key.data = data;
   242     key.len = len;
   243     key.hash = ucx_hash((const char*) data, len);
   244     return key;
   245 }
   248 int ucx_hash(const char *data, size_t len) {
   249     /* murmur hash 2 */
   251     int m = 0x5bd1e995;
   252     int r = 24;
   254     int h = 25 ^ len;
   256     int i = 0;
   257     while (len >= 4) {
   258         int k = data[i + 0] & 0xFF;
   259         k |= (data[i + 1] & 0xFF) << 8;
   260         k |= (data[i + 2] & 0xFF) << 16;
   261         k |= (data[i + 3] & 0xFF) << 24;
   263         k *= m;
   264         k ^= k >> r;
   265         k *= m;
   267         h *= m;
   268         h ^= k;
   270         i += 4;
   271         len -= 4;
   272     }
   274     switch (len) {
   275         case 3: h ^= (data[i + 2] & 0xFF) << 16;
   276         /* no break */
   277         case 2: h ^= (data[i + 1] & 0xFF) << 8;
   278         /* no break */
   279         case 1: h ^= (data[i + 0] & 0xFF); h *= m;
   280         /* no break */
   281     }
   283     h ^= h >> 13;
   284     h *= m;
   285     h ^= h >> 15;
   287     return h;
   288 }
   290 UcxMapIterator ucx_map_iterator(UcxMap *map) {
   291     UcxMapIterator i;
   292     i.map = map;
   293     i.cur = NULL;
   294     i.index = 0;
   295     return i;
   296 }
   298 int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) {
   299     UcxMapElement *e = i->cur;
   301     if (e) {
   302         e = e->next;
   303     } else {
   304         e = i->map->map[0];
   305     }
   307     while (i->index < i->map->size) {
   308         if (e) {
   309             if (e->data) {
   310                 i->cur = e;
   311                 *elm = e->data;
   312                 *key = e->key;
   313                 return 1;
   314             }
   316             e = e->next;
   317         } else {
   318             i->index++;
   320             if (i->index < i->map->size) {
   321                 e = i->map->map[i->index];
   322             }
   323         }
   324     }
   326     return 0;
   327 }

mercurial