mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	Unlike linked lists, it is inefficient to remove items from an alist, particularly if it is large. If most items need to be removed, then the time-complexity approaches O(n2). Provide a way to do this efficiently, by working through the alist once and copying elements down. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			496 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			496 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright 2023 Google LLC
 | |
|  * Written by Simon Glass <sjg@chromium.org>
 | |
|  */
 | |
| 
 | |
| #include <alist.h>
 | |
| #include <string.h>
 | |
| #include <test/lib.h>
 | |
| #include <test/test.h>
 | |
| #include <test/ut.h>
 | |
| 
 | |
| struct my_struct {
 | |
| 	uint val;
 | |
| 	uint other_val;
 | |
| };
 | |
| 
 | |
| enum {
 | |
| 	obj_size	= sizeof(struct my_struct),
 | |
| };
 | |
| 
 | |
| /* Test alist_init() */
 | |
| static int lib_test_alist_init(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	/* with a size of 0, the fields should be inited, with no memory used */
 | |
| 	memset(&lst, '\xff', sizeof(lst));
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	ut_asserteq_ptr(NULL, lst.data);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(0, lst.alloc);
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 	alist_uninit(&lst);
 | |
| 	ut_asserteq_ptr(NULL, lst.data);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(0, lst.alloc);
 | |
| 
 | |
| 	/* use an impossible size */
 | |
| 	ut_asserteq(false, alist_init(&lst, obj_size,
 | |
| 				      CONFIG_SYS_MALLOC_LEN));
 | |
| 	ut_assertnull(lst.data);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(0, lst.alloc);
 | |
| 
 | |
| 	/* use a small size */
 | |
| 	ut_assert(alist_init(&lst, obj_size, 4));
 | |
| 	ut_assertnonnull(lst.data);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(4, lst.alloc);
 | |
| 
 | |
| 	/* free it */
 | |
| 	alist_uninit(&lst);
 | |
| 	ut_asserteq_ptr(NULL, lst.data);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(0, lst.alloc);
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_init, 0);
 | |
| 
 | |
| /* Test alist_get() and alist_getd() */
 | |
| static int lib_test_alist_get(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 	void *ptr;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init(&lst, obj_size, 3));
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(3, lst.alloc);
 | |
| 
 | |
| 	ut_assertnull(alist_get_ptr(&lst, 2));
 | |
| 	ut_assertnull(alist_get_ptr(&lst, 3));
 | |
| 
 | |
| 	ptr = alist_ensure_ptr(&lst, 1);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(2, lst.count);
 | |
| 	ptr = alist_ensure_ptr(&lst, 2);
 | |
| 	ut_asserteq(3, lst.count);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 
 | |
| 	ptr = alist_ensure_ptr(&lst, 3);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(4, lst.count);
 | |
| 	ut_asserteq(6, lst.alloc);
 | |
| 
 | |
| 	ut_assertnull(alist_get_ptr(&lst, 4));
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_get, 0);
 | |
| 
 | |
| /* Test alist_has() */
 | |
| static int lib_test_alist_has(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 	void *ptr;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init(&lst, obj_size, 3));
 | |
| 
 | |
| 	ut_assert(!alist_has(&lst, 0));
 | |
| 	ut_assert(!alist_has(&lst, 1));
 | |
| 	ut_assert(!alist_has(&lst, 2));
 | |
| 	ut_assert(!alist_has(&lst, 3));
 | |
| 
 | |
| 	/* create a new one to force expansion */
 | |
| 	ptr = alist_ensure_ptr(&lst, 4);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 
 | |
| 	ut_assert(alist_has(&lst, 0));
 | |
| 	ut_assert(alist_has(&lst, 1));
 | |
| 	ut_assert(alist_has(&lst, 2));
 | |
| 	ut_assert(alist_has(&lst, 3));
 | |
| 	ut_assert(alist_has(&lst, 4));
 | |
| 	ut_assert(!alist_has(&lst, 5));
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_has, 0);
 | |
| 
 | |
| /* Test alist_ensure() */
 | |
| static int lib_test_alist_ensure(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct my_struct *ptr3, *ptr4;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	ut_asserteq(obj_size, lst.obj_size);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(0, lst.alloc);
 | |
| 	ptr3 = alist_ensure_ptr(&lst, 3);
 | |
| 	ut_asserteq(4, lst.count);
 | |
| 	ut_asserteq(4, lst.alloc);
 | |
| 	ut_assertnonnull(ptr3);
 | |
| 	ptr3->val = 3;
 | |
| 
 | |
| 	ptr4 = alist_ensure_ptr(&lst, 4);
 | |
| 	ut_asserteq(8, lst.alloc);
 | |
| 	ut_asserteq(5, lst.count);
 | |
| 	ut_assertnonnull(ptr4);
 | |
| 	ptr4->val = 4;
 | |
| 	ut_asserteq(4, alist_get(&lst, 4, struct my_struct)->val);
 | |
| 
 | |
| 	ut_asserteq_ptr(ptr4, alist_ensure(&lst, 4, struct my_struct));
 | |
| 
 | |
| 	alist_ensure(&lst, 4, struct my_struct)->val = 44;
 | |
| 	ut_asserteq(44, alist_get(&lst, 4, struct my_struct)->val);
 | |
| 	ut_asserteq(3, alist_get(&lst, 3, struct my_struct)->val);
 | |
| 	ut_assertnull(alist_get(&lst, 7, struct my_struct));
 | |
| 	ut_asserteq(8, lst.alloc);
 | |
| 	ut_asserteq(5, lst.count);
 | |
| 
 | |
| 	/* add some more, checking handling of malloc() failure */
 | |
| 	malloc_enable_testing(0);
 | |
| 	ut_assertnonnull(alist_ensure(&lst, 7, struct my_struct));
 | |
| 	ut_assertnull(alist_ensure(&lst, 8, struct my_struct));
 | |
| 	malloc_disable_testing();
 | |
| 
 | |
| 	lst.flags &= ~ALISTF_FAIL;
 | |
| 	ut_assertnonnull(alist_ensure(&lst, 8, struct my_struct));
 | |
| 	ut_asserteq(16, lst.alloc);
 | |
| 	ut_asserteq(9, lst.count);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_ensure, 0);
 | |
| 
 | |
| /* Test alist_add() bits not tested by lib_test_alist_ensure() */
 | |
| static int lib_test_alist_add(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct my_struct data, *ptr, *ptr2;
 | |
| 	const struct my_struct *chk;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 
 | |
| 	data.val = 123;
 | |
| 	data.other_val = 456;
 | |
| 	ptr = alist_add(&lst, data);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(4, lst.alloc);
 | |
| 	ut_asserteq(1, lst.count);
 | |
| 
 | |
| 	ut_asserteq(123, ptr->val);
 | |
| 	ut_asserteq(456, ptr->other_val);
 | |
| 
 | |
| 	ptr2 = alist_add_placeholder(&lst);
 | |
| 	ut_assertnonnull(ptr2);
 | |
| 
 | |
| 	ptr2->val = 321;
 | |
| 	ptr2->other_val = 654;
 | |
| 
 | |
| 	chk = alist_get(&lst, 1, struct my_struct);
 | |
| 	ut_asserteq(321, chk->val);
 | |
| 	ut_asserteq(654, chk->other_val);
 | |
| 
 | |
| 	ptr2 = alist_getw(&lst, 1, struct my_struct);
 | |
| 	ut_asserteq(321, ptr2->val);
 | |
| 	ut_asserteq(654, ptr2->other_val);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_add, 0);
 | |
| 
 | |
| /* Test alist_next()  */
 | |
| static int lib_test_alist_next(struct unit_test_state *uts)
 | |
| {
 | |
| 	const struct my_struct *ptr;
 | |
| 	struct my_struct data, *ptr2;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	data.val = 123;
 | |
| 	data.other_val = 0;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	data.val = 321;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	data.val = 789;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	ptr = alist_get(&lst, 0, struct my_struct);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(123, ptr->val);
 | |
| 
 | |
| 	ptr = alist_next(&lst, ptr);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(321, ptr->val);
 | |
| 
 | |
| 	ptr2 = (struct my_struct *)ptr;
 | |
| 	ptr2 = alist_nextw(&lst, ptr2);
 | |
| 	ut_assertnonnull(ptr2);
 | |
| 
 | |
| 	ptr = alist_next(&lst, ptr);
 | |
| 	ut_assertnonnull(ptr);
 | |
| 	ut_asserteq(789, ptr->val);
 | |
| 	ut_asserteq_ptr(ptr, ptr2);
 | |
| 	ptr2->val = 89;
 | |
| 	ut_asserteq(89, ptr->val);
 | |
| 
 | |
| 	ptr = alist_next(&lst, ptr);
 | |
| 	ut_assertnull(ptr);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_next, 0);
 | |
| 
 | |
| /* Test alist_for_each()  */
 | |
| static int lib_test_alist_for_each(struct unit_test_state *uts)
 | |
| {
 | |
| 	const struct my_struct *ptr;
 | |
| 	struct my_struct data, *ptr2;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 	int sum;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	ut_asserteq_ptr(NULL, alist_end(&lst, struct my_struct));
 | |
| 
 | |
| 	sum = 0;
 | |
| 	alist_for_each(ptr, &lst)
 | |
| 		sum++;
 | |
| 	ut_asserteq(0, sum);
 | |
| 
 | |
| 	alist_for_each(ptr, &lst)
 | |
| 		sum++;
 | |
| 	ut_asserteq(0, sum);
 | |
| 
 | |
| 	/* add three items */
 | |
| 	data.val = 1;
 | |
| 	data.other_val = 0;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	ptr = lst.data;
 | |
| 	ut_asserteq_ptr(ptr + 1, alist_end(&lst, struct my_struct));
 | |
| 
 | |
| 	data.val = 2;
 | |
| 	alist_add(&lst, data);
 | |
| 	ut_asserteq_ptr(ptr + 2, alist_end(&lst, struct my_struct));
 | |
| 
 | |
| 	data.val = 3;
 | |
| 	alist_add(&lst, data);
 | |
| 	ut_asserteq_ptr(ptr + 3, alist_end(&lst, struct my_struct));
 | |
| 
 | |
| 	/* check alist_chk_ptr() */
 | |
| 	ut_asserteq(true, alist_chk_ptr(&lst, ptr + 2));
 | |
| 	ut_asserteq(false, alist_chk_ptr(&lst, ptr + 3));
 | |
| 	ut_asserteq(false, alist_chk_ptr(&lst, ptr + 4));
 | |
| 	ut_asserteq(true, alist_chk_ptr(&lst, ptr));
 | |
| 	ut_asserteq(false, alist_chk_ptr(&lst, ptr - 1));
 | |
| 
 | |
| 	/* sum all items */
 | |
| 	sum = 0;
 | |
| 	alist_for_each(ptr, &lst)
 | |
| 		sum += ptr->val;
 | |
| 	ut_asserteq(6, sum);
 | |
| 
 | |
| 	/* increment all items */
 | |
| 	alist_for_each(ptr2, &lst)
 | |
| 		ptr2->val += 1;
 | |
| 
 | |
| 	/* sum all items again */
 | |
| 	sum = 0;
 | |
| 	alist_for_each(ptr, &lst)
 | |
| 		sum += ptr->val;
 | |
| 	ut_asserteq(9, sum);
 | |
| 
 | |
| 	ptr = lst.data;
 | |
| 	ut_asserteq_ptr(ptr + 3, alist_end(&lst, struct my_struct));
 | |
| 
 | |
| 	/* empty the list and try again */
 | |
| 	alist_empty(&lst);
 | |
| 	ut_asserteq_ptr(ptr, alist_end(&lst, struct my_struct));
 | |
| 	ut_assertnull(alist_get(&lst, 0, struct my_struct));
 | |
| 
 | |
| 	sum = 0;
 | |
| 	alist_for_each(ptr, &lst)
 | |
| 		sum += ptr->val;
 | |
| 	ut_asserteq(0, sum);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_for_each, 0);
 | |
| 
 | |
| /* Test alist_empty()  */
 | |
