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