list.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:13k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * list.c - generic dynamic list
  3.  *
  4.  * This module implements the generic list. See list.h for an explanation
  5.  * of how to use the list.
  6.  *
  7.  * The list is implemented as an array, a starting index into the array,
  8.  * and an integer giving the length of the list. The list's element i is
  9.  * not necessarily at array element i, but instead it is found at element
  10.  *
  11.  * (start + i) % len
  12.  *
  13.  * This is because we need to make it fast to use the list as a queue,
  14.  * meaning that adding elements to the end and removing them from the
  15.  * beginning must be very fast. Insertions into the middle of the list
  16.  * need not be fast, however. It would be possible to implement the list
  17.  * with a linked list, of course, but this would cause many more memory
  18.  * allocations: every time an item is added to the list, a new node would
  19.  * need to be allocated, and when it is removed, it would need to be freed.
  20.  * Using an array lets us reduce the number of allocations. It also lets
  21.  * us access an arbitrary element in constant time, which is especially
  22.  * useful since it lets us simplify the list API by not adding iterators
  23.  * or an explicit list item type.
  24.  *
  25.  * If insertions and deletions into the middle of the list become common,
  26.  * it would be more efficient to use a buffer gap implementation, but
  27.  * there's no point in doing that until the need arises.
  28.  *
  29.  * Lars Wirzenius <liw@wapit.com>
  30.  */
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include "gwassert.h"
  34. #include "config.h"
  35. #include "list.h"
  36. #include "log.h"
  37. #include "thread.h"
  38. #include "gwmem.h"
  39. struct List
  40. {
  41.     void **tab;
  42.     long tab_size;
  43.     long start;
  44.     long len;
  45.     Mutex *single_operation_lock;
  46.     Mutex *permanent_lock;
  47.     pthread_cond_t nonempty;
  48.     long num_producers;
  49. };
  50. #define INDEX(list, i) (((list)->start + i) % (list)->tab_size)
  51. #define GET(list, i) ((list)->tab[INDEX(list, i)])
  52. static void lock(List *list);
  53. static void unlock(List *list);
  54. static void make_bigger(List *list, long items);
  55. static void delete_items_from_list(List *list, long pos, long count);
  56. List *list_create_real(void)
  57. {
  58.     List *list;
  59.     list = gw_malloc(sizeof(List));
  60.     list->tab = NULL;
  61.     list->tab_size = 0;
  62.     list->start = 0;
  63.     list->len = 0;
  64.     list->single_operation_lock = mutex_create();
  65.     list->permanent_lock = mutex_create();
  66.     pthread_cond_init(&list->nonempty, NULL);
  67.     list->num_producers = 0;
  68.     return list;
  69. }
  70. void list_destroy(List *list, list_item_destructor_t *destructor)
  71. {
  72.     void *item;
  73.     if (list == NULL)
  74.         return;
  75.     if (destructor != NULL) {
  76.         while ((item = list_extract_first(list)) != NULL)
  77.             destructor(item);
  78.     }
  79.     mutex_destroy(list->permanent_lock);
  80.     mutex_destroy(list->single_operation_lock);
  81.     pthread_cond_destroy(&list->nonempty);
  82.     gw_free(list->tab);
  83.     gw_free(list);
  84. }
  85. long list_len(List *list)
  86. {
  87.     long len;
  88.     if (list == NULL)
  89.         return 0;
  90.     lock(list);
  91.     len = list->len;
  92.     unlock(list);
  93.     return len;
  94. }
  95. void list_append(List *list, void *item)
  96. {
  97.     lock(list);
  98.     make_bigger(list, 1);
  99.     list->tab[INDEX(list, list->len)] = item;
  100.     ++list->len;
  101.     pthread_cond_signal(&list->nonempty);
  102.     unlock(list);
  103. }
  104. void list_append_unique(List *list, void *item, int (*cmp)(void *, void *))
  105. {
  106.     void *it;
  107.     long i;
  108.     lock(list);
  109.     it = NULL;
  110.     for (i = 0; i < list->len; ++i) {
  111.         it = GET(list, i);
  112.         if (cmp(it, item)) {
  113.             break;
  114.         }
  115.     }
  116.     if (i == list->len) {
  117.         /* not yet in list, so add it */
  118.         make_bigger(list, 1);
  119.         list->tab[INDEX(list, list->len)] = item;
  120.         ++list->len;
  121.         pthread_cond_signal(&list->nonempty);
  122.     }
  123.     unlock(list);
  124. }
  125.         
  126. void list_insert(List *list, long pos, void *item)
  127. {
  128.     long i;
  129.     lock(list);
  130.     gw_assert(pos >= 0);
  131.     gw_assert(pos <= list->len);
  132.     make_bigger(list, 1);
  133.     for (i = list->len; i > pos; --i)
  134.         list->tab[(list->start + i) % list->tab_size] =
  135.             list->tab[(list->start + i - 1) % list->tab_size];
  136.     list->tab[(list->start + pos) % list->tab_size] = item;
  137.     ++list->len;
  138.     pthread_cond_signal(&list->nonempty);
  139.     unlock(list);
  140. }
  141. void list_delete(List *list, long pos, long count)
  142. {
  143.     lock(list);
  144.     delete_items_from_list(list, pos, count);
  145.     unlock(list);
  146. }
  147. long list_delete_matching(List *list, void *pat, list_item_matches_t *matches)
  148. {
  149.     long i;
  150.     long count;
  151.     lock(list);
  152.     /* XXX this could be made more efficient by noticing
  153.        consecutive items to be removed, but leave that for later.
  154.        --liw */
  155.     i = 0;
  156.     count = 0;
  157.     while (i < list->len) {
  158.         if (matches(GET(list, i), pat)) {
  159.             delete_items_from_list(list, i, 1);
  160.             count++;
  161.         } else {
  162.             ++i;
  163.         }
  164.     }
  165.     unlock(list);
  166.     return count;
  167. }
  168. long list_delete_equal(List *list, void *item)
  169. {
  170.     long i;
  171.     long count;
  172.     lock(list);
  173.     /* XXX this could be made more efficient by noticing
  174.        consecutive items to be removed, but leave that for later.
  175.        --liw */
  176.     i = 0;
  177.     count = 0;
  178.     while (i < list->len) {
  179.         if (GET(list, i) == item) {
  180.             delete_items_from_list(list, i, 1);
  181.             count++;
  182.         } else {
  183.             ++i;
  184.         }
  185.     }
  186.     unlock(list);
  187.     return count;
  188. }
  189. void *list_get(List *list, long pos)
  190. {
  191.     void *item;
  192.     lock(list);
  193.     gw_assert(pos >= 0);
  194.     gw_assert(pos < list->len);
  195.     item = GET(list, pos);
  196.     unlock(list);
  197.     return item;
  198. }
  199. void *list_extract_first(List *list)
  200. {
  201.     void *item;
  202.     gw_assert(list != NULL);
  203.     lock(list);
  204.     if (list->len == 0)
  205.         item = NULL;
  206.     else {
  207.         item = GET(list, 0);
  208.         delete_items_from_list(list, 0, 1);
  209.     }
  210.     unlock(list);
  211.     return item;
  212. }
  213. List *list_extract_matching(List *list, void *pat, list_item_matches_t *cmp)
  214. {
  215.     List *new_list;
  216.     long i;
  217.     new_list = list_create();
  218.     lock(list);
  219.     i = 0;
  220.     while (i < list->len) {
  221.         if (cmp(GET(list, i), pat)) {
  222.             list_append(new_list, GET(list, i));
  223.             delete_items_from_list(list, i, 1);
  224.         } else
  225.             ++i;
  226.     }
  227.     unlock(list);
  228.     if (list_len(new_list) == 0) {
  229.         list_destroy(new_list, NULL);
  230.         return NULL;
  231.     }
  232.     return new_list;
  233. }
  234. void list_lock(List *list)
  235. {
  236.     gw_assert(list != NULL);
  237.     mutex_lock(list->permanent_lock);
  238. }
  239. void list_unlock(List *list)
  240. {
  241.     gw_assert(list != NULL);
  242.     mutex_unlock(list->permanent_lock);
  243. }
  244. int list_wait_until_nonempty(List *list)
  245. {
  246.     int ret;
  247.     lock(list);
  248.     while (list->len == 0 && list->num_producers > 0) {
  249.         pthread_cond_wait(&list->nonempty,
  250.                           &list->single_operation_lock->mutex);
  251.     }
  252.     if (list->len > 0)
  253.         ret = 1;
  254.     else
  255.         ret = -1;
  256.     unlock(list);
  257.     return ret;
  258. }
  259. void list_add_producer(List *list)
  260. {
  261.     lock(list);
  262.     ++list->num_producers;
  263.     unlock(list);
  264. }
  265. int list_producer_count(List *list)
  266. {
  267.     int ret;
  268.     lock(list);
  269.     ret = list->num_producers;
  270.     unlock(list);
  271.     return ret;
  272. }
  273. void list_remove_producer(List *list)
  274. {
  275.     lock(list);
  276.     gw_assert(list->num_producers > 0);
  277.     --list->num_producers;
  278.     pthread_cond_broadcast(&list->nonempty);
  279.     unlock(list);
  280. }
  281. void list_produce(List *list, void *item)
  282. {
  283.     list_append(list, item);
  284. }
  285. void *list_consume(List *list)
  286. {
  287.     void *item;
  288.     lock(list);
  289.     while (list->len == 0 && list->num_producers > 0) {
  290.         pthread_cond_wait(&list->nonempty,
  291.                           &list->single_operation_lock->mutex);
  292.     }
  293.     if (list->len > 0) {
  294.         item = GET(list, 0);
  295.         delete_items_from_list(list, 0, 1);
  296.     } else {
  297.         item = NULL;
  298.     }
  299.     unlock(list);
  300.     return item;
  301. }
  302. void *list_search(List *list, void *pattern, int (*cmp)(void *, void *))
  303. {
  304.     void *item;
  305.     long i;
  306.     lock(list);
  307.     item = NULL;
  308.     for (i = 0; i < list->len; ++i) {
  309.         item = GET(list, i);
  310.         if (cmp(item, pattern)) {
  311.             break;
  312.         }
  313.     }
  314.     if (i == list->len) {
  315.         item = NULL;
  316.     }
  317.     unlock(list);
  318.     return item;
  319. }
  320. List *list_search_all(List *list, void *pattern, int (*cmp)(void *, void *))
  321. {
  322.     List *new_list;
  323.     void *item;
  324.     long i;
  325.     new_list = list_create();
  326.     lock(list);
  327.     item = NULL;
  328.     for (i = 0; i < list->len; ++i) {
  329.         item = GET(list, i);
  330.         if (cmp(item, pattern))
  331.             list_append(new_list, item);
  332.     }
  333.     unlock(list);
  334.     if (list_len(new_list) == 0) {
  335.         list_destroy(new_list, NULL);
  336.         new_list = NULL;
  337.     }
  338.     return new_list;
  339. }
  340. /*************************************************************************/
  341. static void lock(List *list)
  342. {
  343.     gw_assert(list != NULL);
  344.     mutex_lock(list->single_operation_lock);
  345. }
  346. static void unlock(List *list)
  347. {
  348.     gw_assert(list != NULL);
  349.     mutex_unlock(list->single_operation_lock);
  350. }
  351. /*
  352.  * Make the array bigger. It might be more efficient to make the size
  353.  * bigger than what is explicitly requested.
  354.  *
  355.  * Assume list has been locked for a single operation already.
  356.  */
  357. static void make_bigger(List *list, long items)
  358. {
  359.     long old_size, new_size;
  360.     long len_at_beginning, len_at_end;
  361.     if (list->len + items <= list->tab_size)
  362.         return;
  363.     old_size = list->tab_size;
  364.     new_size = old_size + items;
  365.     list->tab = gw_realloc(list->tab, new_size * sizeof(void *));
  366.     list->tab_size = new_size;
  367.     /*
  368.      * Now, one of the following situations is in effect
  369.      * (* is used, empty is unused element):
  370.      *
  371.      * Case 1: Used area did not wrap. No action is necessary.
  372.      * 
  373.      *    old_size              new_size
  374.      *    v                     v
  375.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  376.      * | |*|*|*|*|*|*| | | | | | | | | | | | | | | | |
  377.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  378.      *   ^           ^
  379.      *   start       start+len
  380.      * 
  381.      * Case 2: Used area wrapped, but the part at the beginning
  382.      * of the array fits into the new area. Action: move part
  383.      * from beginning to new area.
  384.      * 
  385.      *    old_size              new_size
  386.      *    v                     v
  387.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  388.      * |*|*| | | | | | | |*|*|*| | | | | | | | | | | |
  389.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  390.      *     ^             ^
  391.      *     start+len     start
  392.      * 
  393.      * Case 3: Used area wrapped, and the part at the beginning
  394.      * of the array does not fit into the new area. Action: move
  395.      * as much as will fit from beginning to new area and move
  396.      * the rest to the beginning.
  397.      * 
  398.      *       old_size   new_size
  399.      *      v   v
  400.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  401.      * |*|*|*|*|*|*|*|*|*| | | | | | | | |*|*|*|*| | |
  402.      * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  403.      *      ^               ^
  404.      *      start+len       start
  405.      */
  406.     gw_assert(list->start < old_size ||
  407.               (list->start == 0 && old_size == 0));
  408.     if (list->start + list->len > old_size) {
  409.         len_at_end = old_size - list->start;
  410.         len_at_beginning = list->len - len_at_end;
  411.         if (len_at_beginning <= new_size - old_size) {
  412.             /* This is Case 2. */
  413.             memmove(list->tab + old_size,
  414.                     list->tab,
  415.                     len_at_beginning * sizeof(void *));
  416.         } else {
  417.             /* This is Case 3. */
  418.             memmove(list->tab + old_size,
  419.                     list->tab,
  420.                     (new_size - old_size) * sizeof(void *));
  421.             memmove(list->tab,
  422.                     list->tab + (new_size - old_size),
  423.                     (len_at_beginning - (new_size - old_size))
  424.                     * sizeof(void *));
  425.         }
  426.     }
  427. }
  428. /*
  429.  * Remove items `pos' through `pos+count-1' from list. Assume list has
  430.  * been locked by caller already.
  431.  */
  432. static void delete_items_from_list(List *list, long pos, long count)
  433. {
  434.     long i, from, to;
  435.     gw_assert(pos >= 0);
  436.     gw_assert(pos < list->len);
  437.     gw_assert(count >= 0);
  438.     gw_assert(pos + count <= list->len);
  439.     /*
  440.      * There are four cases:
  441.      *
  442.      * Case 1: Deletion at beginning of list. Just move start
  443.      * marker forwards (wrapping it at end of array). No need
  444.      * to move any items.
  445.      *
  446.      * Case 2: Deletion at end of list. Just shorten the length
  447.      * of the list. No need to move any items.
  448.      *
  449.      * Case 3: Deletion in the middle so that the list does not
  450.      * wrap in the array. Move remaining items at end of list
  451.      * to the place of the deletion.
  452.      *
  453.      * Case 4: Deletion in the middle so that the list does indeed
  454.      * wrap in the array. Move as many remaining items at the end
  455.      * of the list as will fit to the end of the array, then move
  456.      * the rest to the beginning of the array.
  457.      */
  458.     if (pos == 0) {
  459.         list->start = (list->start + count) % list->tab_size;
  460.         list->len -= count;
  461.     } else if (pos + count == list->len) {
  462.         list->len -= count;
  463.     } else if (list->start + list->len < list->tab_size) {
  464.         memmove(list->tab + list->start + pos,
  465.                 list->tab + list->start + pos + count,
  466.                 (list->len - pos - count) * sizeof(void *));
  467.         list->len -= count;
  468.     } else {
  469.         /*
  470.          * This is not especially efficient, but it's simple and
  471.          * works. Faster methods would have to take more special
  472.          * cases into account. 
  473.          */
  474.         for (i = 0; i < list->len - count - pos; ++i) {
  475.             from = INDEX(list, pos + i + count);
  476.             to = INDEX(list, pos + i);
  477.             list->tab[to] = list->tab[from];
  478.         }
  479.         list->len -= count;
  480.     }
  481. }