Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
xml2.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file xml2.c
12 * @brief libxml2 handle registries and helper utilities for the Fun VM extension.
13 *
14 * Overview
15 * --------
16 * This translation unit implements compact, fixed-size registries that assign
17 * small positive integer handles to libxml2 objects. VM opcodes under
18 * src/vm/xml2/*.c and higher-level helpers can exchange these integer handles
19 * across the VM stack without exposing raw pointers.
20 *
21 * Build-time feature flag
22 * -----------------------
23 * Code in this file is compiled only when the CMake option FUN_WITH_XML2 is
24 * enabled (i.e., the preprocessor symbol FUN_WITH_XML2 is defined). When
25 * disabled, this file contributes no symbols and the corresponding VM opcodes
26 * should be compiled out or provide a graceful fallback.
27 *
28 * Ownership and lifetime
29 * ----------------------
30 * - Document registry (XmlDocSlot) TAKES OWNERSHIP of the registered xmlDocPtr.
31 * Releasing the document handle will call xmlFreeDoc() for the stored
32 * pointer.
33 * - Node registry (XmlNodeSlot) DOES NOT take ownership of xmlNodePtr values.
34 * Nodes are owned by their document. Releasing a node handle only clears the
35 * registry slot; it does not free the underlying xmlNode memory.
36 * - When a document is freed, the libxml2 tree below it is destroyed by
37 * libxml2, thereby invalidating any node pointers previously returned from
38 * that document. The small node handle registry in this file does not track
39 * which document owns which node. Callers must ensure they do not use node
40 * handles after their owning document has been released; they should clear
41 * those node handles explicitly if necessary.
42 *
43 * Limits and handle ranges
44 * ------------------------
45 * - Document handles are allocated from a fixed-size array of 64 slots. Valid
46 * handles are in the inclusive range [1, 63].
47 * - Node handles are allocated from a fixed-size array of 256 slots. Valid
48 * handles are in the inclusive range [1, 255].
49 * - The value 0 is reserved and indicates failure or an invalid handle.
50 *
51 * Error handling
52 * --------------
53 * - Allocation requests fail with a return value of 0 when no free slot is
54 * available.
55 * - Lookups return NULL for invalid or unused handles.
56 * - Free functions return 1 when a valid in-use handle was released, and 0
57 * otherwise. Document release also frees the underlying xmlDoc via
58 * xmlFreeDoc(). Node release does not free the xmlNode memory.
59 *
60 * Thread-safety
61 * -------------
62 * - The registries are simple, unsynchronized arrays. They are NOT
63 * thread-safe. If the VM uses libxml2 handles across multiple threads,
64 * external synchronization is required around all calls into this module.
65 *
66 * Example
67 * -------
68 * @code{.c}
69 * // Assume xmlInitParser() was called by the embedding application.
70 * xmlDocPtr d = xmlReadMemory("<root><x/></root>", 20, "-", NULL, 0);
71 * int dh = xml_doc_alloc(d); // takes ownership of d
72 * xmlDocPtr same = xml_doc_get(dh);
73 * // ... use 'same' with libxml2 APIs ...
74 *
75 * // When done, free the document handle; this calls xmlFreeDoc(same).
76 * (void)xml_doc_free_handle(dh);
77 * @endcode
78 */
79#ifdef FUN_WITH_XML2
80#include <libxml/parser.h>
81#include <libxml/tree.h>
82
83/**
84 * @brief Slot describing a registered XML document.
85 *
86 * The registry owns the xmlDocPtr and will free it when the handle is released
87 * via xml_doc_free_handle().
88 */
89typedef struct {
90 xmlDocPtr doc; /**< Pointer to a parsed libxml2 document (owned by registry). */
91 int in_use; /**< Non-zero if the slot is occupied. */
93/**
94 * @brief Slot describing a registered XML node.
95 *
96 * The registry does not own the node; it is managed by its document. Releasing
97 * a node handle does not free the node memory.
98 */
99typedef struct {
100 xmlNodePtr node; /**< Pointer to a node in some document (not owned). */
101 int in_use; /**< Non-zero if the slot is occupied. */
103
104/**
105 * @brief Fixed-size registry storage for documents.
106 *
107 * Index 0 is reserved. Valid document handle indices are in [1, 63].
108 */
110/**
111 * @brief Fixed-size registry storage for nodes.
112 *
113 * Index 0 is reserved. Valid node handle indices are in [1, 255].
114 */
116
117/**
118 * @brief Allocate a document handle for the given xmlDoc pointer.
119 *
120 * Allocates a free slot in the document registry and stores the supplied
121 * xmlDocPtr. Ownership is transferred to the registry; releasing the handle
122 * will call xmlFreeDoc() for the stored pointer.
123 *
124 * @param d Valid xmlDocPtr to register. The caller must not free the document
125 * directly after a successful registration.
126 * @return Positive handle in the range [1, 63] on success; 0 if no slot is
127 * available.
128 *
129 * @note Passing NULL is undefined behavior for this helper in the sense that
130 * it will simply not find a slot and return 0 if no slot is free; callers
131 * should validate inputs before calling.
132 */
133static int xml_doc_alloc(xmlDocPtr d) {
134 for (int i = 1; i < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])); ++i) {
135 if (!g_xml_docs[i].in_use) {
136 g_xml_docs[i].in_use = 1;
137 g_xml_docs[i].doc = d;
138 return i;
139 }
140 }
141 return 0;
142}
143/**
144 * @brief Retrieve a registered xmlDoc by handle.
145 *
146 * Performs bounds and state checks on the registry and returns the stored
147 * xmlDocPtr for the handle if present.
148 *
149 * @param h Handle previously returned by xml_doc_alloc().
150 * @return xmlDocPtr if the handle is valid and in use; NULL otherwise.
151 */
152static xmlDocPtr xml_doc_get(int h) {
153 if (h > 0 && h < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc;
154 return NULL;
155}
156/**
157 * @brief Free a document handle and the underlying xmlDoc.
158 *
159 * If the handle is valid and in use, calls xmlFreeDoc() for the stored
160 * document pointer, clears the slot, and returns 1.
161 *
162 * @param h Handle to release.
163 * @return 1 if the handle was valid and has been released; 0 otherwise.
164 *
165 * @note Freeing a document invalidates any xmlNodePtr previously obtained from
166 * that document. The node registry in this file does not automatically
167 * clear entries that reference nodes from the freed document; callers are
168 * responsible for avoiding use-after-free by releasing or ignoring such
169 * node handles.
170 */
171static int xml_doc_free_handle(int h) {
172 if (h <= 0 || h >= (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0;
173 if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc);
174 g_xml_docs[h].doc = NULL;
175 g_xml_docs[h].in_use = 0;
176 return 1;
177}
178
179/**
180 * @brief Allocate a node handle for the given xmlNode pointer.
181 *
182 * Allocates a free slot in the node registry and stores the supplied pointer.
183 * Ownership is not transferred; the memory is managed by the owning document.
184 *
185 * @param n Valid xmlNodePtr to register. Must remain valid for as long as the
186 * handle is in use and the owning document is alive.
187 * @return Positive handle in the range [1, 255] on success; 0 if no slot is
188 * available.
189 */
190static int xml_node_alloc(xmlNodePtr n) {
191 for (int i = 1; i < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])); ++i) {
192 if (!g_xml_nodes[i].in_use) {
193 g_xml_nodes[i].in_use = 1;
194 g_xml_nodes[i].node = n;
195 return i;
196 }
197 }
198 return 0;
199}
200/**
201 * @brief Retrieve a registered xmlNode by handle.
202 *
203 * Performs bounds and state checks on the registry and returns the stored
204 * xmlNodePtr for the handle if present.
205 *
206 * @param h Handle previously returned by xml_node_alloc().
207 * @return xmlNodePtr if the handle is valid and in use; NULL otherwise.
208 */
209static xmlNodePtr xml_node_get(int h) {
210 if (h > 0 && h < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node;
211 return NULL;
212}
213/**
214 * @brief Free a node handle without freeing the underlying node.
215 *
216 * Clears the registry slot associated with the given handle. The underlying
217 * xmlNode memory is not freed because nodes are owned by their document.
218 *
219 * @param h Handle to release.
220 * @return 1 if the handle was valid and has been released; 0 otherwise.
221 *
222 * @note Releasing or freeing a document invalidates any nodes originating from
223 * that document. Callers should avoid using node handles after their
224 * document has been released.
225 */
226static int xml_node_free_handle(int h) {
227 if (h <= 0 || h >= (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0;
228 /* nodes are owned by their document; do not free here */
229 g_xml_nodes[h].node = NULL;
230 g_xml_nodes[h].in_use = 0;
231 return 1;
232}
233#endif
Slot describing a registered XML document.
Definition xml2.c:89
xmlDocPtr doc
Definition xml2.c:90
int in_use
Definition xml2.c:91
Slot describing a registered XML node.
Definition xml2.c:99
int in_use
Definition xml2.c:101
xmlNodePtr node
Definition xml2.c:100
static int xml_node_alloc(xmlNodePtr n)
Allocate a node handle for the given xmlNode pointer.
Definition xml2.c:190
static int xml_doc_free_handle(int h)
Free a document handle and the underlying xmlDoc.
Definition xml2.c:171
static XmlDocSlot g_xml_docs[64]
Fixed-size registry storage for documents.
Definition xml2.c:109
static xmlNodePtr xml_node_get(int h)
Retrieve a registered xmlNode by handle.
Definition xml2.c:209
static int xml_doc_alloc(xmlDocPtr d)
Allocate a document handle for the given xmlDoc pointer.
Definition xml2.c:133
static int xml_node_free_handle(int h)
Free a node handle without freeing the underlying node.
Definition xml2.c:226
static xmlDocPtr xml_doc_get(int h)
Retrieve a registered xmlDoc by handle.
Definition xml2.c:152
static XmlNodeSlot g_xml_nodes[256]
Fixed-size registry storage for nodes.
Definition xml2.c:115