summaryrefslogtreecommitdiffstats
path: root/src/tree_edit.h
blob: 951d95d41c89a1c3761a0e40a861a253058feb90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/**
 * @file tree_edit.h
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @brief libyang generic macros and functions to modify YANG schema or data trees. Intended for internal use and libyang
 * plugins.
 *
 * Copyright (c) 2019-2021 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#ifndef LY_TREE_EDIT_H_
#define LY_TREE_EDIT_H_

#include <stdlib.h>

#ifndef LOGMEM
#define LOGMEM(CTX)
#endif

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Wrapper for realloc() call. The only difference is that if it fails to
 * allocate the requested memory, the original memory is freed as well.
 *
 * @param[in] ptr Memory to reallocate.
 * @param[in] size New size of the memory block.
 *
 * @return Pointer to the new memory, NULL on error.
 */
void *ly_realloc(void *ptr, size_t size);

/**
 * @defgroup trees_edit Trees - modification
 * @ingroup trees
 *
 * Generic macros, functions, etc. to modify [schema](@ref schematree) and [data](@ref datatree) trees.
 * @{
 */

/**
 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
 *
 * Increases the size information.
 *
 * This is a generic macro for ::LY_ARRAY_NEW_RET and ::LY_ARRAY_NEW_GOTO.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
 */
#define LY_ARRAY_NEW(CTX, ARRAY, EACTION) \
    { \
        char *p__; \
        if (ARRAY) { \
            ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
            p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
                    sizeof(LY_ARRAY_COUNT_TYPE) + (*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) * sizeof *(ARRAY))); \
            if (!p__) { \
                --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
                LOGMEM(CTX); \
                EACTION; \
            } \
        } else { \
            p__ = (char *)malloc(sizeof(LY_ARRAY_COUNT_TYPE) + sizeof *(ARRAY)); \
            if (!p__) { \
                LOGMEM(CTX); \
                EACTION; \
            } \
            *((LY_ARRAY_COUNT_TYPE*)(p__)) = 1; \
        } \
        p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
        memcpy(&(ARRAY), &p__, sizeof p__); \
    }

/**
 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
 *
 * Increases the size information.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
 */
#define LY_ARRAY_NEW_RET(CTX, ARRAY, NEW_ITEM, RETVAL) \
    LY_ARRAY_NEW(CTX, ARRAY, return RETVAL); \
    (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
    memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))

/**
 * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
 *
 * Increases the size information.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
 * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
 * @param[out] RET Variable to store error code.
 * @param[in] GOTO Label to go in case of error (memory allocation failure).
 */
#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
    LY_ARRAY_NEW(CTX, ARRAY, RET = LY_EMEM; goto GOTO); \
    (NEW_ITEM) = &(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) - 1]; \
    memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))

/**
 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
 *
 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
 * when the items are filled.
 *
 * This is a generic macro for ::LY_ARRAY_CREATE_RET and ::LY_ARRAY_CREATE_GOTO.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to create.
 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
 */
#define LY_ARRAY_CREATE(CTX, ARRAY, SIZE, EACTION) \
    { \
        char *p__; \
        if (ARRAY) { \
            p__ = (char *)realloc(((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1), \
                    sizeof(LY_ARRAY_COUNT_TYPE) + ((*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1) + (SIZE)) * sizeof *(ARRAY))); \
            if (!p__) { \
                LOGMEM(CTX); \
                EACTION; \
            } \
        } else { \
            p__ = (char *)calloc(1, sizeof(LY_ARRAY_COUNT_TYPE) + (SIZE) * sizeof *(ARRAY)); \
            if (!p__) { \
                LOGMEM(CTX); \
                EACTION; \
            } \
        } \
        p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \
        memcpy(&(ARRAY), &p__, sizeof p__); \
        if (ARRAY) { \
            memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \
        } \
    }

/**
 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
 * If the ARRAY already exists, it is resized (space for SIZE items is added and zeroed).
 *
 * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
 * when the items are filled.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to create.
 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
 */
#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
    LY_ARRAY_CREATE(CTX, ARRAY, SIZE, return RETVAL)

/**
 * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
 * If the ARRAY already exists, it is resized (space for SIZE items is added).
 *
 * Does not set the count information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
 * when the items are filled.
 *
 * @param[in] CTX libyang context for logging.
 * @param[in,out] ARRAY Pointer to the array to create.
 * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
 * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
 * @param[out] RET Variable to store error code.
 * @param[in] GOTO Label to go in case of error (memory allocation failure).
 */
#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
    LY_ARRAY_CREATE(CTX, ARRAY, SIZE, RET = LY_EMEM; goto GOTO)

/**
 * @brief Increment the items counter in a ([sized array](@ref sizedarrays)).
 *
 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
 *
 * @param[in] ARRAY Pointer to the array to affect.
 */
#define LY_ARRAY_INCREMENT(ARRAY) \
        ++(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))

/**
 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)).
 *
 * Does not change the allocated memory used by the ARRAY. To do so, use LY_ARRAY_CREATE_RET,
 * LY_ARRAY_CREATE_GOTO or LY_ARRAY_RESIZE_ERR_RET.
 *
 * @param[in] ARRAY Pointer to the array to affect.
 */
#define LY_ARRAY_DECREMENT(ARRAY) \
        --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))

/**
 * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
 * in case it was decremented to 0.
 *
 * @param[in] ARRAY Pointer to the array to affect.
 */
#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
        --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
        if (!LY_ARRAY_COUNT(ARRAY)) { \
            LY_ARRAY_FREE(ARRAY); \
            (ARRAY) = NULL; \
        }

/**
 * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
 *
 * The items inside the array are not freed.
 *
 * @param[in] ARRAY A ([sized array](@ref sizedarrays)) to be freed.
 */
#define LY_ARRAY_FREE(ARRAY) \
        if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}

/**
 * @brief Insert item into linked list.
 *
 * @param[in,out] LIST Linked list to add to.
 * @param[in] NEW_ITEM New item, that will be appended to the list, must be already allocated.
 * @param[in] LINKER name of structuring member that is used to connect items together.
 */
#define LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)\
    if (!(*LIST)) { \
        memcpy(LIST, &(NEW_ITEM), sizeof NEW_ITEM); \
    } else { \
        size_t offset__ = (char *)&(*LIST)->LINKER - (char *)(*LIST); \
        char **iter__ = (char **)((size_t)(*LIST) + offset__); \
        while (*iter__) { \
            iter__ = (char **)((size_t)(*iter__) + offset__); \
        } \
        memcpy(iter__, &(NEW_ITEM), sizeof NEW_ITEM); \
    }

/**
 * @brief Allocate and insert new item into linked list, return in case of error.
 *
 * This is a generic macro for ::LY_LIST_NEW_RET and ::LY_LIST_NEW_GOTO.
 *
 * @param[in] CTX used for logging.
 * @param[in,out] LIST Linked list to add to.
 * @param[out] NEW_ITEM New item that is appended to the list.
 * @param[in] LINKER name of structure member that is used to connect items together.
 * @param[in] EACTION Action to perform in case of error (memory allocation failure).
 */
#define LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, EACTION) \
    { \
        char *p__ = (char *)calloc(1, sizeof *NEW_ITEM); \
        if (!p__) { \
            LOGMEM(CTX); \
            EACTION; \
        } \
        memcpy(&(NEW_ITEM), &p__, sizeof p__); \
        LY_LIST_INSERT(LIST, NEW_ITEM, LINKER); \
    }

/**
 * @brief Allocate and insert new item into linked list, return in case of error.
 *
 * @param[in] CTX used for logging.
 * @param[in,out] LIST Linked list to add to.
 * @param[out] NEW_ITEM New item that is appended to the list.
 * @param[in] LINKER name of structure member that is used to connect items together.
 * @param[in] RETVAL Return value for the case of error (memory allocation failure).
 */
#define LY_LIST_NEW_RET(CTX, LIST, NEW_ITEM, LINKER, RETVAL) \
    LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, return RETVAL)

/**
 * @brief Allocate and insert new item into linked list, goto specified label in case of error.
 *
 * @param[in] CTX used for logging.
 * @param[in,out] LIST Linked list to add to.
 * @param[out] NEW_ITEM New item that is appended to the list.
 * @param[in] LINKER name of structure member that is used to connect items together.
 * @param[in] RET variable to store returned error type.
 * @param[in] LABEL label to goto in case of error.
 */
#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, RET, LABEL) \
    LY_LIST_NEW(CTX, LIST, NEW_ITEM, LINKER, RET = LY_EMEM; goto LABEL)

/** @} trees_edit */

#ifdef __cplusplus
}
#endif

#endif /* LY_TREE_EDIT_H_ */