mirror of
				https://github.com/nyanmisaka/mpp.git
				synced 2025-10-31 04:26:39 +08:00 
			
		
		
		
	 ea564af5c6
			
		
	
	ea564af5c6
	
	
	
		
			
			Porting from kernel lib/list_sort.c Signed-off-by: Yandong Lin <yandong.lin@rock-chips.com> Change-Id: If6b0e07a863a8e95b47f2b89a2941d21674c9108
		
			
				
	
	
		
			659 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			659 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright 2015 Rockchip Electronics Co. LTD
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #define MODULE_TAG "mpp_list"
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdarg.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include "mpp_log.h"
 | |
| #include "mpp_list.h"
 | |
| #include "mpp_common.h"
 | |
| 
 | |
| 
 | |
| #define LIST_DEBUG(fmt, ...) mpp_log(fmt, ## __VA_ARGS__)
 | |
| #define LIST_ERROR(fmt, ...) mpp_err(fmt, ## __VA_ARGS__)
 | |
| 
 | |
| RK_U32 mpp_list::keys = 0;
 | |
| 
 | |
| typedef struct mpp_list_node {
 | |
|     mpp_list_node*  prev;
 | |
|     mpp_list_node*  next;
 | |
|     RK_U32          key;
 | |
|     RK_S32          size;
 | |
| } mpp_list_node;
 | |
| 
 | |
| static inline void list_node_init(mpp_list_node *node)
 | |
| {
 | |
|     node->prev = node->next = node;
 | |
| }
 | |
| 
 | |
| static inline void list_node_init_with_key_and_size(mpp_list_node *node, RK_U32 key, RK_S32 size)
 | |
| {
 | |
|     list_node_init(node);
 | |
|     node->key   = key;
 | |
|     node->size  = size;
 | |
| }
 | |
| 
 | |
| static mpp_list_node* create_list(void *data, RK_S32 size, RK_U32 key)
 | |
| {
 | |
|     mpp_list_node *node = (mpp_list_node*)malloc(sizeof(mpp_list_node) + size);
 | |
|     if (node) {
 | |
|         void *dst = (void*)(node + 1);
 | |
|         list_node_init_with_key_and_size(node, key, size);
 | |
|         memcpy(dst, data, size);
 | |
|     } else {
 | |
|         LIST_ERROR("failed to allocate list node");
 | |
|     }
 | |
|     return node;
 | |
| }
 | |
| 
 | |
| static inline void _mpp_list_add(mpp_list_node * _new, mpp_list_node * prev, mpp_list_node * next)
 | |
| {
 | |
|     next->prev = _new;
 | |
|     _new->next = next;
 | |
|     _new->prev = prev;
 | |
|     prev->next = _new;
 | |
| }
 | |
| 
 | |
| static inline void mpp_list_add(mpp_list_node *_new, mpp_list_node *head)
 | |
| {
 | |
|     _mpp_list_add(_new, head, head->next);
 | |
| }
 | |
| 
 | |
| static inline void mpp_list_add_tail(mpp_list_node *_new, mpp_list_node *head)
 | |
