1 /**
2  * @file lv_iter.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_assert.h"
11 
12 #include "lv_iter.h"
13 
14 #include "lv_circle_buf.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 
20 /**********************
21  *      TYPEDEFS
22  **********************/
23 
24 struct _lv_iter_t {
25     /* Iterator state */
26     void  *  instance;        /**< Pointer to the object to iterate over */
27     uint32_t elem_size;       /**< Size of one element in bytes */
28     void  *  context;         /**< Custom context for the iteration */
29     uint32_t context_size;    /**< Size of the custom context in bytes */
30 
31     /* Peeking */
32     lv_circle_buf_t * peek_buf;   /**< Circular buffer for peeking */
33     uint32_t peek_offset;         /**< Offset in the peek buffer */
34 
35     /* Callbacks */
36     lv_iter_next_cb next_cb;  /**< Callback to get the next element */
37 };
38 
39 /**********************
40  *  STATIC PROTOTYPES
41  **********************/
42 
43 static bool peek_fill_cb(void * buf, uint32_t buf_len, int32_t index, void * user_data);
44 
45 /**********************
46  *  GLOBAL VARIABLES
47  **********************/
48 
49 /**********************
50  *  STATIC VARIABLES
51  **********************/
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
lv_iter_create(void * instance,const uint32_t elem_size,const uint32_t context_size,lv_iter_next_cb next_cb)61 lv_iter_t * lv_iter_create(void * instance, const uint32_t elem_size, const uint32_t context_size,
62                            lv_iter_next_cb next_cb)
63 {
64     lv_iter_t * iter = lv_malloc_zeroed(sizeof(lv_iter_t));
65     LV_ASSERT_MALLOC(iter);
66 
67     if(iter == NULL) {
68         LV_LOG_ERROR("Could not allocate memory for iterator");
69         return NULL;
70     }
71 
72     iter->instance = instance;
73     iter->elem_size = elem_size;
74     iter->context_size = context_size;
75     iter->next_cb = next_cb;
76 
77     if(context_size > 0) {
78         iter->context = lv_malloc_zeroed(context_size);
79         LV_ASSERT_MALLOC(iter->context);
80     }
81 
82     return iter;
83 }
84 
lv_iter_get_context(const lv_iter_t * iter)85 void * lv_iter_get_context(const lv_iter_t * iter)
86 {
87     LV_ASSERT_NULL(iter);
88 
89     return iter ? iter->context : NULL;
90 }
91 
lv_iter_destroy(lv_iter_t * iter)92 void lv_iter_destroy(lv_iter_t * iter)
93 {
94     LV_ASSERT_NULL(iter);
95     if(iter == NULL) return;
96 
97     if(iter->context_size > 0) lv_free(iter->context);
98     if(iter->peek_buf != NULL) lv_circle_buf_destroy(iter->peek_buf);
99 
100     iter->context = NULL;
101     iter->peek_buf = NULL;
102 
103     lv_free(iter);
104 }
105 
lv_iter_make_peekable(lv_iter_t * iter,const uint32_t capacity)106 void lv_iter_make_peekable(lv_iter_t * iter, const uint32_t capacity)
107 {
108     LV_ASSERT_NULL(iter);
109     if(iter == NULL) return;
110 
111     if(capacity == 0 || iter->peek_buf != NULL) return;
112     iter->peek_buf = lv_circle_buf_create(capacity, iter->elem_size);
113     LV_ASSERT_NULL(iter->peek_buf);
114 }
115 
lv_iter_next(lv_iter_t * iter,void * elem)116 lv_result_t lv_iter_next(lv_iter_t * iter, void * elem)
117 {
118     LV_ASSERT_NULL(iter);
119     if(iter == NULL) return LV_RESULT_INVALID;
120 
121     lv_circle_buf_t * c_buf = iter->peek_buf;
122     if(c_buf != NULL && !lv_circle_buf_is_empty(c_buf)) {
123         if(elem) lv_circle_buf_read(c_buf, elem);
124         else lv_circle_buf_skip(c_buf);
125         iter->peek_offset = 0;
126         return LV_RESULT_OK;
127     }
128 
129     const lv_result_t iter_res = iter->next_cb(iter->instance, iter->context, elem);
130     if(iter_res == LV_RESULT_INVALID) return LV_RESULT_INVALID;
131 
132     if(c_buf != NULL) iter->peek_offset = 0;
133 
134     return iter_res;
135 }
136 
lv_iter_peek(lv_iter_t * iter,void * elem)137 lv_result_t lv_iter_peek(lv_iter_t * iter, void * elem)
138 {
139     LV_ASSERT_NULL(iter);
140     if(iter == NULL) return LV_RESULT_INVALID;
141 
142     lv_circle_buf_t * c_buf = iter->peek_buf;
143     if(c_buf == NULL) return LV_RESULT_INVALID;
144 
145     const uint32_t peek_count = lv_circle_buf_size(c_buf);
146     if(iter->peek_offset >= peek_count) {
147         const uint32_t required = iter->peek_offset + 1 - peek_count;
148         const uint32_t filled = lv_circle_buf_fill(c_buf, required, peek_fill_cb, iter);
149         if(filled != required) return LV_RESULT_INVALID;
150     }
151 
152     lv_circle_buf_peek_at(c_buf, iter->peek_offset, elem);
153 
154     return LV_RESULT_OK;
155 }
156 
lv_iter_peek_advance(lv_iter_t * iter)157 lv_result_t lv_iter_peek_advance(lv_iter_t * iter)
158 {
159     LV_ASSERT_NULL(iter);
160     if(iter == NULL) return LV_RESULT_INVALID;
161 
162     if(iter->peek_buf == NULL || iter->peek_offset + 1 >= lv_circle_buf_capacity(iter->peek_buf))
163         return LV_RESULT_INVALID;
164     iter->peek_offset++;
165     return LV_RESULT_OK;
166 }
167 
lv_iter_peek_reset(lv_iter_t * iter)168 lv_result_t lv_iter_peek_reset(lv_iter_t * iter)
169 {
170     LV_ASSERT_NULL(iter);
171     if(iter == NULL) return LV_RESULT_INVALID;
172 
173     if(iter->peek_buf == NULL) return LV_RESULT_INVALID;
174 
175     iter->peek_offset = 0;
176     return LV_RESULT_OK;
177 }
178 
lv_iter_inspect(lv_iter_t * iter,const lv_iter_inspect_cb inspect_cb)179 void lv_iter_inspect(lv_iter_t * iter, const lv_iter_inspect_cb inspect_cb)
180 {
181     LV_ASSERT_NULL(iter);
182     if(iter == NULL) return;
183 
184     void * elem = lv_malloc_zeroed(iter->elem_size);
185     LV_ASSERT_MALLOC(elem);
186 
187     if(elem == NULL) {
188         LV_LOG_ERROR("Could not allocate memory for element");
189         return;
190     }
191 
192     while(lv_iter_next(iter, elem) == LV_RESULT_OK) {
193         inspect_cb(elem);
194     }
195 
196     lv_free(elem);
197 }
198 
199 /**********************
200  *   STATIC FUNCTIONS
201  **********************/
202 
peek_fill_cb(void * buf,const uint32_t buf_len,const int32_t index,void * user_data)203 static bool peek_fill_cb(void * buf, const uint32_t buf_len, const int32_t index, void * user_data)
204 {
205     LV_UNUSED(buf_len);
206     LV_UNUSED(index);
207 
208     const lv_iter_t * iter = user_data;
209     const lv_result_t iter_res = iter->next_cb(iter->instance, iter->context, buf);
210     if(iter_res == LV_RESULT_INVALID) return false;
211 
212     return true;
213 }
214