memory_pool.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Swift Navigation Inc.
00003  * Contact: Fergus Noble <fergus@swift-nav.com>
00004  *
00005  * This source is subject to the license found in the file 'LICENSE' which must
00006  * be be distributed together with this source. All other rights reserved.
00007  *
00008  * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
00009  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
00010  * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
00011  */
00012 
00013 #include <stdlib.h>
00014 #include <string.h>
00015 
00016 #include "memory_pool.h"
00017 
00018 inline static size_t calc_node_size(size_t element_size)
00019 {
00020   return element_size + sizeof(memory_pool_node_hdr_t);
00021 }
00022 
00023 inline static node_t *get_node_n(memory_pool_t *pool, node_t *head, u32 n)
00024 {
00025   return (node_t *)((u8 *)head + calc_node_size(pool->element_size) * n);
00026 }
00027 
00062 memory_pool_t *memory_pool_new(u32 n_elements, size_t element_size)
00063 {
00064   memory_pool_t *new_pool = malloc(sizeof(memory_pool_t));
00065   if (!new_pool) {
00066     return NULL;
00067   }
00068 
00069   /* Allocate memory pool */
00070   size_t node_size = calc_node_size(element_size);
00071   node_t *buff = (node_t *)malloc(node_size * n_elements);
00072   if (!buff) {
00073     free(new_pool);
00074     return NULL;
00075   }
00076 
00077   memory_pool_init(new_pool, n_elements, element_size, buff);
00078 
00079   return new_pool;
00080 }
00081 
00103 s8 memory_pool_init(memory_pool_t *new_pool, u32 n_elements,
00104                     size_t element_size, void *buff)
00105 {
00106   if (!new_pool) {
00107     return -1;
00108   }
00109 
00110   new_pool->n_elements = n_elements;
00111   new_pool->element_size = element_size;
00112 
00113   /* Setup memory pool buffer area */
00114   new_pool->pool = (node_t *)buff;
00115   if (!new_pool->pool) {
00116     return -2;
00117   }
00118 
00119   /* Create linked list out of all the nodes in the pool, adding them to the
00120    * list of free nodes so they are ready to be allocated. */
00121   new_pool->free_nodes_head = new_pool->pool;
00122   node_t *current = NULL;
00123   node_t *next_free = NULL;
00124   /* Make linked list from last element to first,
00125    * very last element points to NULL. */
00126   for (s32 i=n_elements-1; i>=0; i--) {
00127     current = get_node_n(new_pool, new_pool->free_nodes_head, i);
00128     current->hdr.next = next_free;
00129     next_free = current;
00130   }
00131 
00132   /* No nodes currently allocated. */
00133   new_pool->allocated_nodes_head = NULL;
00134 
00135   return 0;
00136 }
00137 
00144 void memory_pool_destroy(memory_pool_t *pool)
00145 {
00146   free(pool->pool);
00147   free(pool);
00148 }
00149 
00157 s32 memory_pool_n_free(memory_pool_t *pool)
00158 {
00159   u32 count = 0;
00160 
00161   node_t *p = pool->free_nodes_head;
00162   while (p && count <= pool->n_elements) {
00163     p = p->hdr.next;
00164     count++;
00165   }
00166 
00167   if (count == pool->n_elements && p)
00168     /* The list of free elements is larger than the pool,
00169      * something has gone horribly wrong. */
00170     return -1;
00171 
00172   return count;
00173 }
00174 
00181 s32 memory_pool_n_allocated(memory_pool_t *pool)
00182 {
00183   u32 count = 0;
00184 
00185   node_t *p = pool->allocated_nodes_head;
00186   while (p && count <= pool->n_elements) {
00187     p = p->hdr.next;
00188     count++;
00189   }
00190 
00191   if (count == pool->n_elements && p)
00192     /* The list of free elements is larger than the pool,
00193      * something has gone horribly wrong. */
00194     return -1;
00195 
00196   return count;
00197 }
00198 
00205 u8 memory_pool_empty(memory_pool_t *pool)
00206 {
00207   return (pool->allocated_nodes_head == NULL);
00208 }
00209 
00212 u32 memory_pool_n_elements(memory_pool_t *pool)
00213 {
00214   return pool->n_elements;
00215 }
00228 s32 memory_pool_to_array(memory_pool_t *pool, void *array)
00229 {
00230   u32 count = 0;
00231 
00232   node_t *p = pool->allocated_nodes_head;
00233   while (p && count <= pool->n_elements) {
00234     memcpy((u8 *)array + count*pool->element_size, p->elem, pool->element_size);
00235     p = p->hdr.next;
00236     count++;
00237   }
00238 
00239   if (count == pool->n_elements && p)
00240     /* The list of free elements is larger than the pool,
00241      * something has gone horribly wrong. */
00242     return -1;
00243 
00244   return count;
00245 }
00246 
00254 element_t *memory_pool_add(memory_pool_t *pool)
00255 {
00256   /* Take the head of the list of free nodes, insert it as the head of the
00257    * allocated nodes and return a pointer to the node's element. */
00258 
00259   if (!pool->free_nodes_head) {
00260     /* free_nodes_head is NULL, no free nodes available, pool is full. */
00261     return NULL;
00262   }
00263 
00264   node_t *new_node = pool->free_nodes_head;
00265   pool->free_nodes_head = new_node->hdr.next;
00266 
00267   new_node->hdr.next = pool->allocated_nodes_head;
00268   pool->allocated_nodes_head = new_node;
00269 
00270   return new_node->elem;
00271 }
00272 
00279 s32 memory_pool_map(memory_pool_t *pool, void *arg, void (*f)(void *arg, element_t *elem))
00280 {
00281   u32 count = 0;
00282 
00283   node_t *p = pool->allocated_nodes_head;
00284   while (p && count <= pool->n_elements) {
00285     (*f)(arg, p->elem);
00286     p = p->hdr.next;
00287     count++;
00288   }
00289 
00290   if (count == pool->n_elements && p)
00291     /* The list of elements is larger than the pool,
00292      * something has gone horribly wrong. */
00293     return -1;
00294 
00295   return count;
00296 }
00297 
00308 s32 memory_pool_fold(memory_pool_t *pool, void *x0,
00309                      void (*f)(void *x, element_t *elem))
00310 {
00311   u32 count = 0;
00312 
00313   node_t *p = pool->allocated_nodes_head;
00314   while (p && count <= pool->n_elements) {
00315     (*f)(x0, p->elem);
00316     p = p->hdr.next;
00317     count++;
00318   }
00319 
00320   if (count == pool->n_elements && p)
00321     /* The list of elements is larger than the pool,
00322      * something has gone horribly wrong. */
00323     return -1;
00324 
00325   return count;
00326 }
00327 
00338 double memory_pool_dfold(memory_pool_t *pool, double x0,
00339                          double (*f)(double x, element_t *elem))
00340 {
00341   u32 count = 0;
00342   double x = x0;
00343 
00344   node_t *p = pool->allocated_nodes_head;
00345   while (p && count <= pool->n_elements) {
00346     x = (*f)(x, p->elem);
00347     p = p->hdr.next;
00348     count++;
00349   }
00350 
00351   return x;
00352 }
00353 
00364 float memory_pool_ffold(memory_pool_t *pool, float x0,
00365                         float (*f)(float x, element_t *elem))
00366 {
00367   u32 count = 0;
00368   float x = x0;
00369 
00370   node_t *p = pool->allocated_nodes_head;
00371   while (p && count <= pool->n_elements) {
00372     x = (*f)(x, p->elem);
00373     p = p->hdr.next;
00374     count++;
00375   }
00376 
00377   return x;
00378 }
00379 
00390 s32 memory_pool_ifold(memory_pool_t *pool, s32 x0,
00391                       s32 (*f)(s32 x, element_t *elem))
00392 {
00393   u32 count = 0;
00394   s32 x = x0;
00395 
00396   node_t *p = pool->allocated_nodes_head;
00397   while (p && count <= pool->n_elements) {
00398     x = (*f)(x, p->elem);
00399     p = p->hdr.next;
00400     count++;
00401   }
00402 
00403   return x;
00404 }
00405 
00414 s32 memory_pool_filter(memory_pool_t *pool, void *arg, s8 (*f)(void *arg, element_t *elem))
00415 {
00416   u32 count = 0;
00417 
00418   /* Construct a fake 'previous' node for the head of the list, this eliminates
00419    * special cases for the beginning of the list. */
00420   node_t fake_head_node_prev = {
00421     .hdr = {
00422       .next = pool->allocated_nodes_head,
00423     },
00424   };
00425 
00426   node_t *p_prev = &fake_head_node_prev;
00427   node_t *p = pool->allocated_nodes_head;
00428 
00429   while (p && count <= pool->n_elements) {
00430     if ((*f)(arg, p->elem)) {
00431       /* Keep element, move along.. */
00432       p_prev = p;
00433       p = p->hdr.next;
00434       count++;
00435     } else {
00436       /* Drop this element from the list */
00437       p_prev->hdr.next = p->hdr.next;
00438       /* and return its node to the pool. */
00439       p->hdr.next = pool->free_nodes_head;
00440       pool->free_nodes_head = p;
00441       p = p_prev->hdr.next;
00442     }
00443   }
00444 
00445   /* Use our fake previous node to update the head pointer. */
00446   pool->allocated_nodes_head = fake_head_node_prev.hdr.next;
00447 
00448   if (count == pool->n_elements && p)
00449     /* The list of elements is larger than the pool,
00450      * something has gone horribly wrong. */
00451     return -1;
00452 
00453   return count;
00454 }
00455 
00461 s32 memory_pool_clear(memory_pool_t *pool)
00462 {
00463   u32 count = 0;
00464   node_t *p = pool->allocated_nodes_head;
00465 
00466   if (!p) {
00467     /* If allocated_nodes_head is NULL then the pool was already empty. */
00468     return 0;
00469   }
00470 
00471   while (p->hdr.next && count <= pool->n_elements) {
00472     p = p->hdr.next;
00473     count++;
00474   }
00475 
00476   if (p->hdr.next) {
00477     /* Failed to find the end of the allocaed nodes list. */
00478     return -1;
00479   }
00480 
00481   /* p now points to the last node in the list. We can now insert the old
00482    * allocated nodes list in front of the current free nodes list. */
00483   p->hdr.next = pool->free_nodes_head;
00484   pool->free_nodes_head = pool->allocated_nodes_head;
00485   pool->allocated_nodes_head = NULL;
00486 
00487   return 0;
00488 }
00489 
00535 void memory_pool_sort(memory_pool_t *pool, void *arg,
00536                       s32 (*cmp)(void *arg, element_t *a, element_t *b))
00537 {
00538   /* If collection is empty, return immediately. */
00539   if (!pool->allocated_nodes_head)
00540     return;
00541 
00542   u32 insize = 1;
00543 
00544   while (1) {
00545     node_t *p = pool->allocated_nodes_head;
00546     pool->allocated_nodes_head = NULL;
00547     node_t *tail = NULL;
00548 
00549     u32 nmerges = 0;  /* count number of merges we do in this pass */
00550 
00551     while (p) {
00552       nmerges++;  /* there exists a merge to be done */
00553       /* step `insize' places along from p */
00554       node_t *q = p;
00555       u32 psize = 0;
00556       for (u32 i = 0; i < insize; i++) {
00557         psize++;
00558         q = q->hdr.next;
00559         if (!q) break;
00560       }
00561 
00562       /* if q hasn't fallen off end, we have two lists to merge */
00563       u32 qsize = insize;
00564 
00565       /* now we have two lists; merge them */
00566       while (psize > 0 || (qsize > 0 && q)) {
00567 
00568         node_t *e;
00569 
00570         /* decide whether next element of merge comes from p or q */
00571         if (psize == 0) {
00572           /* p is empty; e must come from q. */
00573           e = q; q = q->hdr.next; qsize--;
00574         } else if (qsize == 0 || !q) {
00575           /* q is empty; e must come from p. */
00576           e = p; p = p->hdr.next; psize--;
00577         } else if (cmp(arg, p->elem, q->elem) <= 0) {
00578           /* First element of p is lower (or same);
00579            * e must come from p. */
00580           e = p; p = p->hdr.next; psize--;
00581         } else {
00582           /* First element of q is lower; e must come from q. */
00583           e = q; q = q->hdr.next; qsize--;
00584         }
00585 
00586         /* add the next element to the merged list */
00587         if (tail) {
00588           tail->hdr.next = e;
00589         } else {
00590           pool->allocated_nodes_head = e;
00591         }
00592         tail = e;
00593       }
00594 
00595       /* now p has stepped `insize' places along, and q has too */
00596       p = q;
00597     }
00598     tail->hdr.next = NULL;
00599 
00600     /* If we have done only one merge, we're finished. */
00601     if (nmerges <= 1)   /* allow for nmerges==0, the empty list case */
00602       return;
00603 
00604     /* Otherwise repeat, merging lists twice the size */
00605     insize *= 2;
00606   }
00607 }
00608 
00644 void memory_pool_group_by(memory_pool_t *pool, void *arg,
00645                           s32 (*cmp)(void *arg, element_t *a, element_t *b),
00646                           void *x0, size_t x_size,
00647                           void (*agg)(element_t *new, void *x, u32 n, element_t *elem))
00648 {
00649   /* If collection is empty, return immediately. */
00650   if (!pool->allocated_nodes_head)
00651     return;
00652 
00653   /* First sort the existing list using the compare function. */
00654   memory_pool_sort(pool, arg, cmp);
00655 
00656   /* Save the head of the unaggregated list and reset the pool head where the
00657    * aggregated data will be added. */
00658   node_t *old_head = pool->allocated_nodes_head;
00659   pool->allocated_nodes_head = NULL;
00660 
00661   /* Allocate working area for the fold function. */
00662   u8 x_work[x_size];
00663 
00664   u32 count = 0;
00665 
00666   node_t *p = old_head;
00667 
00668   while (p && count <= pool->n_elements) {
00669     u32 group_count = 0;
00670 
00671     /* Keep a pointer to the head of the group. */
00672     node_t *group_head = p;
00673 
00674     /* Re-initialize the working area. */
00675     if (x_size)
00676       memcpy(x_work, x0, x_size);
00677 
00678     /* Create a new element to hold the aggregate of this group. */
00679     element_t *new_elem = memory_pool_add(pool);
00680 
00681     /* Initialize the new element to the first element in the group. */
00682     memcpy(new_elem, p->elem, pool->element_size);
00683 
00684     /* Aggregate this group. */
00685     do {
00686       agg(new_elem, (void *)x_work, group_count, p->elem);
00687       group_count++;
00688 
00689       /* Store pointer to next node to process. */
00690       node_t *next_p = p->hdr.next;
00691 
00692       /* Return current node to the pool. */
00693       p->hdr.next = pool->free_nodes_head;
00694       pool->free_nodes_head = p;
00695 
00696       p = next_p;
00697     } while(p && cmp(arg, group_head->elem, p->elem) == 0);
00698 
00699     count++;
00700   }
00701 }
00702 
00721 s32 memory_pool_product(memory_pool_t *pool, void *xs, u32 n_xs, size_t x_size,
00722                         void (*prod)(element_t *new, void *x, u32 n_xs, u32 n, element_t *elem))
00723 {
00724   /* Save the head of the original list and reset the pool head where the
00725    * product data will be added. */
00726   node_t *old_head = pool->allocated_nodes_head;
00727   pool->allocated_nodes_head = NULL;
00728 
00729   u32 count = 0;
00730 
00731   node_t *p = old_head;
00732   while (p && count <= pool->n_elements) {
00733     /* Add one element to the new list for each pair of
00734      * the current element and an item in xs. */
00735     for (u32 i=0; i<n_xs; i++) {
00736       element_t *new = memory_pool_add(pool);
00737       if (!new) {
00738         /* Pool is full. */
00739         return -2;
00740       }
00741       /* Initialize the element to the same as the original element. */
00742       memcpy(new, p->elem, pool->element_size);
00743       prod(new, ((u8 *)xs + i*x_size), n_xs, i, p->elem);
00744       count++;
00745     }
00746 
00747     /* Store pointer to next node to process. */
00748     node_t *next_p = p->hdr.next;
00749 
00750     /* Return current node to the pool. */
00751     p->hdr.next = pool->free_nodes_head;
00752     pool->free_nodes_head = p;
00753 
00754     p = next_p;
00755   }
00756 
00757   if (count == pool->n_elements && p)
00758     /* The list of elements is larger than the pool,
00759      * something has gone horribly wrong. */
00760     return -1;
00761 
00762   return count;
00763 }
00764 
00765 s32 memory_pool_product_generator(memory_pool_t *pool, void *x0, u32 max_xs, size_t x_size,
00766                                   s8 (*next)(void *x, u32 n),
00767                                   void (*prod)(element_t *new, void *x, u32 n, element_t *elem))
00768 {
00769   /* Save the head of the original list and reset the pool head where the
00770    * product data will be added. */
00771   node_t *old_head = pool->allocated_nodes_head;
00772   pool->allocated_nodes_head = NULL;
00773 
00774   u32 count = 0;
00775 
00776   node_t *p = old_head;
00777   while (p && count <= pool->n_elements) {
00778     /* Iterate through our generator adding a new element for each pair
00779      * of a generated element and the current element from the old list. */
00780     u8 x_work[x_size];
00781     memcpy(x_work, x0, x_size);
00782     u32 x_count = 0;
00783     do {
00784       if (x_count > max_xs) {
00785         /* Exceded maximum number of generator iterations. */
00786         return -3;
00787       }
00788       element_t *new = memory_pool_add(pool);
00789       if (!new) {
00790         /* Pool is full. */
00791         return -2;
00792       }
00793       /* Initialize the element to the same as the original element. */
00794       memcpy(new, p->elem, pool->element_size);
00795       prod(new, x_work, x_count, p->elem);
00796       x_count++;
00797       count++;
00798     } while (next(x_work, x_count));
00799 
00800     /* Store pointer to next node to process. */
00801     node_t *next_p = p->hdr.next;
00802 
00803     /* Return current node to the pool. */
00804     p->hdr.next = pool->free_nodes_head;
00805     pool->free_nodes_head = p;
00806 
00807     p = next_p;
00808   }
00809 
00810   if (count == pool->n_elements && p)
00811     /* The list of elements is larger than the pool,
00812      * something has gone horribly wrong. */
00813     return -1;
00814 
00815   return count;
00816 }
00817 


swiftnav
Author(s):
autogenerated on Sat Jun 8 2019 18:55:56