| {
 | |
|     _mpp_list_add(_new, head->prev, head);
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::add_at_head(void *data, RK_S32 size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head) {
 | |
|         mpp_list_node *node = create_list(data, size, 0);
 | |
|         if (node) {
 | |
|             mpp_list_add(node, head);
 | |
|             count++;
 | |
|             ret = 0;
 | |
|         } else {
 | |
|             ret = -ENOMEM;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::add_at_tail(void *data, RK_S32 size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head) {
 | |
|         mpp_list_node *node = create_list(data, size, 0);
 | |
|         if (node) {
 | |
|             mpp_list_add_tail(node, head);
 | |
|             count++;
 | |
|             ret = 0;
 | |
|         } else {
 | |
|             ret = -ENOMEM;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void release_list(mpp_list_node*node, void *data, RK_S32 size)
 | |
| {
 | |
|     void *src = (void*)(node + 1);
 | |
|     if (node->size == size) {
 | |
|         if (data)
 | |
|             memcpy(data, src, size);
 | |
|     } else {
 | |
|         LIST_ERROR("node size check failed when release_list");
 | |
|         size = (size < node->size) ? (size) : (node->size);
 | |
|         if (data)
 | |
|             memcpy(data, src, size);
 | |
|     }
 | |
|     free(node);
 | |
| }
 | |
| 
 | |
| static inline void _mpp_list_del(mpp_list_node *prev, mpp_list_node *next)
 | |
| {
 | |
|     next->prev = prev;
 | |
|     prev->next = next;
 | |
| }
 | |
| 
 | |
| static inline void mpp_list_del_init(mpp_list_node *node)
 | |
| {
 | |
|     _mpp_list_del(node->prev, node->next);
 | |
|     list_node_init(node);
 | |
| }
 | |
| 
 | |
| static inline void _list_del_node_no_lock(mpp_list_node *node, void *data, RK_S32 size)
 | |
| {
 | |
|     mpp_list_del_init(node);
 | |
|     release_list(node, data, size);
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::del_at_head(void *data, RK_S32 size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head && count) {
 | |
|         _list_del_node_no_lock(head->next, data, size);
 | |
|         count--;
 | |
|         ret = 0;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::del_at_tail(void *data, RK_S32 size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head && count) {
 | |
|         _list_del_node_no_lock(head->prev, data, size);
 | |
|         count--;
 | |
|         ret = 0;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static mpp_list_node* create_list_with_size(void *data, RK_S32 size, RK_U32 key)
 | |
| {
 | |
|     mpp_list_node *node = (mpp_list_node*)malloc(sizeof(mpp_list_node) +
 | |
|                                                  sizeof(size) + size);
 | |
|     if (node) {
 | |
|         RK_S32 *dst = (RK_S32 *)(node + 1);
 | |
|         list_node_init_with_key_and_size(node, key, size);
 | |
|         *dst++ = size;
 | |
|         memcpy(dst, data, size);
 | |
|     } else {
 | |
|         LIST_ERROR("failed to allocate list node");
 | |
|     }
 | |
|     return node;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::fifo_wr(void *data, RK_S32 size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head) {
 | |
|         mpp_list_node *node = create_list_with_size(data, size, 0);
 | |
|         if (node) {
 | |
|             mpp_list_add_tail(node, head);
 | |
|             count++;
 | |
|             ret = 0;
 | |
|         } else {
 | |
|             ret = -ENOMEM;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void release_list_with_size(mpp_list_node* node, void *data, RK_S32 *size)
 | |
| {
 | |
|     RK_S32 *src = (RK_S32*)(node + 1);
 | |
|     RK_S32 data_size = *src++;
 | |
| 
 | |
|     *size = data_size;
 | |
| 
 | |
|     if (data)
 | |
|         memcpy(data, src, data_size);
 | |
| 
 | |
|     free(node);
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::fifo_rd(void *data, RK_S32 *size)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     if (head && count) {
 | |
|         mpp_list_node *node = head->next;
 | |
| 
 | |
|         mpp_list_del_init(node);
 | |
|         release_list_with_size(node, data, size);
 | |
|         count--;
 | |
|         ret = 0;
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::list_is_empty()
 | |
| {
 | |
|     RK_S32 ret = (count == 0);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::list_size()
 | |
| {
 | |
|     RK_S32 ret = count;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::add_by_key(void *data, RK_S32 size, RK_U32 *key)
 | |
| {
 | |
|     RK_S32 ret = 0;
 | |
|     if (head) {
 | |
|         RK_U32 list_key = get_key();
 | |
|         *key = list_key;
 | |
|         mpp_list_node *node = create_list(data, size, list_key);
 | |
|         if (node) {
 | |
|             mpp_list_add_tail(node, head);
 | |
|             count++;
 | |
|             ret = 0;
 | |
|         } else {
 | |
|             ret = -ENOMEM;
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::del_by_key(void *data, RK_S32 size, RK_U32 key)
 | |
| {
 | |
|     RK_S32 ret = 0;
 | |
|     if (head && count) {
 | |
|         struct mpp_list_node *tmp = head->next;
 | |
|         ret = -EINVAL;
 | |
|         while (tmp->next != head) {
 | |
|             if (tmp->key == key) {
 | |
|                 _list_del_node_no_lock(tmp, data, size);
 | |
|                 count--;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| RK_S32 mpp_list::show_by_key(void *data, RK_U32 key)
 | |
| {
 | |
|     RK_S32 ret = -EINVAL;
 | |
|     (void)data;
 | |
|     (void)key;
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| RK_S32 mpp_list::flush()
 | |
| {
 | |
|     if (head) {
 | |
|         while (count) {
 | |
|             mpp_list_node* node = head->next;
 | |
|             mpp_list_del_init(node);
 | |
|             if (destroy) {
 | |
|                 destroy((void*)(node + 1));
 | |
|             }
 | |
|             free(node);
 | |
|             count--;
 | |
|         }
 | |
|     }
 | |
|     signal();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| RK_U32 mpp_list::get_key()
 | |
| {
 | |
|     return keys++;
 | |
| }
 | |
| 
 | |
| mpp_list::mpp_list(node_destructor func)
 | |
|     : destroy(NULL),
 | |
|       head(NULL),
 | |
|       count(0)
 | |
| {
 | |
|     destroy = func;
 | |
|     head = (mpp_list_node*)malloc(sizeof(mpp_list_node));
 | |
|     if (NULL == head) {
 | |
|         LIST_ERROR("failed to allocate list header");
 | |
|     } else {
 | |
|         list_node_init_with_key_and_size(head, 0, 0);
 | |
|     }
 | |
| }
 | |
| 
 | |
| mpp_list::~mpp_list()
 | |
| {
 | |
|     flush();
 | |
|     if (head) free(head);
 | |
|     head = NULL;
 | |
|     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>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #define LOOP_RK_LIST    600
 | |
| 
 | |
| #define COUNT_ADD       100
 | |
| #define COUNT_DEL       99
 | |
| 
 | |
| volatile int err = 0;
 | |
| 
 | |
| static int mpp_list_fifo_test(mpp_list *list_0)
 | |
| {
 | |
|     int count;
 | |
|     VPUMemLinear_t m;
 | |
|     for (count = 0; count < COUNT_ADD; count++) {
 | |
|         err |= VPUMallocLinear(&m, 100);
 | |
|         if (err) {
 | |
|             printf("VPUMallocLinear in mpp_list_fifo_test\n");
 | |
|             break;
 | |
|         }
 | |
|         err |= list_0->add_at_head(&m, sizeof(m));
 | |
|         if (err) {
 | |
|             printf("add_at_head in mpp_list_fifo_test\n");
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!err) {
 | |
|         for (count = 0; count < COUNT_DEL; count++) {
 | |
|             err |= list_0->del_at_tail(&m, sizeof(m));
 | |
|             if (err) {
 | |
|                 printf("del_at_tail in mpp_list_fifo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|             err |= VPUFreeLinear(&m);
 | |
|             if (err) {
 | |
|                 printf("VPUFreeLinear in mpp_list_fifo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static int mpp_list_filo_test(mpp_list *list_0)
 | |
| {
 | |
|     int count;
 | |
|     VPUMemLinear_t m;
 | |
|     for (count = 0; count < COUNT_ADD + COUNT_DEL; count++) {
 | |
|         if (count & 1) {
 | |
|             err |= list_0->del_at_head(&m, sizeof(m));
 | |
|             if (err) {
 | |
|                 printf("del_at_head in mpp_list_filo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|             err |= VPUFreeLinear(&m);
 | |
|             if (err) {
 | |
|                 printf("VPUFreeLinear in mpp_list_fifo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|         } else {
 | |
|             err |= VPUMallocLinear(&m, 100);
 | |
|             if (err) {
 | |
|                 printf("VPUMallocLinear in mpp_list_filo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|             err |= list_0->add_at_head(&m, sizeof(m));
 | |
|             if (err) {
 | |
|                 printf("add_at_head in mpp_list_fifo_test\n");
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| 
 | |
| void *mpp_list_test_loop_0(void *pdata)
 | |
| {
 | |
|     int i;
 | |
|     mpp_list *list_0 = (mpp_list *)pdata;
 | |
| 
 | |
|     printf("mpp_list test 0 loop start\n");
 | |
|     for (i = 0; i < LOOP_RK_LIST; i++) {
 | |
|         err |= mpp_list_filo_test(list_0);
 | |
|         if (err) break;
 | |
|     }
 | |
| 
 | |
|     if (err) {
 | |
|         printf("thread: found vpu mem operation err %d\n", err);
 | |
|     } else {
 | |
|         printf("thread: test done and found no err\n");
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| int mpp_list_test_0()
 | |
| {
 | |
|     int i, err = 0;
 | |
|     printf("mpp_list test 0 FIFO start\n");
 | |
| 
 | |
|     mpp_list *list_0 = new mpp_list((node_destructor)VPUFreeLinear);
 | |
| 
 | |
|     pthread_t mThread;
 | |
|     pthread_attr_t attr;
 | |
|     pthread_attr_init(&attr);
 | |
|     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
 | |
| 
 | |
|     pthread_create(&mThread, &attr, mpp_list_test_loop_0, (void*)list_0);
 | |
|     pthread_attr_destroy(&attr);
 | |
| 
 | |
|     for (i = 0; i < LOOP_RK_LIST; i++) {
 | |
|         err |= mpp_list_fifo_test(list_0);
 | |
|         if (err) break;
 | |
|     }
 | |
|     if (err) {
 | |
|         printf("main  : found mpp_list operation err %d\n", err);
 | |
|     } else {
 | |
|         printf("main  : test done and found no err\n");
 | |
|     }
 | |
| 
 | |
|     void *dummy;
 | |
|     pthread_join(mThread, &dummy);
 | |
| 
 | |
|     printf("mpp_list test 0 end size %d\n", list_0->list_size());
 | |
|     delete list_0;
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| #define TOTAL_RK_LIST_TEST_COUNT    1
 | |
| 
 | |
| typedef int (*RK_LIST_TEST_FUNC)(void);
 | |
| RK_LIST_TEST_FUNC test_func[TOTAL_RK_LIST_TEST_COUNT] = {
 | |
|     mpp_list_test_0,
 | |
| };
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
|     int i, start = 0, end = 0;
 | |
|     if (argc < 2) {
 | |
|         end = TOTAL_RK_LIST_TEST_COUNT;
 | |
|     } else if (argc == 2) {
 | |
|         start = atoi(argv[1]);
 | |
|         end   = start + 1;
 | |
|     } else if (argc == 3) {
 | |
|         start = atoi(argv[1]);
 | |
|         end   = atoi(argv[2]);
 | |
|     } else {
 | |
|         printf("too many argc %d\n", argc);
 | |
|         return -1;
 | |
|     }
 | |
|     if (start < 0 || start > TOTAL_RK_LIST_TEST_COUNT || end < 0 || end > TOTAL_RK_LIST_TEST_COUNT) {
 | |
|         printf("invalid input: start %d end %d\n", start, end);
 | |
|         return -1;
 | |
|     }
 | |
|     for (i = start; i < end; i++) {
 | |
|         int err = test_func[i]();
 | |
|         if (err) {
 | |
|             printf("test case %d return err %d\n", i, err);
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| #endif
 | |
| 
 |