lists: add list sorting routine
PATCH 1/1 lists: add list sorting routine
From: Davidlohr Bueso <[email protected]>
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.
--- include/c.h | 8 +++ include/list.h | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 0 deletions(-)
include/c.h +8 -0
@@ -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;
include/list.h +132 -0
144 lines not on this page · raw message
Signature · 1 line
1.7.4.1
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
Quoted 184 lines · Davidlohr Bueso
From: Davidlohr Bueso <[email protected]>
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.Signed-off-by Davidlohr Bueso <[email protected]>--- include/c.h | 8 +++ include/list.h | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 0 deletions(-)include/c.h +8
-0@@ -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;include/list.h +132
-0@@ -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 */
Karel Zak
maintainerQuoted 2 lines · Davidlohr Bueso
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.husers.
[...]
Quoted 3 lines
include/list.h _INLINE_ void list_splice(struct list_head *list, struct list_head *head) · line 205
Quoted 3 lines
include/list.h +3
-0+static struct list_head *merge (int (*cmp)(struct list_head *a, +struct list_head *b), +struct list_head *a,struct list_head *b)
shouldn't be the function "static inline" (or _INLINE_ ) ?
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Quoted 11 lines · Karel Zak
Quoted 2 lines · Davidlohr Bueso
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.husers.[...]
Quoted 3 lines
include/list.h _INLINE_ void list_splice(struct list_head *list, struct list_head *head) · line 205
Quoted 3 lines
include/list.h +3
-0+static struct list_head *merge (int (*cmp)(struct list_head *a, +struct list_head *b), +struct list_head *a,struct list_head *b)shouldn't be the function "static inline" (or _INLINE_ ) ?
Hmm yes, do you want me to send another patch with the correction?
Karel Zak
maintainerQuoted 7 lines · Davidlohr Bueso
Quoted 6 lines · Karel Zak
Quoted 2 lines · Davidlohr Bueso
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.husers.[...]
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
include/list.h _INLINE_ void list_splice(struct list_head *list, struct list_head *head) · line 205
Quoted 3 lines
include/list.h +3
-0+static struct list_head *merge (int (*cmp)(struct list_head *a, +struct list_head *b), +struct list_head *a,struct list_head *b)shouldn't be the function "static inline" (or _INLINE_ ) ?
Hmm yes, do you want me to send another patch with the correction?
No, I'll fix it.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
Quoted 3 lines · Davidlohr Bueso
include/c.h | 8 +++ include/list.h | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 0 deletions(-)
Applied, thanks.
include/list.h _INLINE_ void list_splice(struct list_head *list, struct list_head *head) · line 291
Quoted 4 lines
include/list.h +4
-0+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 ;
size_t
Quoted 12 lines
include/list.h +12
-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;
include/list.h _INLINE_ void list_splice(struct list_head *list, struct list_head *head) · line 313
Quoted 10 lines
include/list.h +10
-0+ 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 )
use -Wsign-compare to get more fun :-)
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com