blob: af13fd44daf211918a6594393e3fa457b039bf0b [file] [log] [blame]
Radek Krejci859a15a2021-03-05 20:56:59 +01001/**
2 * @file tree_edit.h
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang generic macros and functions to modify YANG schema or data trees. Intended for internal use and libyang
5 * plugins.
6 *
7 * Copyright (c) 2019-2021 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#ifndef LY_TREE_EDIT_H_
17#define LY_TREE_EDIT_H_
18
19#include <stdlib.h>
20
Radek Krejci859a15a2021-03-05 20:56:59 +010021#ifndef LOGMEM
22#define LOGMEM(CTX)
23#endif
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29/**
30 * @brief Wrapper for realloc() call. The only difference is that if it fails to
31 * allocate the requested memory, the original memory is freed as well.
32 *
33 * @param[in] ptr Memory to reallocate.
34 * @param[in] size New size of the memory block.
35 *
36 * @return Pointer to the new memory, NULL on error.
37 */
38void *ly_realloc(void *ptr, size_t size);
39
40/**
41 * @defgroup trees_edit Trees - modification
42 * @ingroup trees
43 *
44 * Generic macros, functions, etc. to modify [schema](@ref schematree) and [data](@ref datatree) trees.
45 * @{
46 */
47
Radek Krejci6b273352021-04-13 20:33:42 +020048/**
49 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
50 *
51 * Increases the size information.
52 *
53 * This is a generic macro for ::LY_ARRAY_NEW_RET and ::LY_ARRAY_NEW_GOTO.
54 *
55 * @param[in] CTX libyang context for logging.
56 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
57 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
Radek Krejci6b273352021-04-13 20:33:42 +020058 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
59 */
60#define LY_ARRAY_NEW(CTX, ARRAY, EACTION) \
61 { \
Michal Vasko59e90fc2021-09-22 12:17:08 +020062 char *p__; \
Radek Krejci6b273352021-04-13 20:33:42 +020063 if (ARRAY) { \
64 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
Michal Vasko59e90fc2021-09-22 12:17:08 +020065 p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
Radek Krejci6b273352021-04-13 20:33:42 +020066 sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
67 if (!p__) { \
Michal Vasko99db3e72022-05-24 10:50:11 +020068 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
Radek Krejci6b273352021-04-13 20:33:42 +020069 LOGMEM(CTX); \
70 EACTION; \
71 } \
72 } else { \
Michal Vasko59e90fc2021-09-22 12:17:08 +020073 p__ = (char *)malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
Radek Krejci6b273352021-04-13 20:33:42 +020074 if (!p__) { \
75 LOGMEM(CTX); \
76 EACTION; \
77 } \
78 *((LY_ARRAY_COUNT_TYPE*)(p__)) = 1; \
79 } \
Michal Vasko59e90fc2021-09-22 12:17:08 +020080 p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
Radek Krejci6b273352021-04-13 20:33:42 +020081 memcpy(&(ARRAY), &p__, sizeof p__); \
82 }
Radek Krejci859a15a2021-03-05 20:56:59 +010083
84/**
85 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
86 *
87 * Increases the size information.
88 *
89 * @param[in] CTX libyang context for logging.
90 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
91 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
92 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
93 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
94 */
95#define LY_ARRAY_NEW_RET(CTX, ARRAY, NEW_ITEM, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +020096 LY_ARRAY_NEW(CTX, ARRAY, return RETVAL); \
97 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
98 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
Radek Krejci859a15a2021-03-05 20:56:59 +010099
100/**
101 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
102 *
103 * Increases the size information.
104 *
105 * @param[in] CTX libyang context for logging.
106 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
107 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
108 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
109 * @param[out] RET Variable to store error code.
110 * @param[in] GOTO Label to go in case of error (memory allocation failure).
111 */
112#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
Radek Krejci6b273352021-04-13 20:33:42 +0200113 LY_ARRAY_NEW(CTX, ARRAY, RET = LY_EMEM; goto GOTO); \
114 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
115 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
116
117/**
118 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
119 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
120 *
121 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
122 * when the items are filled.
123 *
124 * This is a generic macro for ::LY_ARRAY_CREATE_RET and ::LY_ARRAY_CREATE_GOTO.
125 *
126 * @param[in] CTX libyang context for logging.
127 * @param[in,out] ARRAY Pointer to the array to create.
128 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
129 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
130 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
131 */
132#define LY_ARRAY_CREATE(CTX, ARRAY, SIZE, EACTION) \
133 { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200134 char *p__; \
Radek Krejci6b273352021-04-13 20:33:42 +0200135 if (ARRAY) { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200136 p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
Radek Krejci6b273352021-04-13 20:33:42 +0200137 sizeof(LY_ARRAY_COUNT_TYPE) + ((*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
138 if (!p__) { \
Radek Krejci859a15a2021-03-05 20:56:59 +0100139 LOGMEM(CTX); \
Radek Krejci6b273352021-04-13 20:33:42 +0200140 EACTION; \
141 } \
142 } else { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200143 p__ = (char *)calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
Radek Krejci6b273352021-04-13 20:33:42 +0200144 if (!p__) { \
145 LOGMEM(CTX); \
146 EACTION; \
Radek Krejci859a15a2021-03-05 20:56:59 +0100147 } \
148 } \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200149 p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
Radek Krejci6b273352021-04-13 20:33:42 +0200150 memcpy(&(ARRAY), &p__, sizeof p__); \
151 if (ARRAY) { \
152 memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
153 } \
154 }
Radek Krejci859a15a2021-03-05 20:56:59 +0100155
156/**
157 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
158 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
159 *
160 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
161 * when the items are filled.
162 *
163 * @param[in] CTX libyang context for logging.
164 * @param[in,out] ARRAY Pointer to the array to create.
165 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
166 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
167 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
168 */
169#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200170 LY_ARRAY_CREATE(CTX, ARRAY, SIZE, return RETVAL)
Radek Krejci859a15a2021-03-05 20:56:59 +0100171
172/**
173 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
174 * If the ARRAY already exists, it is resized (space for SIZE items is added).
175 *
176 * Does not set the count information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
177 * when the items are filled.
178 *
179 * @param[in] CTX libyang context for logging.
180 * @param[in,out] ARRAY Pointer to the array to create.
181 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
182 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
183 * @param[out] RET Variable to store error code.
184 * @param[in] GOTO Label to go in case of error (memory allocation failure).
185 */
186#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
Radek Krejci6b273352021-04-13 20:33:42 +0200187 LY_ARRAY_CREATE(CTX, ARRAY, SIZE, RET = LY_EMEM; goto GOTO)
Radek Krejci859a15a2021-03-05 20:56:59 +0100188
189/**
190 * @brief Increment the items counter in a ([sized array](@ref sizedarrays)).
191 *
192 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
193 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
194 *
195 * @param[in] ARRAY Pointer to the array to affect.
196 */
197#define LY_ARRAY_INCREMENT(ARRAY) \
198 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
199
200/**
201 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)).
202 *
203 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
204 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
205 *
206 * @param[in] ARRAY Pointer to the array to affect.
207 */
208#define LY_ARRAY_DECREMENT(ARRAY) \
209 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
210
211/**
212 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
213 * in case it was decremented to 0.
214 *
215 * @param[in] ARRAY Pointer to the array to affect.
216 */
217#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
218 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
219 if (!LY_ARRAY_COUNT(ARRAY)) { \
220 LY_ARRAY_FREE(ARRAY); \
221 (ARRAY) = NULL; \
222 }
223
224/**
225 * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
226 *
227 * The items inside the array are not freed.
228 *
229 * @param[in] ARRAY A ([sized array](@ref sizedarrays)) to be freed.
230 */
231#define LY_ARRAY_FREE(ARRAY) \
232 if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}
233
234/**
stewegf9041a22024-01-18 13:29:12 +0100235 * @brief Remove item from array based on value
236 *
237 * @param[in, out] ARRAY A ([sized array](@ref sizedarrays)) to be modified.
238 * @param[in] VALUE The item value to be removed. Only the first occurence will be removed.
239 */
240#define LY_ARRAY_REMOVE_VALUE(ARRAY, VALUE) \
241 { \
242 LY_ARRAY_COUNT_TYPE index__; \
243 LY_ARRAY_FOR(ARRAY, index__) { \
steweg67388952024-01-25 12:14:50 +0100244 if ((ARRAY)[index__] == VALUE) { \
stewegf9041a22024-01-18 13:29:12 +0100245 if (index__ != LY_ARRAY_COUNT(ARRAY) - 1) { \
steweg67388952024-01-25 12:14:50 +0100246 memmove(&((ARRAY)[index__]), &((ARRAY)[LY_ARRAY_COUNT(ARRAY) - 1]), sizeof *(ARRAY)); \
stewegf9041a22024-01-18 13:29:12 +0100247 } \
248 LY_ARRAY_DECREMENT(ARRAY); \
249 break; \
250 } \
251 } \
252 }
253
254/**
Radek Krejci859a15a2021-03-05 20:56:59 +0100255 * @brief Insert item into linked list.
256 *
257 * @param[in,out] LIST Linked list to add to.
258 * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
aPiecekb0445f22021-06-24 11:34:07 +0200259 * @param[in] LINKER name of structuring member that is used to connect items together.
Radek Krejci859a15a2021-03-05 20:56:59 +0100260 */
261#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
262 if (!(*LIST)) { \
Radek Krejci027d1f42021-04-13 20:28:04 +0200263 memcpy(LIST, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100264 } else { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200265 size_t offset__ = (char *)&(*LIST)->LINKER - (char *)(*LIST); \
266 char **iter__ = (char **)((size_t)(*LIST) + offset__); \
Radek Krejci027d1f42021-04-13 20:28:04 +0200267 while (*iter__) { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200268 iter__ = (char **)((size_t)(*iter__) + offset__); \
Radek Krejci027d1f42021-04-13 20:28:04 +0200269 } \
270 memcpy(iter__, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100271 }
272
273/**
274 * @brief Allocate and insert new item into linked list, return in case of error.
275 *
Radek Krejci6b273352021-04-13 20:33:42 +0200276 * This is a generic macro for ::LY_LIST_NEW_RET and ::LY_LIST_NEW_GOTO.
277 *
278 * @param[in] CTX used for logging.
279 * @param[in,out] LIST Linked list to add to.
280 * @param[out] NEW_ITEM New item that is appended to the list.
281 * @param[in] LINKER name of structure member that is used to connect items together.
282 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
283 */
284#define LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, EACTION) \
285 { \
Michal Vasko59e90fc2021-09-22 12:17:08 +0200286 char *p__ = (char *)calloc(1, sizeof *NEW_ITEM); \
Radek Krejci6b273352021-04-13 20:33:42 +0200287 if (!p__) { \
288 LOGMEM(CTX); \
289 EACTION; \
290 } \
291 memcpy(&(NEW_ITEM), &p__, sizeof p__); \
292 LY_LIST_INSERT(LIST, NEW_ITEM, LINKER); \
293 }
294
295/**
296 * @brief Allocate and insert new item into linked list, return in case of error.
297 *
Radek Krejci859a15a2021-03-05 20:56:59 +0100298 * @param[in] CTX used for logging.
299 * @param[in,out] LIST Linked list to add to.
300 * @param[out] NEW_ITEM New item that is appended to the list.
301 * @param[in] LINKER name of structure member that is used to connect items together.
302 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
303 */
304#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200305 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, return RETVAL)
Radek Krejci859a15a2021-03-05 20:56:59 +0100306
307/**
308 * @brief Allocate and insert new item into linked list, goto specified label in case of error.
309 *
310 * @param[in] CTX used for logging.
311 * @param[in,out] LIST Linked list to add to.
312 * @param[out] NEW_ITEM New item that is appended to the list.
313 * @param[in] LINKER name of structure member that is used to connect items together.
314 * @param[in] RET variable to store returned error type.
315 * @param[in] LABEL label to goto in case of error.
316 */
317#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200318 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, RET = LY_EMEM; goto LABEL)
319
320/** @} trees_edit */
Radek Krejci859a15a2021-03-05 20:56:59 +0100321
322#ifdef __cplusplus
323}
324#endif
325
326#endif /* LY_TREE_EDIT_H_ */