Commit e59b7388 authored by Andrey Smirnov's avatar Andrey Smirnov Committed by Rusty Russell

list.h: opaque list iteration functionality

See https://github.com/rustyrussell/ccan/pull/1#issuecomment-2856561
for details.
(Reworked with minor cleanups -- Rusty)
parent 87f505a5
......@@ -326,9 +326,7 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* printf("Name: %s\n", child->name);
*/
#define list_for_each(h, i, member) \
for (i = container_of_var(list_debug(h)->n.next, i, member); \
&i->member != &(h)->n; \
i = container_of_var(i->member.next, i, member))
list_for_each_off(h, i, list_off_var_(i, member))
/**
* list_for_each_rev - iterate through a list backwards.
......@@ -367,14 +365,105 @@ static inline const void *list_tail_(const struct list_head *h, size_t off)
* }
*/
#define list_for_each_safe(h, i, nxt, member) \
for (i = container_of_var(list_debug(h)->n.next, i, member), \
nxt = container_of_var(i->member.next, i, member); \
&i->member != &(h)->n; \
i = nxt, nxt = container_of_var(i->member.next, i, member))
list_for_each_safe_off(h, i, nxt, list_off_var_(i, member))
/**
* list_for_each_off - iterate through a list of memory regions.
* @h: the list_head
* @i: the pointer to a memory region wich contains list node data.
* @off: offset(relative to @i) at which list node data resides.
*
* This is a low-level wrapper to iterate @i over the entire list, used to
* implement all oher, more high-level, for-each constructs. It's a for loop,
* so you can break and continue as normal.
*
* WARNING! Being the low-level macro that it is, this wrapper doesn't know
* nor care about the type of @i. The only assumtion made is that @i points
* to a chunk of memory that at some @offset, relative to @i, contains a
* properly filled `struct node_list' which in turn contains pointers to
* memory chunks and it's turtles all the way down. Whith all that in mind
* remember that given the wrong pointer/offset couple this macro will
* happilly churn all you memory untill SEGFAULT stops it, in other words
* caveat emptor.
*
* It is worth mentioning that one of legitimate use-cases for that wrapper
* is operation on opaque types with known offset for `struct list_node'
* member(preferably 0), because it allows you not to disclose the type of
* @i.
*
* Example:
* list_for_each_off(&parent->children, child,
* offsetof(struct child, list))
* printf("Name: %s\n", child->name);
*/
#define list_for_each_off(h, i, off) \
for (i = list_node_to_off_(list_debug(h)->n.next, (off)); \
list_node_from_off_((void *)i, (off)) != &(h)->n; \
i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \
(off)))
/**
* list_for_each_safe_off - iterate through a list of memory regions, maybe
* during deletion
* @h: the list_head
* @i: the pointer to a memory region wich contains list node data.
* @nxt: the structure containing the list_node
* @off: offset(relative to @i) at which list node data resides.
*
* For details see `list_for_each_off' and `list_for_each_safe'
* descriptions.
*
* Example:
* list_for_each_safe_off(&parent->children, child,
* next, offsetof(struct child, list))
* printf("Name: %s\n", child->name);
*/
#define list_for_each_safe_off(h, i, nxt, off) \
for (i = list_node_to_off_(list_debug(h)->n.next, (off)), \
nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \
(off)); \
list_node_from_off_(i, (off)) != &(h)->n; \
i = nxt, \
nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \
(off)))
/* Other -off variants. */
#define list_entry_off(n, type, off) \
((type *)list_node_from_off_((n), (off)))
#define list_head_off(h, type, off) \
((type *)list_head_off((h), (off)))
#define list_tail_off(h, type, off) \
((type *)list_tail_((h), (off)))
#define list_add_off(h, n, off) \
list_add((h), list_node_from_off_((n), (off)))
#define list_del_off(n, off) \
list_del(list_node_from_off_((n), (off)))
#define list_del_from_off(h, n, off) \
list_del_from(h, list_node_from_off_((n), (off)))
/* Offset helper functions so we only single-evaluate. */
static inline void *list_node_to_off_(struct list_node *node, size_t off)
{
return (void *)((char *)node - off);
}
static inline struct list_node *list_node_from_off_(void *ptr, size_t off)
{
return (struct list_node *)((char *)ptr + off);
}
/* Get the offset of the member, but make sure it's a list_node. */
#define list_off_(type, member) \
(container_off(type, member) + \
check_type(((type *)0)->member, struct list_node))
#define list_off_var_(var, member) \
(container_off_var(var, member) + \
check_type(var->member, struct list_node))
#endif /* CCAN_LIST_H */
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <ccan/list/list.h>
#include "helper.h"
#define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \
(42)
struct opaque {
struct list_node list;
size_t secret_offset;
char secret_drawer[42];
};
static bool not_randomized = true;
struct opaque *create_opaque_blob(void)
{
struct opaque *blob = calloc(1, sizeof(struct opaque));
if (not_randomized) {
srandom((int)time(NULL));
not_randomized = false;
}
blob->secret_offset = random() % (sizeof(blob->secret_drawer));
blob->secret_drawer[blob->secret_offset] =
ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING;
return blob;
}
bool if_blobs_know_the_secret(struct opaque *blob)
{
bool answer = true;
int i;
for (i = 0; i < sizeof(blob->secret_drawer) /
sizeof(blob->secret_drawer[0]); i++)
if (i != blob->secret_offset)
answer = answer && (blob->secret_drawer[i] == 0);
else
answer = answer &&
(blob->secret_drawer[blob->secret_offset] ==
ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING);
return answer;
}
void destroy_opaque_blob(struct opaque *blob)
{
free(blob);
}
/* These are in a separate C file so we can test undefined structures. */
struct opaque;
typedef struct opaque opaque_t;
opaque_t *create_opaque_blob(void);
bool if_blobs_know_the_secret(opaque_t *blob);
void destroy_opaque_blob(opaque_t *blob);
#include <ccan/list/list.h>
#include <ccan/tap/tap.h>
#include <ccan/list/list.c>
#include "helper.h"
struct parent {
const char *name;
......@@ -21,8 +22,10 @@ int main(int argc, char *argv[])
struct child c1, c2, c3, *c, *n;
unsigned int i;
struct list_head list = LIST_HEAD_INIT(list);
opaque_t *q, *nq;
struct list_head opaque_list = LIST_HEAD_INIT(opaque_list);
plan_tests(53);
plan_tests(65);
/* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
ok1(list_empty(&static_list));
ok1(list_check(&static_list, NULL));
......@@ -147,6 +150,49 @@ int main(int argc, char *argv[])
ok1(i == 3);
ok1(list_empty(&parent.children));
/* Test list_for_each_off. */
list_add_tail(&opaque_list,
(struct list_node *)create_opaque_blob());
list_add_tail(&opaque_list,
(struct list_node *)create_opaque_blob());
list_add_tail(&opaque_list,
(struct list_node *)create_opaque_blob());
i = 0;
list_for_each_off(&opaque_list, q, 0) {
i++;
ok1(if_blobs_know_the_secret(q));
}
ok1(i == 3);
/* Test list_for_each_safe_off, list_del_off and list_del_from_off. */
i = 0;
list_for_each_safe_off(&opaque_list, q, nq, 0) {
switch (i++) {
case 0:
ok1(if_blobs_know_the_secret(q));
list_del_off(q, 0);
destroy_opaque_blob(q);
break;
case 1:
ok1(if_blobs_know_the_secret(q));
list_del_from_off(&opaque_list, q, 0);
destroy_opaque_blob(q);
break;
case 2:
ok1(c == &c3);
list_del_from_off(&opaque_list, q, 0);
destroy_opaque_blob(q);
break;
}
ok1(list_check(&opaque_list, NULL));
if (i > 2)
break;
}
ok1(i == 3);
ok1(list_empty(&opaque_list));
/* Test list_top/list_tail on empty list. */
ok1(list_top(&parent.children, struct child, list) == NULL);
ok1(list_tail(&parent.children, struct child, list) == NULL);
......
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