mirror of
				https://github.com/nyanmisaka/mpp.git
				synced 2025-10-31 12:36:44 +08:00 
			
		
		
		
	[mpp_list]: Add list_sort func
Porting from kernel lib/list_sort.c Signed-off-by: Yandong Lin <yandong.lin@rock-chips.com> Change-Id: If6b0e07a863a8e95b47f2b89a2941d21674c9108
This commit is contained in:
		| @@ -196,6 +196,10 @@ static __inline int list_empty(struct list_head *head) | ||||
|     return head->next == head; | ||||
| } | ||||
|  | ||||
| typedef RK_S32 (*list_cmp_func_t)(void *, const struct list_head *, const struct list_head *); | ||||
|  | ||||
| void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -23,6 +23,7 @@ | ||||
|  | ||||
| #include "mpp_log.h" | ||||
| #include "mpp_list.h" | ||||
| #include "mpp_common.h" | ||||
|  | ||||
|  | ||||
| #define LIST_DEBUG(fmt, ...) mpp_log(fmt, ## __VA_ARGS__) | ||||
| @@ -325,6 +326,166 @@ mpp_list::~mpp_list() | ||||
|     destroy = NULL; | ||||
| } | ||||
|  | ||||
| /* list sort porting from kernel list_sort.c */ | ||||
|  | ||||
| /* | ||||
|  * 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(void *priv, list_cmp_func_t cmp, | ||||
|                                struct list_head *a, struct list_head *b) | ||||
| { | ||||
|     struct list_head *head, **tail = &head; | ||||
|  | ||||
|     for (;;) { | ||||
|         /* if equal, take 'a' -- important for sort stability */ | ||||
|         if (cmp(priv, a, b) <= 0) { | ||||
|             *tail = a; | ||||
|             tail = &a->next; | ||||
|             a = a->next; | ||||
|             if (!a) { | ||||
|                 *tail = b; | ||||
|                 break; | ||||
|             } | ||||
|         } else { | ||||
|             *tail = b; | ||||
|             tail = &b->next; | ||||
|             b = b->next; | ||||
|             if (!b) { | ||||
|                 *tail = a; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return head; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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_final(void *priv, list_cmp_func_t cmp, struct list_head *head, | ||||
|                         struct list_head *a, struct list_head *b) | ||||
| { | ||||
|     struct list_head *tail = head; | ||||
|     RK_U8 count = 0; | ||||
|  | ||||
|     for (;;) { | ||||
|         /* if equal, take 'a' -- important for sort stability */ | ||||
|         if (cmp(priv, a, b) <= 0) { | ||||
|             tail->next = a; | ||||
|             a->prev = tail; | ||||
|             tail = a; | ||||
|             a = a->next; | ||||
|             if (!a) | ||||
|                 break; | ||||
|         } else { | ||||
|             tail->next = b; | ||||
|             b->prev = tail; | ||||
|             tail = b; | ||||
|             b = b->next; | ||||
|             if (!b) { | ||||
|                 b = a; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Finish linking remainder of list b on to tail */ | ||||
|     tail->next = b; | ||||
|     do { | ||||
|         /* | ||||
|          * If the merge is highly unbalanced (e.g. the input is | ||||
|          * already sorted), 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. | ||||
|          */ | ||||
|         if (!++count) | ||||
|             cmp(priv, b, b); | ||||
|         b->prev = tail; | ||||
|         tail = b; | ||||
|         b = b->next; | ||||
|     } while (b); | ||||
|  | ||||
|     /* And the final links to make a circular doubly-linked list */ | ||||
|     tail->next = head; | ||||
|     head->prev = tail; | ||||
| } | ||||
|  | ||||
| void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp) | ||||
| { | ||||
|     struct list_head *list = head->next, *pending = NULL; | ||||
|     size_t count = 0;   /* Count of pending */ | ||||
|  | ||||
|     if (list == head->prev) /* Zero or one elements */ | ||||
|         return; | ||||
|  | ||||
|     /* Convert to a null-terminated singly-linked list. */ | ||||
|     head->prev->next = NULL; | ||||
|  | ||||
|     /* | ||||
|      * Data structure invariants: | ||||
|      * - All lists are singly linked and null-terminated; prev | ||||
|      *   pointers are not maintained. | ||||
|      * - pending is a prev-linked "list of lists" of sorted | ||||
|      *   sublists awaiting further merging. | ||||
|      * - Each of the sorted sublists is power-of-two in size. | ||||
|      * - Sublists are sorted by size and age, smallest & newest at front. | ||||
|      * - There are zero to two sublists of each size. | ||||
|      * - A pair of pending sublists are merged as soon as the number | ||||
|      *   of following pending elements equals their size (i.e. | ||||
|      *   each time count reaches an odd multiple of that size). | ||||
|      *   That ensures each later final merge will be at worst 2:1. | ||||
|      * - Each round consists of: | ||||
|      *   - Merging the two sublists selected by the highest bit | ||||
|      *     which flips when count is incremented, and | ||||
|      *   - Adding an element from the input as a size-1 sublist. | ||||
|      */ | ||||
|     do { | ||||
|         size_t bits; | ||||
|         struct list_head **tail = &pending; | ||||
|  | ||||
|         /* Find the least-significant clear bit in count */ | ||||
|         for (bits = count; bits & 1; bits >>= 1) | ||||
|             tail = &(*tail)->prev; | ||||
|         /* Do the indicated merge */ | ||||
|         if (bits) { | ||||
|             struct list_head *a = *tail, *b = a->prev; | ||||
|  | ||||
|             a = merge(priv, cmp, b, a); | ||||
|             /* Install the merged result in place of the inputs */ | ||||
|             a->prev = b->prev; | ||||
|             *tail = a; | ||||
|         } | ||||
|  | ||||
|         /* Move one element from input list to pending */ | ||||
|         list->prev = pending; | ||||
|         pending = list; | ||||
|         list = list->next; | ||||
|         pending->next = NULL; | ||||
|         count++; | ||||
|     } while (list); | ||||
|  | ||||
|     /* End of input; merge together all the pending lists. */ | ||||
|     list = pending; | ||||
|     pending = pending->prev; | ||||
|     for (;;) { | ||||
|         struct list_head *next = pending->prev; | ||||
|  | ||||
|         if (!next) | ||||
|             break; | ||||
|         list = merge(priv, cmp, pending, list); | ||||
|         pending = next; | ||||
|     } | ||||
|     /* The final merge, rebuilding prev links */ | ||||
|     merge_final(priv, cmp, head, pending, list); | ||||
| } | ||||
|  | ||||
| #if BUILD_RK_LIST_TEST | ||||
| #include "vpu_mem.h" | ||||
| #include <stdio.h> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yandong Lin
					Yandong Lin