Commit 38694b56 authored by Vladimír Čunát's avatar Vladimír Čunát

lib/generic/queue: add iterators

The typing around queue_it_begin() isn't ideal,
but I'm unable to come with anything better.
I'm really missing generics for these things.
parent a2eafcc0
......@@ -37,9 +37,18 @@
queue_pop(q);
assert(queue_head(q) == 2);
assert(queue_tail(q) == 4);
// you may iterate
typedef queue_it_t(int) queue_it_int_t;
for (queue_it_int_t it = queue_it_begin(q); !queue_it_finished(it);
queue_it_next(it)) {
++queue_it_val(it);
}
assert(queue_tail(q) == 5);
queue_push_head(q, 0);
++queue_tail(q);
assert(queue_tail(q) == 5);
assert(queue_tail(q) == 6);
// free it up
queue_deinit(q);
......@@ -106,6 +115,36 @@
((const size_t)(q).queue.len)
/** @brief Type for queue iterator, parametrized by value type.
* It's a simple structure that owns no other resources.
* You may NOT use it after doing any push or pop (without _begin again). */
#define queue_it_t(type) \
union { \
type *pdata_t; /* only the *type* information is used */ \
struct queue_it iter; \
}
/** @brief Initialize a queue iterator at the head of the queue.
* If you use this in assignment (instead of initialization),
* you will unfortunately need to add corresponding type-cast in front.
* Beware: there's no type-check between queue and iterator! */
#define queue_it_begin(q) \
{ .iter = queue_it_begin_impl(&(q).queue) }
/** @brief Return a "reference" to the current element (it's an L-value) . */
#define queue_it_val(it) \
( *(__typeof__((it).pdata_t)) queue_it_val_impl(&(it).iter) )
/** @brief Test if the iterator has gone past the last element.
* If it has, you may not use _val or _next. */
#define queue_it_finished(it) \
queue_it_finished_impl(&(it).iter)
/** @brief Advance the iterator to the next element. */
#define queue_it_next(it) \
queue_it_next_impl(&(it).iter)
/* ====================== Internal for the implementation ================== */
/** @cond internal */
......@@ -176,6 +215,43 @@ static inline void queue_pop_impl(struct queue *q)
--(q->len);
}
struct queue_it {
struct queue_chunk *chunk;
int16_t pos, item_size;
};
static inline struct queue_it queue_it_begin_impl(struct queue *q)
{
assert(q);
return (struct queue_it){
.chunk = q->head,
.pos = q->head ? q->head->begin : -1,
.item_size = q->item_size,
};
}
static inline bool queue_it_finished_impl(struct queue_it *it)
{
return it->chunk == NULL || it->pos >= it->chunk->end;
}
static inline void * queue_it_val_impl(struct queue_it *it)
{
assert(!queue_it_finished_impl(it));
return it->chunk->data + it->pos * it->item_size;
}
static inline void queue_it_next_impl(struct queue_it *it)
{
assert(!queue_it_finished_impl(it));
++(it->pos);
if (it->pos < it->chunk->end)
return;
it->chunk = it->chunk->next;
it->pos = it->chunk ? it->chunk->begin : -1;
}
/** @endcond (internal) */
/** @} (addtogroup generics) */
......@@ -19,6 +19,7 @@
/* The main intention is to use queues with pointers, so we test the same-sized int. */
typedef queue_t(ptrdiff_t) queue_int_t;
typedef queue_it_t(int) queue_int_it_t;
static void test_int(void **state_)
{
......@@ -40,6 +41,15 @@ static void test_int(void **state_)
}
assert_int_equal(queue_len(q), 3 + 99);
/* Basic iterator test. */
int i = 0;
for (queue_int_it_t it = queue_it_begin(q); !queue_it_finished(it);
queue_it_next(it)) {
++queue_it_val(it);
++i;
}
assert_int_equal(queue_len(q), i);
queue_deinit(q);
queue_init(q);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment