blob: 6233eafa3e2d36f380adcde13108adadd8d7ce78 [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
21#include "log.h"
22#include "tree.h"
23
24#ifndef LOGMEM
25#define LOGMEM(CTX)
26#endif
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/**
33 * @brief Wrapper for realloc() call. The only difference is that if it fails to
34 * allocate the requested memory, the original memory is freed as well.
35 *
36 * @param[in] ptr Memory to reallocate.
37 * @param[in] size New size of the memory block.
38 *
39 * @return Pointer to the new memory, NULL on error.
40 */
41void *ly_realloc(void *ptr, size_t size);
42
43/**
44 * @defgroup trees_edit Trees - modification
45 * @ingroup trees
46 *
47 * Generic macros, functions, etc. to modify [schema](@ref schematree) and [data](@ref datatree) trees.
48 * @{
49 */
50
51/** @} trees_edit */
52
53/**
54 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
55 *
56 * Increases the size information.
57 *
58 * @param[in] CTX libyang context for logging.
59 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
60 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
61 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
62 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
63 */
64#define LY_ARRAY_NEW_RET(CTX, ARRAY, NEW_ITEM, RETVAL) \
65 if (!(ARRAY)) { \
66 ARRAY = malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
67 *((LY_ARRAY_COUNT_TYPE*)(ARRAY)) = 1; \
68 } else { \
69 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
70 ARRAY = ly_realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
71 if (!(ARRAY)) { \
72 LOGMEM(CTX); \
73 return RETVAL; \
74 } \
75 } \
76 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
77 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
78 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
79
80/**
81 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
82 *
83 * Increases the size information.
84 *
85 * @param[in] CTX libyang context for logging.
86 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
87 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
88 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
89 * @param[out] RET Variable to store error code.
90 * @param[in] GOTO Label to go in case of error (memory allocation failure).
91 */
92#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
93 if (!(ARRAY)) { \
94 ARRAY = malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
95 *((LY_ARRAY_COUNT_TYPE*)(ARRAY)) = 1; \
96 } else { \
97 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
98 ARRAY = ly_realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
99 if (!(ARRAY)) { \
100 RET = LY_EMEM; \
101 LOGMEM(CTX); \
102 goto GOTO; \
103 } \
104 } \
105 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
106 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
107 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
108
109/**
110 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
111 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
112 *
113 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
114 * when the items are filled.
115 *
116 * @param[in] CTX libyang context for logging.
117 * @param[in,out] ARRAY Pointer to the array to create.
118 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
119 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
120 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
121 */
122#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
123 if (ARRAY) { \
124 ARRAY = ly_realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), sizeof(LY_ARRAY_COUNT_TYPE) + ((*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
125 if (!(ARRAY)) { \
126 LOGMEM(CTX); \
127 return RETVAL; \
128 } \
129 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
130 memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
131 } else { \
132 ARRAY = calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
133 if (!(ARRAY)) { \
134 LOGMEM(CTX); \
135 return RETVAL; \
136 } \
137 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
138 }
139
140/**
141 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
142 * If the ARRAY already exists, it is resized (space for SIZE items is added).
143 *
144 * Does not set the count information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
145 * when the items are filled.
146 *
147 * @param[in] CTX libyang context for logging.
148 * @param[in,out] ARRAY Pointer to the array to create.
149 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
150 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
151 * @param[out] RET Variable to store error code.
152 * @param[in] GOTO Label to go in case of error (memory allocation failure).
153 */
154#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
155 if (ARRAY) { \
156 ARRAY = ly_realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), sizeof(LY_ARRAY_COUNT_TYPE) + ((LY_ARRAY_COUNT(ARRAY) + (SIZE)) * sizeof *(ARRAY))); \
157 if (!(ARRAY)) { \
158 RET = LY_EMEM; \
159 LOGMEM(CTX); \
160 goto GOTO; \
161 } \
162 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
163 memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
164 } else { \
165 ARRAY = calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
166 if (!(ARRAY)) { \
167 RET = LY_EMEM; \
168 LOGMEM(CTX); \
169 goto GOTO; \
170 } \
171 ARRAY = (void*)((LY_ARRAY_COUNT_TYPE*)(ARRAY) + 1); \
172 }
173
174/**
175 * @brief Increment the items counter in a ([sized array](@ref sizedarrays)).
176 *
177 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
178 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
179 *
180 * @param[in] ARRAY Pointer to the array to affect.
181 */
182#define LY_ARRAY_INCREMENT(ARRAY) \
183 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
184
185/**
186 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)).
187 *
188 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
189 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
190 *
191 * @param[in] ARRAY Pointer to the array to affect.
192 */
193#define LY_ARRAY_DECREMENT(ARRAY) \
194 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
195
196/**
197 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
198 * in case it was decremented to 0.
199 *
200 * @param[in] ARRAY Pointer to the array to affect.
201 */
202#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
203 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
204 if (!LY_ARRAY_COUNT(ARRAY)) { \
205 LY_ARRAY_FREE(ARRAY); \
206 (ARRAY) = NULL; \
207 }
208
209/**
210 * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
211 *
212 * The items inside the array are not freed.
213 *
214 * @param[in] ARRAY A ([sized array](@ref sizedarrays)) to be freed.
215 */
216#define LY_ARRAY_FREE(ARRAY) \
217 if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}
218
219/**
220 * @brief Insert item into linked list.
221 *
222 * @param[in,out] LIST Linked list to add to.
223 * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
224 * @param[in] LINKER name of structuin member that is used to connect items together.
225 */
226#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
227 if (!(*LIST)) { \
228 *LIST = (__typeof__(*(LIST)))NEW_ITEM; \
229 } else { \
230 do { \
231 __typeof__(*(LIST)) iterator; \
232 for (iterator = *(LIST); iterator->LINKER; iterator = iterator->LINKER) {} \
233 iterator->LINKER = (__typeof__(*(LIST)))NEW_ITEM; \
234 } while (0); \
235 }
236
237/**
238 * @brief Allocate and insert new item into linked list, return in case of error.
239 *
240 * @param[in] CTX used for logging.
241 * @param[in,out] LIST Linked list to add to.
242 * @param[out] NEW_ITEM New item that is appended to the list.
243 * @param[in] LINKER name of structure member that is used to connect items together.
244 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
245 */
246#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
247 NEW_ITEM = calloc(1, sizeof *NEW_ITEM); \
248 if (!(NEW_ITEM)) { \
249 LOGMEM(CTX); \
250 return RETVAL; \
251 } \
252 LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)
253
254/**
255 * @brief Allocate and insert new item into linked list, goto specified label in case of error.
256 *
257 * @param[in] CTX used for logging.
258 * @param[in,out] LIST Linked list to add to.
259 * @param[out] NEW_ITEM New item that is appended to the list.
260 * @param[in] LINKER name of structure member that is used to connect items together.
261 * @param[in] RET variable to store returned error type.
262 * @param[in] LABEL label to goto in case of error.
263 */
264#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
265 NEW_ITEM = calloc(1, sizeof *NEW_ITEM); \
266 if (!(NEW_ITEM)) { \
267 RET = LY_EMEM; \
268 LOGMEM(CTX); \
269 goto LABEL; \
270 } \
271 LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)
272
273#ifdef __cplusplus
274}
275#endif
276
277#endif /* LY_TREE_EDIT_H_ */