Re: [PATCH] lists: add list sorting routine

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



One note: the code is added to the header file instead of its own C file
so we don't have to update all the build rules of the list.h users.

Thanks,
Davidlohr

On Mon, 2011-12-12 at 23:52 +0100, Davidlohr Bueso wrote:
> From: Davidlohr Bueso <dave@xxxxxxx>
> 
> We need a list sorting function, just to mention one example user that could benefit is the lib/tt code to sort columns.
> 
> This patch adds list_sort() which uses the Merge Sort algorithm, behaving nicely in O(nlog(n)), heavily based on the kernel's implementation[1].
> The private data (void *priv) passed to the comparison function as been removed to adopt a qsort(3)-like syntax, and IMHO we don't really need it anyways.
> 
> [1]: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=lib/list_sort.c;h=d7325c6b103f0be078ff3672c35c468ed35738f1;hb=HEAD
> 
> Signed-off-by: Davidlohr Bueso <dave@xxxxxxx>
> ---
>  include/c.h    |    8 +++
>  include/list.h |  132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 140 insertions(+), 0 deletions(-)
> 
> diff --git a/include/c.h b/include/c.h
> index c4a95aa..290303e 100644
> --- a/include/c.h
> +++ b/include/c.h
> @@ -104,6 +104,14 @@
>  	_max1 > _max2 ? _max1 : _max2; })
>  #endif
>  
> +#ifndef offsetof
> +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
> +#endif
> +
> +#define container_of(ptr, type, member) ({                      \
> +			const typeof( ((type *)0)->member ) *__mptr = (ptr); \
> +			(type *)( (char *)__mptr - offsetof(type,member) );})
> +
>  #ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
>  # ifdef HAVE___PROGNAME
>  extern char *__progname;
> diff --git a/include/list.h b/include/list.h
> index f7b17fe..afcfeeb 100644
> --- a/include/list.h
> +++ b/include/list.h
> @@ -3,6 +3,7 @@
>   * Copyright (C) 1999-2008 by Theodore Ts'o
>   *
>   * (based on list.h from e2fsprogs)
> + * Merge sort based on kernel's implementation.
>   */
>  
>  #ifndef UTIL_LINUX_LIST_H
> @@ -192,6 +193,137 @@ _INLINE_ void list_splice(struct list_head *list, struct list_head *head)
>  	for (pos = (head)->next, pnext = pos->next; pos != (head); \
>  	     pos = pnext, pnext = pos->next)
>  
> +#define MAX_LIST_LENGTH_BITS 20
> +
> +/*
> + * Returns a list organized in an intermediate format suited
> + * to chaining of merge() calls: null-terminated, no reserved or
> + * sentinel head node, "prev" links not maintained.
> + */
> +static struct list_head *merge(int (*cmp)(struct list_head *a,
> +					  struct list_head *b),
> +			       struct list_head *a, struct list_head *b)
> +{
> +	struct list_head head, *tail = &head;
> +
> +	while (a && b) {
> +		/* if equal, take 'a' -- important for sort stability */
> +		if ((*cmp)(a, b) <= 0) {
> +			tail->next = a;
> +			a = a->next;
> +		} else {
> +			tail->next = b;
> +			b = b->next;
> +		}
> +		tail = tail->next;
> +	}
> +	tail->next = a?:b;
> +	return head.next;
> +}
> +
> +/*
> + * Combine final list merge with restoration of standard doubly-linked
> + * list structure.  This approach duplicates code from merge(), but
> + * runs faster than the tidier alternatives of either a separate final
> + * prev-link restoration pass, or maintaining the prev links
> + * throughout.
> + */
> +static void merge_and_restore_back_links(int (*cmp)(struct list_head *a,
> +						    struct list_head *b),
> +					 struct list_head *head,
> +					 struct list_head *a, struct list_head *b)
> +{
> +	struct list_head *tail = head;
> +
> +	while (a && b) {
> +		/* if equal, take 'a' -- important for sort stability */
> +		if ((*cmp)(a, b) <= 0) {
> +			tail->next = a;
> +			a->prev = tail;
> +			a = a->next;
> +		} else {
> +			tail->next = b;
> +			b->prev = tail;
> +			b = b->next;
> +		}
> +		tail = tail->next;
> +	}
> +	tail->next = a ? : b;
> +
> +	do {
> +		/*
> +		 * In worst cases this loop may run many iterations.
> +		 * Continue callbacks to the client even though no
> +		 * element comparison is needed, so the client's cmp()
> +		 * routine can invoke cond_resched() periodically.
> +		 */
> +		(*cmp)(tail->next, tail->next);
> +
> +		tail->next->prev = tail;
> +		tail = tail->next;
> +	} while (tail->next);
> +
> +	tail->next = head;
> +	head->prev = tail;
> +}
> +
> +
> +/**
> + * list_sort - sort a list
> + * @head: the list to sort
> + * @cmp: the elements comparison function
> + *
> + * This function implements "merge sort", which has O(nlog(n))
> + * complexity.
> + *
> + * The comparison function @cmp must return a negative value if @a
> + * should sort before @b, and a positive value if @a should sort after
> + * @b. If @a and @b are equivalent, and their original relative
> + * ordering is to be preserved, @cmp must return 0.
> + */
> +_INLINE_ void list_sort(struct list_head *head,
> +			int (*cmp)(struct list_head *a,
> +				   struct list_head *b))
> +{
> +	struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
> +							   -- last slot is a sentinel */
> +	int lev;  /* index into part[] */
> +	int max_lev = 0;
> +	struct list_head *list;
> +
> +	if (list_empty(head))
> +		return;
> +
> +	memset(part, 0, sizeof(part));
> +
> +	head->prev->next = NULL;
> +	list = head->next;
> +
> +	while (list) {
> +		struct list_head *cur = list;
> +		list = list->next;
> +		cur->next = NULL;
> +
> +		for (lev = 0; part[lev]; lev++) {
> +			cur = merge(cmp, part[lev], cur);
> +			part[lev] = NULL;
> +		}
> +		if (lev > max_lev) {
> +			/* list passed to list_sort() too long for efficiency */
> +			if (lev >= ARRAY_SIZE(part)-1)
> +				lev--;
> +			max_lev = lev;
> +		}
> +		part[lev] = cur;
> +	}
> +
> +	for (lev = 0; lev < max_lev; lev++)
> +		if (part[lev])
> +			list = merge(cmp, part[lev], list);
> +
> +	merge_and_restore_back_links(cmp, head, part[max_lev], list);
> +}
> +
>  #undef _INLINE_
>  
>  #endif /* UTIL_LINUX_LIST_H */


--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux