mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-30 19:48:19 +00:00 
			
		
		
		
	In various places it is useful to have an array of structures, but allow it to grow. In some cases we work around it by setting maximum number of entries, using a Kconfig option. In other places we use a linked list, which does not provide for random access and can complicate the code. Introduce a new data structure, which is a variable-sized list of structs each of the same, pre-set size. It provides O(1) access and is reasonably efficient at expanding linearly, since it doubles in size when it runs out of space. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			243 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			5.5 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);
 |