| static int lib_test_alist_empty(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct my_struct data;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	data.val = 1;
 | |
| 	data.other_val = 0;
 | |
| 	alist_add(&lst, data);
 | |
| 	ut_asserteq(1, lst.count);
 | |
| 	ut_asserteq(4, lst.alloc);
 | |
| 
 | |
| 	alist_empty(&lst);
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_asserteq(4, lst.alloc);
 | |
| 	ut_assertnonnull(lst.data);
 | |
| 	ut_asserteq(sizeof(data), lst.obj_size);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_empty, 0);
 | |
| 
 | |
| static int lib_test_alist_filter(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct my_struct *from, *to, *ptr;
 | |
| 	struct my_struct data;
 | |
| 	struct alist lst;
 | |
| 	ulong start;
 | |
| 	int count;
 | |
| 
 | |
| 	start = ut_check_free();
 | |
| 
 | |
| 	ut_assert(alist_init_struct(&lst, struct my_struct));
 | |
| 	data.val = 1;
 | |
| 	data.other_val = 0;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	data.val = 2;
 | |
| 	alist_add(&lst, data);
 | |
| 
 | |
| 	data.val = 3;
 | |
| 	alist_add(&lst, data);
 | |
| 	ptr = lst.data;
 | |
| 
 | |
| 	/* filter out all values except 2 */
 | |
| 	alist_for_each_filter(from, to, &lst) {
 | |
| 		if (from->val != 2)
 | |
| 			*to++ = *from;
 | |
| 	}
 | |
| 	alist_update_end(&lst, to);
 | |
| 
 | |
| 	ut_asserteq(2, lst.count);
 | |
| 	ut_assertnonnull(lst.data);
 | |
| 
 | |
| 	ut_asserteq(1, alist_get(&lst, 0, struct my_struct)->val);
 | |
| 	ut_asserteq(3, alist_get(&lst, 1, struct my_struct)->val);
 | |
| 	ut_asserteq_ptr(ptr + 3, from);
 | |
| 	ut_asserteq_ptr(ptr + 2, to);
 | |
| 
 | |
| 	/* filter out nothing */
 | |
| 	alist_for_each_filter(from, to, &lst) {
 | |
| 		if (from->val != 2)
 | |
| 			*to++ = *from;
 | |
| 	}
 | |
| 	alist_update_end(&lst, to);
 | |
| 	ut_asserteq_ptr(ptr + 2, from);
 | |
| 	ut_asserteq_ptr(ptr + 2, to);
 | |
| 
 | |
| 	ut_asserteq(2, lst.count);
 | |
| 	ut_assertnonnull(lst.data);
 | |
| 
 | |
| 	ut_asserteq(1, alist_get(&lst, 0, struct my_struct)->val);
 | |
| 	ut_asserteq(3, alist_get(&lst, 1, struct my_struct)->val);
 | |
| 
 | |
| 	/* filter out everything */
 | |
| 	alist_for_each_filter(from, to, &lst) {
 | |
| 		if (from->val == 2)
 | |
| 			*to++ = *from;
 | |
| 	}
 | |
| 	alist_update_end(&lst, to);
 | |
| 	ut_asserteq_ptr(ptr + 2, from);
 | |
| 	ut_asserteq_ptr(ptr, to);
 | |
| 
 | |
| 	/* filter out everything (nop) */
 | |
| 	count = 0;
 | |
| 	alist_for_each_filter(from, to, &lst) {
 | |
| 		if (from->val == 2)
 | |
| 			*to++ = *from;
 | |
| 		count++;
 | |
| 	}
 | |
| 	alist_update_end(&lst, to);
 | |
| 	ut_asserteq_ptr(ptr, from);
 | |
| 	ut_asserteq_ptr(ptr, to);
 | |
| 	ut_asserteq(0, count);
 | |
| 
 | |
| 	ut_asserteq(0, lst.count);
 | |
| 	ut_assertnonnull(lst.data);
 | |
| 
 | |
| 	alist_uninit(&lst);
 | |
| 
 | |
| 	/* Check for memory leaks */
 | |
| 	ut_assertok(ut_check_delta(start));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| LIB_TEST(lib_test_alist_filter, 0);
 |