blob: 7284b4f5c87de8ff7ea69b02adcb5982b513a5a6 [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
Radek Krejci6b273352021-04-13 20:33:42 +020051/**
52 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
53 *
54 * Increases the size information.
55 *
56 * This is a generic macro for ::LY_ARRAY_NEW_RET and ::LY_ARRAY_NEW_GOTO.
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.
Radek Krejci6b273352021-04-13 20:33:42 +020061 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
62 */
63#define LY_ARRAY_NEW(CTX, ARRAY, EACTION) \
64 { \
65 void *p__; \
66 if (ARRAY) { \
67 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
68 p__ = realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
69 sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
70 if (!p__) { \
71 --(*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)); \
72 LOGMEM(CTX); \
73 EACTION; \
74 } \
75 } else { \
76 p__ = malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
77 if (!p__) { \
78 LOGMEM(CTX); \
79 EACTION; \
80 } \
81 *((LY_ARRAY_COUNT_TYPE*)(p__)) = 1; \
82 } \
83 p__ = (void*)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
84 memcpy(&(ARRAY), &p__, sizeof p__); \
85 }
Radek Krejci859a15a2021-03-05 20:56:59 +010086
87/**
88 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
89 *
90 * Increases the size information.
91 *
92 * @param[in] CTX libyang context for logging.
93 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
94 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
95 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
96 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
97 */
98#define LY_ARRAY_NEW_RET(CTX, ARRAY, NEW_ITEM, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +020099 LY_ARRAY_NEW(CTX, ARRAY, return RETVAL); \
100 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
101 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
Radek Krejci859a15a2021-03-05 20:56:59 +0100102
103/**
104 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
105 *
106 * Increases the size information.
107 *
108 * @param[in] CTX libyang context for logging.
109 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
110 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
111 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
112 * @param[out] RET Variable to store error code.
113 * @param[in] GOTO Label to go in case of error (memory allocation failure).
114 */
115#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
Radek Krejci6b273352021-04-13 20:33:42 +0200116 LY_ARRAY_NEW(CTX, ARRAY, RET = LY_EMEM; goto GOTO); \
117 (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
118 memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
119
120/**
121 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
122 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
123 *
124 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
125 * when the items are filled.
126 *
127 * This is a generic macro for ::LY_ARRAY_CREATE_RET and ::LY_ARRAY_CREATE_GOTO.
128 *
129 * @param[in] CTX libyang context for logging.
130 * @param[in,out] ARRAY Pointer to the array to create.
131 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
132 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
133 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
134 */
135#define LY_ARRAY_CREATE(CTX, ARRAY, SIZE, EACTION) \
136 { \
137 void *p__; \
138 if (ARRAY) { \
139 p__ = realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
140 sizeof(LY_ARRAY_COUNT_TYPE) + ((*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
141 if (!p__) { \
Radek Krejci859a15a2021-03-05 20:56:59 +0100142 LOGMEM(CTX); \
Radek Krejci6b273352021-04-13 20:33:42 +0200143 EACTION; \
144 } \
145 } else { \
146 p__ = calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
147 if (!p__) { \
148 LOGMEM(CTX); \
149 EACTION; \
Radek Krejci859a15a2021-03-05 20:56:59 +0100150 } \
151 } \
Radek Krejci6b273352021-04-13 20:33:42 +0200152 p__ = (void*)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
153 memcpy(&(ARRAY), &p__, sizeof p__); \
154 if (ARRAY) { \
155 memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
156 } \
157 }
Radek Krejci859a15a2021-03-05 20:56:59 +0100158
159/**
160 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
161 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
162 *
163 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
164 * when the items are filled.
165 *
166 * @param[in] CTX libyang context for logging.
167 * @param[in,out] ARRAY Pointer to the array to create.
168 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
169 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
170 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
171 */
172#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200173 LY_ARRAY_CREATE(CTX, ARRAY, SIZE, return RETVAL)
Radek Krejci859a15a2021-03-05 20:56:59 +0100174
175/**
176 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
177 * If the ARRAY already exists, it is resized (space for SIZE items is added).
178 *
179 * Does not set the count information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
180 * when the items are filled.
181 *
182 * @param[in] CTX libyang context for logging.
183 * @param[in,out] ARRAY Pointer to the array to create.
184 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
185 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
186 * @param[out] RET Variable to store error code.
187 * @param[in] GOTO Label to go in case of error (memory allocation failure).
188 */
189#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
Radek Krejci6b273352021-04-13 20:33:42 +0200190 LY_ARRAY_CREATE(CTX, ARRAY, SIZE, RET = LY_EMEM; goto GOTO)
Radek Krejci859a15a2021-03-05 20:56:59 +0100191
192/**
193 * @brief Increment the items counter in a ([sized array](@ref sizedarrays)).
194 *
195 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
196 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
197 *
198 * @param[in] ARRAY Pointer to the array to affect.
199 */
200#define LY_ARRAY_INCREMENT(ARRAY) \
201 ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
202
203/**
204 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)).
205 *
206 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
207 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
208 *
209 * @param[in] ARRAY Pointer to the array to affect.
210 */
211#define LY_ARRAY_DECREMENT(ARRAY) \
212 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
213
214/**
215 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
216 * in case it was decremented to 0.
217 *
218 * @param[in] ARRAY Pointer to the array to affect.
219 */
220#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
221 --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
222 if (!LY_ARRAY_COUNT(ARRAY)) { \
223 LY_ARRAY_FREE(ARRAY); \
224 (ARRAY) = NULL; \
225 }
226
227/**
228 * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
229 *
230 * The items inside the array are not freed.
231 *
232 * @param[in] ARRAY A ([sized array](@ref sizedarrays)) to be freed.
233 */
234#define LY_ARRAY_FREE(ARRAY) \
235 if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}
236
237/**
238 * @brief Insert item into linked list.
239 *
240 * @param[in,out] LIST Linked list to add to.
241 * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
aPiecekb0445f22021-06-24 11:34:07 +0200242 * @param[in] LINKER name of structuring member that is used to connect items together.
Radek Krejci859a15a2021-03-05 20:56:59 +0100243 */
244#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
245 if (!(*LIST)) { \
Radek Krejci027d1f42021-04-13 20:28:04 +0200246 memcpy(LIST, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100247 } else { \
Radek Krejci027d1f42021-04-13 20:28:04 +0200248 size_t offset__ = (void*)&(*LIST)->LINKER - (void*)(*LIST); \
249 void **iter__ = (void **)((size_t)(*LIST) + offset__); \
250 while (*iter__) { \
251 iter__ = (void **)((size_t)(*iter__) + offset__); \
252 } \
253 memcpy(iter__, &(NEW_ITEM), sizeof NEW_ITEM); \
Radek Krejci859a15a2021-03-05 20:56:59 +0100254 }
255
256/**
257 * @brief Allocate and insert new item into linked list, return in case of error.
258 *
Radek Krejci6b273352021-04-13 20:33:42 +0200259 * This is a generic macro for ::LY_LIST_NEW_RET and ::LY_LIST_NEW_GOTO.
260 *
261 * @param[in] CTX used for logging.
262 * @param[in,out] LIST Linked list to add to.
263 * @param[out] NEW_ITEM New item that is appended to the list.
264 * @param[in] LINKER name of structure member that is used to connect items together.
265 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
266 */
267#define LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, EACTION) \
268 { \
269 void *p__ = calloc(1, sizeof *NEW_ITEM); \
270 if (!p__) { \
271 LOGMEM(CTX); \
272 EACTION; \
273 } \
274 memcpy(&(NEW_ITEM), &p__, sizeof p__); \
275 LY_LIST_INSERT(LIST, NEW_ITEM, LINKER); \
276 }
277
278/**
279 * @brief Allocate and insert new item into linked list, return in case of error.
280 *
Radek Krejci859a15a2021-03-05 20:56:59 +0100281 * @param[in] CTX used for logging.
282 * @param[in,out] LIST Linked list to add to.
283 * @param[out] NEW_ITEM New item that is appended to the list.
284 * @param[in] LINKER name of structure member that is used to connect items together.
285 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
286 */
287#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200288 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, return RETVAL)
Radek Krejci859a15a2021-03-05 20:56:59 +0100289
290/**
291 * @brief Allocate and insert new item into linked list, goto specified label in case of error.
292 *
293 * @param[in] CTX used for logging.
294 * @param[in,out] LIST Linked list to add to.
295 * @param[out] NEW_ITEM New item that is appended to the list.
296 * @param[in] LINKER name of structure member that is used to connect items together.
297 * @param[in] RET variable to store returned error type.
298 * @param[in] LABEL label to goto in case of error.
299 */
300#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
Radek Krejci6b273352021-04-13 20:33:42 +0200301 LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, RET = LY_EMEM; goto LABEL)
302
303/** @} trees_edit */
Radek Krejci859a15a2021-03-05 20:56:59 +0100304
305#ifdef __cplusplus
306}
307#endif
308
309#endif /* LY_TREE_EDIT_H_ */