blob: 358711b965f96ffdec7b2609c4435128d9adfff5 [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 { \
62 void *p__; \
63 if (ARRAY) { \
64 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
65 p__ = realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
66 sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
67 if (!p__) { \
68 --(*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)); \
69 LOGMEM(CTX); \
70 EACTION; \
71 } \
72 } else { \
73 p__ = malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
74 if (!p__) { \
75 LOGMEM(CTX); \
76 EACTION; \
77 } \
78 *((LY_ARRAY_COUNT_TYPE*)(p__)) = 1; \
79 } \
80 p__ = (void*)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
81 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 { \
134 void *p__; \
135 if (ARRAY) { \
136 p__ = realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
137 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 { \
143 p__ = calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
144 if (!p__) { \
145 LOGMEM(CTX); \
146 EACTION; \
Radek Krejci859a15a2021-03-05 20:56:59 +0100147 } \
148 } \
Radek Krejci6b273352021-04-13 20:33:42 +0200149 p__ = (void*)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
150 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/**
235 * @brief Insert item into linked list.
236 *
237 * @param[in,out] LIST Linked list to add to.
238 * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
aPiecekb0445f22021-06-24 11:34:07 +0200239 * @param[in] LINKER name of structuring member that is used to connect items together.
Radek Krejci859a15a2021-03-05 20:56:59 +0100240 */
241#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
242 if (!(*LIST)) { \
Radek Krejci027d1f42021-04-13 20:28:04 +0200243 memcpy(LIST, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100244 } else { \
Radek Krejci027d1f42021-04-13 20:28:04 +0200245 size_t offset__ = (void*)&(*LIST)->LINKER - (void*)(*LIST); \
246 void **iter__ = (void **)((size_t)(*LIST) + offset__); \
247 while (*iter__) { \
248 iter__ = (void **)((size_t)(*iter__) + offset__); \
249 } \
250 memcpy(iter__, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100251 }
252
253/**
254 * @brief Allocate and insert new item into linked list, return in case of error.
255 *
Radek Krejci6b273352021-04-13 20:33:42 +0200256 * This is a generic macro for ::LY_LIST_NEW_RET and ::LY_LIST_NEW_GOTO.
257 *
258 * @param[in] CTX used for logging.
259 * @param[in,out] LIST Linked list to add to.
260 * @param[out] NEW_ITEM New item that is appended to the list.
261 * @param[in] LINKER name of structure member that is used to connect items together.
262 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
263 */
264#define LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, EACTION) \
265 { \
266 void *p__ = calloc(1, sizeof *NEW_ITEM); \
267 if (!p__) { \
268 LOGMEM(CTX); \
269 EACTION; \
270 } \
271 memcpy(&(NEW_ITEM), &p__, sizeof p__); \
272 LY_LIST_INSERT(LIST, NEW_ITEM, LINKER); \
273 }
274
275/**
276 * @brief Allocate and insert new item into linked list, return in case of error.
277 *
Radek Krejci859a15a2021-03-05 20:56:59 +0100278 * @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] RETVAL Return value for the case of error (memory allocation failure).
283 */
284#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200285 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, return RETVAL)
Radek Krejci859a15a2021-03-05 20:56:59 +0100286
287/**
288 * @brief Allocate and insert new item into linked list, goto specified label in case of error.
289 *
290 * @param[in] CTX used for logging.
291 * @param[in,out] LIST Linked list to add to.
292 * @param[out] NEW_ITEM New item that is appended to the list.
293 * @param[in] LINKER name of structure member that is used to connect items together.
294 * @param[in] RET variable to store returned error type.
295 * @param[in] LABEL label to goto in case of error.
296 */
297#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200298 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, RET = LY_EMEM; goto LABEL)
299
300/** @} trees_edit */
Radek Krejci859a15a2021-03-05 20:56:59 +0100301
302#ifdef __cplusplus
303}
304#endif
305
306#endif /* LY_TREE_EDIT_H_ */