[ccan] [PATCH] list: add slist.h for singly-linked list (WIP)
David Gibson
david at gibson.dropbear.id.au
Fri Sep 30 10:07:32 AEST 2016
On Thu, Sep 29, 2016 at 06:17:12PM -0400, Emilio G. Cota wrote:
> AFAIK there's no "proper" (as in "same API as ccan/list") singly-linked
> list implementation in CCAN.
>
> The appended is obviously incomplete (e.g. no debug, no deletions) but as is
> it gives me a ~15% speedup when doing a BFS traversal of a large graph, whose
> edges are represented as an adjacency list.
>
> - Should we have something like this? I tried adding a for_each macro to
> lqueue but that was just too ugly to live. Yes, one could just use *next
> pointers in the original code, but then there's no list_for_each, and
> no trivial update path to a doubly-linked list should the need arise.
It depends a bit how much of the list interface can be duplicated.
When I wrote lstack and lqueue, it was suggested I base them on a
general singly linked list module. I investigated that, but got
bogged down in the details of what the interface should be, and how
much it could resemble the list interface. That's what I restricted
myself to the more limited special purpose interfaces.
But if you can pull it off, it would be a nice thing to have.
> - If so, should it be a separate module, a submodule under list, or just
> a header next to list.h?
> I'm just dropping slist.h here due to laziness; I like the idea of having
> both singly and doubly-linked list implementations under list/ though,
> so that they can share code and are less likely be overlooked by
> users.
Interesting question. My first inclination would have been simply to
have it as a separate module alongside list. But your suggestion of
making it a list submodule is persuasive.
>
> Signed-off-by: Emilio G. Cota <cota at braap.org>
> ---
> ccan/list/slist.h | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 195 insertions(+)
> create mode 100644 ccan/list/slist.h
>
> diff --git a/ccan/list/slist.h b/ccan/list/slist.h
> new file mode 100644
> index 0000000..d0eae32
> --- /dev/null
> +++ b/ccan/list/slist.h
> @@ -0,0 +1,195 @@
> +/* Licensed under BSD-MIT - see LICENSE file for details */
> +#ifndef CCAN_SLIST_H
> +#define CCAN_SLIST_H
> +//#define CCAN_SLIST_DEBUG 1
> +#include <ccan/str/str.h>
> +#include <ccan/container_of/container_of.h>
> +#include <ccan/check_type/check_type.h>
> +
> +/**
> + * struct slist_node - an entry in a singly-linked list
> + * @next: next entry (self if empty)
> + *
> + * This is used as an entry in a singly-linked list.
> + * Example:
> + * struct child {
> + * const char *name;
> + * // Linked list of all us children.
> + * struct slist_node slist;
> + * };
> + */
> +struct slist_node {
> + struct slist_node *next;
> +};
> +
> +/**
> + * struct slist_head - the head of a singly-linked list
> + * @h: the slist_head (containing next pointer)
> + *
> + * This is used as the head of a singly-linked list.
> + * Example:
> + * struct parent {
> + * const char *name;
> + * struct slist_head children;
> + * unsigned int num_children;
> + * };
> + */
> +struct slist_head {
> + struct slist_node n;
> +};
> +
> +#define SLIST_LOC __FILE__ ":" stringify(__LINE__)
> +#ifdef CCAN_SLIST_DEBUG
> +#define slist_debug(h, loc) slist_check((h), loc)
> +#define slist_debug_node(n, loc) slist_check_node((n), loc)
> +#else
> +#define slist_debug(h, loc) ((void)loc, h)
> +#define slist_debug_node(n, loc) ((void)loc, n)
> +#endif
> +
> +/**
> + * SLIST_HEAD_INIT - initializer for an empty slist_head
> + * @name: the name of the slist.
> + *
> + * Explicit initializer for an empty slist.
> + *
> + * See also:
> + * SLIST_HEAD, slist_head_init()
> + *
> + * Example:
> + * static struct slist_head my_slist = SLIST_HEAD_INIT(my_slist);
> + */
> +#define SLIST_HEAD_INIT(name) { { &(name).n } }
> +
> +/**
> + * SLIST_HEAD - define and initialize an empty slist_head
> + * @name: the name of the slist.
> + *
> + * The SLIST_HEAD macro defines a slist_head and initializes it to an empty
> + * slist. It can be prepended by "static" to define a static slist_head.
> + *
> + * See also:
> + * SLIST_HEAD_INIT, slist_head_init()
> + *
> + * Example:
> + * static SLIST_HEAD(my_global_slist);
> + */
> +#define SLIST_HEAD(name) \
> + struct slist_head name = SLIST_HEAD_INIT(name)
> +
> +/**
> + * slist_head_init - initialize a slist_head
> + * @h: the slist_head to set to the empty slist
> + *
> + * Example:
> + * ...
> + * struct parent *parent = malloc(sizeof(*parent));
> + *
> + * slist_head_init(&parent->children);
> + * parent->num_children = 0;
> + */
> +static inline void slist_head_init(struct slist_head *h)
> +{
> + h->n.next = &h->n;
> +}
> +
> +/**
> + * slist_add - add an entry at the start of a linked list.
> + * @h: the slist_head to add the node to
> + * @n: the slist_node to add to the list.
> + *
> + * The slist_node does not need to be initialized; it will be overwritten.
> + * Example:
> + * struct child *child = malloc(sizeof(*child));
> + *
> + * child->name = "marvin";
> + * slist_add(&parent->children, &child->slist);
> + * parent->num_children++;
> + */
> +#define slist_add(h, n) slist_add_(h, n, SLIST_LOC)
> +static inline void slist_add_(struct slist_head *h,
> + struct slist_node *n,
> + const char *abortstr)
> +{
> + n->next = h->n.next;
> + h->n.next = n;
> +}
> +
> +/**
> + * slist_for_each - iterate through a slist.
> + * @h: the slist_head (warning: evaluated multiple times!)
> + * @i: the structure containing the slist_node
> + * @member: the slist_node member of the structure
> + *
> + * This is a convenient wrapper to iterate @i over the entire slist. It's
> + * a for loop, so you can break and continue as normal.
> + *
> + * Example:
> + * slist_for_each(&parent->children, child, slist)
> + * printf("Name: %s\n", child->name);
> + */
> +#define slist_for_each(h, i, member) \
> + slist_for_each_off(h, i, slist_off_var_(i, member))
> +
> +/**
> + * slist_for_each_off - iterate through a slist of memory regions.
> + * @h: the slist_head
> + * @i: the pointer to a memory region wich contains slist node data.
> + * @off: offset(relative to @i) at which slist node data resides.
> + *
> + * This is a low-level wrapper to iterate @i over the entire slist, 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 assumption made is that @i points
> + * to a chunk of memory that at some @offset, relative to @i, contains a
> + * properly filled `struct slist_node' which in turn contains pointers to
> + * memory chunks and it's turtles all the way down. With all that in mind
> + * remember that given the wrong pointer/offset couple this macro will
> + * happily 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 slist_node'
> + * member(preferably 0), because it allows you not to disclose the type of
> + * @i.
> + *
> + * Example:
> + * slist_for_each_off(&parent->children, child,
> + * offsetof(struct child, slist))
> + * printf("Name: %s\n", child->name);
> + */
> +#define slist_for_each_off(h, i, off) \
> + for (i = slist_node_to_off_(slist_debug(h, SLIST_LOC)->n.next, \
> + (off)); \
> + slist_node_from_off_((void *)i, (off)) != &(h)->n; \
> + i = slist_node_to_off_(slist_node_from_off_((void *)i, \
> + (off))->next, (off)))
> +
> +/* Offset helper functions so we only single-evaluate. */
> +static inline void *slist_node_to_off_(struct slist_node *node, size_t off)
> +{
> + return (void *)((char *)node - off);
> +}
> +static inline struct slist_node *slist_node_from_off_(void *ptr, size_t off)
> +{
> + return (struct slist_node *)((char *)ptr + off);
> +}
> +
> +/* Get the offset of the member, but make sure it's a slist_node. */
> +#define slist_off_(type, member) \
> + (container_off(type, member) + \
> + check_type(((type *)0)->member, struct slist_node))
> +
> +#define slist_off_var_(var, member) \
> + (container_off_var(var, member) + \
> + check_type(var->member, struct slist_node))
> +
> +#if HAVE_TYPEOF
> +#define slist_typeof(var) typeof(var)
> +#else
> +#define slist_typeof(var) void *
> +#endif
> +
> +#endif /* CCAN_SLIST_H */
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.ozlabs.org/pipermail/ccan/attachments/20160930/0ac88cf0/attachment-0001.sig>
More information about the ccan
mailing list