implement backwards iterator - fixes #238

Wed, 15 Feb 2023 16:48:11 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 15 Feb 2023 16:48:11 +0100
changeset 655
7340c4255f1f
parent 654
c9d008861178
child 656
2ccb9f881420

implement backwards iterator - fixes #238

src/array_list.c file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
tests/test_list.cpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/array_list.c	Wed Feb 08 20:26:26 2023 +0100
     1.2 +++ b/src/array_list.c	Wed Feb 15 16:48:11 2023 +0100
     1.3 @@ -395,6 +395,21 @@
     1.4      }
     1.5  }
     1.6  
     1.7 +static void cx_arl_iter_prev(void *it) {
     1.8 +    struct cx_iterator_base_s *itbase = it;
     1.9 +    struct cx_mut_iterator_s *iter = it;
    1.10 +    cx_array_list *const list = iter->src_handle;
    1.11 +    if (itbase->remove) {
    1.12 +        itbase->remove = false;
    1.13 +        cx_arl_remove(iter->src_handle, iter->index);
    1.14 +    }
    1.15 +    iter->index--;
    1.16 +    if (iter->index < list->base.size) {
    1.17 +        iter->elem_handle = ((char *) list->data)
    1.18 +                            + iter->index * list->base.itemsize;
    1.19 +    }
    1.20 +}
    1.21 +
    1.22  static bool cx_arl_iter_flag_rm(void *it) {
    1.23      struct cx_iterator_base_s *iter = it;
    1.24      if (iter->mutating) {
    1.25 @@ -407,7 +422,8 @@
    1.26  
    1.27  static struct cx_iterator_s cx_arl_iterator(
    1.28          struct cx_list_s const *list,
    1.29 -        size_t index
    1.30 +        size_t index,
    1.31 +        bool backwards
    1.32  ) {
    1.33      struct cx_iterator_s iter;
    1.34  
    1.35 @@ -416,7 +432,7 @@
    1.36      iter.elem_handle = cx_arl_at(list, index);
    1.37      iter.base.valid = cx_arl_iter_valid;
    1.38      iter.base.current = cx_arl_iter_current;
    1.39 -    iter.base.next = cx_arl_iter_next;
    1.40 +    iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
    1.41      iter.base.flag_removal = cx_arl_iter_flag_rm;
    1.42      iter.base.remove = false;
    1.43      iter.base.mutating = false;
     2.1 --- a/src/cx/list.h	Wed Feb 08 20:26:26 2023 +0100
     2.2 +++ b/src/cx/list.h	Wed Feb 15 16:48:11 2023 +0100
     2.3 @@ -211,7 +211,8 @@
     2.4       */
     2.5      struct cx_iterator_s (*iterator)(
     2.6              struct cx_list_s const *list,
     2.7 -            size_t index
     2.8 +            size_t index,
     2.9 +            bool backward
    2.10      );
    2.11  };
    2.12  
    2.13 @@ -456,11 +457,30 @@
    2.14   * @return a new iterator
    2.15   */
    2.16  __attribute__((__nonnull__, __warn_unused_result__))
    2.17 -static inline CxIterator cxListIterator(
    2.18 +static inline CxIterator cxListIteratorAt(
    2.19          CxList const *list,
    2.20          size_t index
    2.21  ) {
    2.22 -    return list->cl->iterator(list, index);
    2.23 +    return list->cl->iterator(list, index, false);
    2.24 +}
    2.25 +
    2.26 +/**
    2.27 + * Returns a backwards iterator pointing to the item at the specified index.
    2.28 + *
    2.29 + * The returned iterator is position-aware.
    2.30 + *
    2.31 + * If the index is out of range, a past-the-end iterator will be returned.
    2.32 + *
    2.33 + * @param list the list
    2.34 + * @param index the index where the iterator shall point at
    2.35 + * @return a new iterator
    2.36 + */
    2.37 +__attribute__((__nonnull__, __warn_unused_result__))
    2.38 +static inline CxIterator cxListBackwardsIteratorAt(
    2.39 +        CxList const *list,
    2.40 +        size_t index
    2.41 +) {
    2.42 +    return list->cl->iterator(list, index, true);
    2.43  }
    2.44  
    2.45  /**
    2.46 @@ -475,7 +495,25 @@
    2.47   * @return a new iterator
    2.48   */
    2.49  __attribute__((__nonnull__, __warn_unused_result__))
    2.50 -CxMutIterator cxListMutIterator(
    2.51 +CxMutIterator cxListMutIteratorAt(
    2.52 +        CxList *list,
    2.53 +        size_t index
    2.54 +);
    2.55 +
    2.56 +/**
    2.57 + * Returns a mutating backwards iterator pointing to the item at the
    2.58 + * specified index.
    2.59 + *
    2.60 + * The returned iterator is position-aware.
    2.61 + *
    2.62 + * If the index is out of range, a past-the-end iterator will be returned.
    2.63 + *
    2.64 + * @param list the list
    2.65 + * @param index the index where the iterator shall point at
    2.66 + * @return a new iterator
    2.67 + */
    2.68 +__attribute__((__nonnull__, __warn_unused_result__))
    2.69 +CxMutIterator cxListMutBackwardsIteratorAt(
    2.70          CxList *list,
    2.71          size_t index
    2.72  );
    2.73 @@ -491,8 +529,8 @@
    2.74   * @return a new iterator
    2.75   */
    2.76  __attribute__((__nonnull__, __warn_unused_result__))
    2.77 -static inline CxIterator cxListBegin(CxList const *list) {
    2.78 -    return list->cl->iterator(list, 0);
    2.79 +static inline CxIterator cxListIterator(CxList const *list) {
    2.80 +    return list->cl->iterator(list, 0, false);
    2.81  }
    2.82  
    2.83  /**
    2.84 @@ -506,8 +544,39 @@
    2.85   * @return a new iterator
    2.86   */
    2.87  __attribute__((__nonnull__, __warn_unused_result__))
    2.88 -static inline CxMutIterator cxListBeginMut(CxList *list) {
    2.89 -    return cxListMutIterator(list, 0);
    2.90 +static inline CxMutIterator cxListMutIterator(CxList *list) {
    2.91 +    return cxListMutIteratorAt(list, 0);
    2.92 +}
    2.93 +
    2.94 +
    2.95 +/**
    2.96 + * Returns a backwards iterator pointing to the last item of the list.
    2.97 + *
    2.98 + * The returned iterator is position-aware.
    2.99 + *
   2.100 + * If the list is empty, a past-the-end iterator will be returned.
   2.101 + *
   2.102 + * @param list the list
   2.103 + * @return a new iterator
   2.104 + */
   2.105 +__attribute__((__nonnull__, __warn_unused_result__))
   2.106 +static inline CxIterator cxListBackwardsIterator(CxList const *list) {
   2.107 +    return list->cl->iterator(list, list->size - 1, true);
   2.108 +}
   2.109 +
   2.110 +/**
   2.111 + * Returns a mutating backwards iterator pointing to the last item of the list.
   2.112 + *
   2.113 + * The returned iterator is position-aware.
   2.114 + *
   2.115 + * If the list is empty, a past-the-end iterator will be returned.
   2.116 + *
   2.117 + * @param list the list
   2.118 + * @return a new iterator
   2.119 + */
   2.120 +__attribute__((__nonnull__, __warn_unused_result__))
   2.121 +static inline CxMutIterator cxListMutBackwardsIterator(CxList *list) {
   2.122 +    return cxListMutBackwardsIteratorAt(list, list->size - 1);
   2.123  }
   2.124  
   2.125  /**
     3.1 --- a/src/linked_list.c	Wed Feb 08 20:26:26 2023 +0100
     3.2 +++ b/src/linked_list.c	Wed Feb 15 16:48:11 2023 +0100
     3.3 @@ -764,6 +764,27 @@
     3.4      }
     3.5  }
     3.6  
     3.7 +static void cx_ll_iter_prev(void *it) {
     3.8 +    struct cx_iterator_base_s *itbase = it;
     3.9 +    if (itbase->remove) {
    3.10 +        itbase->remove = false;
    3.11 +        struct cx_mut_iterator_s *iter = it;
    3.12 +        cx_linked_list *ll = iter->src_handle;
    3.13 +        cx_linked_list_node *node = iter->elem_handle;
    3.14 +        iter->elem_handle = node->prev;
    3.15 +        iter->index--;
    3.16 +        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
    3.17 +                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
    3.18 +        ll->base.size--;
    3.19 +        cxFree(ll->base.allocator, node);
    3.20 +    } else {
    3.21 +        struct cx_iterator_s *iter = it;
    3.22 +        iter->index--;
    3.23 +        cx_linked_list_node *node = iter->elem_handle;
    3.24 +        iter->elem_handle = node->prev;
    3.25 +    }
    3.26 +}
    3.27 +
    3.28  static void *cx_ll_iter_current(void const *it) {
    3.29      struct cx_iterator_s const *iter = it;
    3.30      cx_linked_list_node *node = iter->elem_handle;
    3.31 @@ -782,7 +803,8 @@
    3.32  
    3.33  static CxIterator cx_ll_iterator(
    3.34          struct cx_list_s const *list,
    3.35 -        size_t index
    3.36 +        size_t index,
    3.37 +        bool backwards
    3.38  ) {
    3.39      CxIterator iter;
    3.40      iter.index = index;
    3.41 @@ -790,7 +812,7 @@
    3.42      iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
    3.43      iter.base.valid = cx_ll_iter_valid;
    3.44      iter.base.current = cx_ll_iter_current;
    3.45 -    iter.base.next = cx_ll_iter_next;
    3.46 +    iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
    3.47      iter.base.flag_removal = cx_ll_iter_flag_rm;
    3.48      iter.base.mutating = false;
    3.49      iter.base.remove = false;
     4.1 --- a/src/list.c	Wed Feb 08 20:26:26 2023 +0100
     4.2 +++ b/src/list.c	Wed Feb 15 16:48:11 2023 +0100
     4.3 @@ -149,9 +149,10 @@
     4.4  
     4.5  static struct cx_iterator_s cx_pl_iterator(
     4.6          struct cx_list_s const *list,
     4.7 -        size_t index
     4.8 +        size_t index,
     4.9 +        bool backwards
    4.10  ) {
    4.11 -    struct cx_iterator_s iter = list->climpl->iterator(list, index);
    4.12 +    struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
    4.13      iter.base.current_impl = iter.base.current;
    4.14      iter.base.current = cx_pl_iter_current;
    4.15      return iter;
    4.16 @@ -194,14 +195,14 @@
    4.17  void cxListDestroy(CxList *list) {
    4.18      switch (list->content_destructor_type) {
    4.19          case CX_DESTRUCTOR_SIMPLE: {
    4.20 -            CxIterator iter = cxListBegin(list);
    4.21 +            CxIterator iter = cxListIterator(list);
    4.22              cx_foreach(void*, elem, iter) {
    4.23                  list->simple_destructor(elem);
    4.24              }
    4.25              break;
    4.26          }
    4.27          case CX_DESTRUCTOR_ADVANCED: {
    4.28 -            CxIterator iter = cxListBegin(list);
    4.29 +            CxIterator iter = cxListIterator(list);
    4.30              cx_foreach(void*, elem, iter) {
    4.31                  list->advanced_destructor.func(list->advanced_destructor.data, elem);
    4.32              }
    4.33 @@ -225,8 +226,8 @@
    4.34      } else {
    4.35          // different compare functions, use iterator
    4.36          if (list->size == other->size) {
    4.37 -            CxIterator left = cxListBegin(list);
    4.38 -            CxIterator right = cxListBegin(other);
    4.39 +            CxIterator left = cxListIterator(list);
    4.40 +            CxIterator right = cxListIterator(other);
    4.41              for (size_t i = 0; i < list->size; i++) {
    4.42                  void *leftValue = cxIteratorCurrent(left);
    4.43                  void *rightValue = cxIteratorCurrent(right);
    4.44 @@ -244,11 +245,11 @@
    4.45      }
    4.46  }
    4.47  
    4.48 -CxMutIterator cxListMutIterator(
    4.49 +CxMutIterator cxListMutIteratorAt(
    4.50          CxList *list,
    4.51          size_t index
    4.52  ) {
    4.53 -    CxIterator it = list->cl->iterator(list, index);
    4.54 +    CxIterator it = list->cl->iterator(list, index, false);
    4.55      it.base.mutating = true;
    4.56  
    4.57      // we know the iterators share the same memory layout
    4.58 @@ -256,3 +257,16 @@
    4.59      memcpy(&iter, &it, sizeof(CxMutIterator));
    4.60      return iter;
    4.61  }
    4.62 +
    4.63 +CxMutIterator cxListMutBackwardsIteratorAt(
    4.64 +        CxList *list,
    4.65 +        size_t index
    4.66 +) {
    4.67 +    CxIterator it = list->cl->iterator(list, index, true);
    4.68 +    it.base.mutating = true;
    4.69 +
    4.70 +    // we know the iterators share the same memory layout
    4.71 +    CxMutIterator iter;
    4.72 +    memcpy(&iter, &it, sizeof(CxMutIterator));
    4.73 +    return iter;
    4.74 +}
     5.1 --- a/tests/test_list.cpp	Wed Feb 08 20:26:26 2023 +0100
     5.2 +++ b/tests/test_list.cpp	Wed Feb 15 16:48:11 2023 +0100
     5.3 @@ -735,13 +735,13 @@
     5.4          result = cxListSwap(list, 16, 17);
     5.5          EXPECT_NE(0, result);
     5.6  
     5.7 -        auto iter = cxListBegin(list);
     5.8 +        auto iter = cxListIterator(list);
     5.9          cx_foreach(int*, e, iter) {
    5.10              EXPECT_EQ(*e, swapped[iter.index]);
    5.11          }
    5.12 -        // TODO: replace with backward iterator
    5.13 -        cx_for_n(i, 16) {
    5.14 -            EXPECT_EQ(*((int *) cxListAt(list, i)), swapped[i]);
    5.15 +        iter = cxListBackwardsIterator(list);
    5.16 +        cx_foreach(int*, e, iter) {
    5.17 +            EXPECT_EQ(*e, swapped[iter.index]);
    5.18          }
    5.19      }
    5.20  
    5.21 @@ -777,16 +777,44 @@
    5.22      }
    5.23  
    5.24      void verifyIterator(CxList *list) const {
    5.25 -        int i = 0;
    5.26 -        auto iter = cxListBeginMut(list);
    5.27 +        auto iter = cxListIterator(list);
    5.28 +        size_t i = 0;
    5.29          cx_foreach(int*, x, iter) {
    5.30 -            ASSERT_EQ(iter.index, (size_t) (i + 1) / 2);
    5.31 -            ASSERT_EQ(*x, testdata.data[i]);
    5.32 -            if (i % 2 == 1) cxIteratorFlagRemoval(iter);
    5.33 +            ASSERT_EQ(i, iter.index);
    5.34 +            EXPECT_EQ(*x, testdata.data[iter.index]);
    5.35              i++;
    5.36          }
    5.37 +        ASSERT_EQ(i, list->size);
    5.38 +        iter = cxListBackwardsIterator(list);
    5.39 +        cx_foreach(int*, x, iter) {
    5.40 +            ASSERT_EQ(i - 1, iter.index);
    5.41 +            EXPECT_EQ(*x, testdata.data[iter.index]);
    5.42 +            i--;
    5.43 +        }
    5.44 +        ASSERT_EQ(i, 0);
    5.45          auto len = testdata_len;
    5.46 -        EXPECT_EQ(i, len);
    5.47 +        i = len / 2;
    5.48 +        auto mut_iter = cxListMutIteratorAt(list, i);
    5.49 +        size_t j = 0;
    5.50 +        cx_foreach(int*, x, mut_iter) {
    5.51 +            ASSERT_EQ(mut_iter.index, len / 2 + j / 2);
    5.52 +            ASSERT_EQ(*x, testdata.data[i]);
    5.53 +            if (i % 2 == 1) cxIteratorFlagRemoval(mut_iter);
    5.54 +            i++;
    5.55 +            j++;
    5.56 +        }
    5.57 +        ASSERT_EQ(i, len);
    5.58 +        i = len / 2;
    5.59 +        j = 0;
    5.60 +        mut_iter = cxListMutBackwardsIteratorAt(list, i - 1);
    5.61 +        cx_foreach(int*, x, mut_iter) {
    5.62 +            ASSERT_EQ(mut_iter.index, len / 2 - 1 - j);
    5.63 +            ASSERT_EQ(*x, testdata.data[i - 1]);
    5.64 +            if (i % 2 == 0) cxIteratorFlagRemoval(mut_iter);
    5.65 +            i--;
    5.66 +            j++;
    5.67 +        }
    5.68 +        ASSERT_EQ(i, 0);
    5.69          ASSERT_EQ(list->size, len / 2);
    5.70          cx_for_n(j, len / 2) ASSERT_EQ(*(int *) cxListAt(list, j), testdata.data[j * 2]);
    5.71      }
    5.72 @@ -794,7 +822,7 @@
    5.73      static void verifyInsertViaIterator(CxList *list) {
    5.74          int newdata[] = {10, 20, 30, 40, 50};
    5.75  
    5.76 -        auto iter = cxListMutIterator(list, 2);
    5.77 +        auto iter = cxListMutIteratorAt(list, 2);
    5.78          EXPECT_TRUE(cxIteratorValid(iter));
    5.79          EXPECT_EQ(iter.index, 2);
    5.80          EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2);
    5.81 @@ -807,16 +835,16 @@
    5.82          EXPECT_EQ(iter.index, 3);
    5.83          EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2);
    5.84  
    5.85 -        iter = cxListBeginMut(list);
    5.86 +        iter = cxListMutIterator(list);
    5.87          cxListInsertBefore(&iter, &newdata[2]);
    5.88          EXPECT_TRUE(cxIteratorValid(iter));
    5.89          EXPECT_EQ(iter.index, 1);
    5.90          EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 0);
    5.91 -        iter = cxListMutIterator(list, list->size);
    5.92 +        iter = cxListMutIteratorAt(list, list->size);
    5.93          cxListInsertBefore(&iter, &newdata[3]);
    5.94          EXPECT_FALSE(cxIteratorValid(iter));
    5.95          EXPECT_EQ(iter.index, 9);
    5.96 -        iter = cxListMutIterator(list, list->size);
    5.97 +        iter = cxListMutIteratorAt(list, list->size);
    5.98          cxListInsertAfter(&iter, &newdata[4]);
    5.99          EXPECT_FALSE(cxIteratorValid(iter));
   5.100          EXPECT_EQ(iter.index, 10);

mercurial