| 77 Filling the input buffer is cost-free if there is no data already in the input buffer. |
73 Filling the input buffer is cost-free if there is no data already in the input buffer. |
| 78 In that case, the input buffer only stores the pointer to the original data without creating a copy. |
74 In that case, the input buffer only stores the pointer to the original data without creating a copy. |
| 79 Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed, |
75 Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed, |
| 80 and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments. |
76 and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments. |
| 81 |
77 |
| |
78 When all the data from the input buffer was successfully consumed, `cxPropertiesNext()` returns `CX_PROPERTIES_NO_DATA`. |
| |
79 |
| 82 > This is all still free of any copies and allocations. |
80 > This is all still free of any copies and allocations. |
| 83 > That means, the pointers in `key` and `value` after `cxPropertiesNext()` returns will point into the input buffer. |
81 > That means, the pointers in `key` and `value` after `cxPropertiesNext()` returns will point into the input buffer. |
| 84 > If you intend to store the key and/or the value somewhere else, it is strongly recommended to create a copy with `cx_strdup()`, |
82 > If you intend to store the key and/or the value somewhere else, it is strongly recommended to create a copy with `cx_strdup()`, |
| 85 > because you will otherwise soon end up with a dangling pointer. |
83 > because you will otherwise soon end up with a dangling pointer. |
| 86 > {style="note"} |
84 > {style="note"} |
| 139 CxPropertiesStatus |
137 CxPropertiesStatus |
| 140 cxPropertiesLoad(CxProperties *prop, |
138 cxPropertiesLoad(CxProperties *prop, |
| 141 CxPropertiesSink sink, CxPropertiesSource source); |
139 CxPropertiesSink sink, CxPropertiesSource source); |
| 142 ``` |
140 ``` |
| 143 |
141 |
| 144 <warning> |
142 The basic idea of `cxPropertiesLoad()` is that key/value-pairs are extracted from a _source_ and ingested by a _sink_. |
| 145 TODO: write documentation |
143 For the most common scenarios where properties data is read from a string or a file and put into a map, several functions are available. |
| 146 </warning> |
144 But you can specify your [own sources and sinks](#creating-own-sources-and-sinks), as well. |
| 147 |
145 |
| 148 ### Additional Status Codes |
146 The following example shows a simple function which loads all properties data from a file. |
| 149 |
147 The `chunk_size` argument when creating the file source specifies |
| 150 For sources and sinks there are three additional special status codes, |
148 how many bytes are read from the file and filled into the properties parser in one read/sink cycle. |
| 151 which only appear as return values for `cxPropertiesLoad()`. |
149 |
| 152 |
150 ```C |
| 153 | Status Code | Meaning | |
151 #include <stdio.h> |
| 154 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
152 #include <cx/properties.h> |
| 155 | CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. | |
153 |
| 156 | CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. | |
154 int load_props_from_file(const char *filename, CxMap *map) { |
| 157 | CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. | |
155 FILE *f = fopen(filename, "r"); |
| 158 |
156 if (!f) return -1; |
| |
157 CxProperties prop; |
| |
158 cxPropertiesInitDefault(&prop); |
| |
159 CxPropertiesSink sink = cxPropertiesMapSink(map); |
| |
160 CxPropertiesSource src = cxPropertiesFileSource(f, 512); |
| |
161 CxPropertiesStatus status = cxPropertiesLoad(&prop, sink, src); |
| |
162 fclose(f); |
| |
163 return status; |
| |
164 } |
| |
165 |
| |
166 // usage: |
| |
167 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); |
| |
168 if (load_props_from_file("my-props.properties", map)) { |
| |
169 // error handling |
| |
170 } else { |
| |
171 // assuming my-props.properties contains the following line: |
| |
172 // my-key = some value |
| |
173 char *value = cxMapGet(map, "my-key"); |
| |
174 } |
| |
175 ``` |
| |
176 |
| |
177 > The function `cxPropertiesLoad()` should usually not return `CX_PROPERTIES_INCOMPLETE_DATA` because the parser is automatically refilled from the source. |
| |
178 > If it does, it could mean that the source was unable to provide all the data, or the properties data ended unexpectedly. |
| |
179 > The most expected status code is `CX_PROPERTIES_NO_ERROR` which means that at least one key/value-pair was found. |
| |
180 > If `cxPropertiesLoad()` returns `CX_PROPERTIES_NO_DATA` it means that the source did not provide any key/value-pair. |
| |
181 > There are several special status codes which are documented [below](#additional-status-codes). |
| 159 |
182 |
| 160 ### Creating own Sources and Sinks |
183 ### Creating own Sources and Sinks |
| 161 |
184 |
| 162 ```C |
185 ```C |
| 163 #include <cx/properties.h> |
186 #include <cx/properties.h> |
| 188 void *data; |
211 void *data; |
| 189 cx_properties_sink_func sink_func; |
212 cx_properties_sink_func sink_func; |
| 190 } CxPropertiesSink; |
213 } CxPropertiesSink; |
| 191 ``` |
214 ``` |
| 192 |
215 |
| 193 <warning> |
216 You can create your own sources and sinks by initializing the respective structures. |
| 194 TODO: write documentation |
217 For a source, only the `read_func` is mandatory, the other two functions are optional and used for initialization and cleanup, if required. |
| 195 </warning> |
218 The file source created by `cxPropertiesFileSource()`, for example, |
| |
219 uses the `read_init_func` to allocate, and the `read_clean_func` to free the read buffer, respectively. |
| |
220 |
| |
221 Since the default map sink created by `cxPropertiesMapSink()` stores `char*` pointers into a map, |
| |
222 the following example uses a different sink, which stores them as `cxmutstr` values, automatically freeing them |
| |
223 when the map gets destroyed. |
| |
224 |
| |
225 ```C |
| |
226 #include <stdio.h> |
| |
227 #include <unistd.h> |
| |
228 #include <fcntl.h> |
| |
229 #include <sys/stat.h> |
| |
230 #include <sys/mman.h> |
| |
231 #include <cx/properties.h> |
| |
232 #include <cx/hash_map.h> |
| |
233 |
| |
234 static int prop_mmap(CxProperties *prop, CxPropertiesSource *src) { |
| |
235 struct stat s; |
| |
236 int fd = open(src->src, O_RDONLY); |
| |
237 if (fd < 0) return -1; |
| |
238 // re-use the data field to store the fd |
| |
239 // there are cleaner ways, but this is just for illustration |
| |
240 src->src = (void*) fd; |
| |
241 fstat(fd, &s); |
| |
242 // memory map the entire file |
| |
243 // and store the address and length in the properties source |
| |
244 src->data_ptr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
| |
245 src->data_size = s.st_size; |
| |
246 return src->data_ptr == NULL; |
| |
247 } |
| |
248 |
| |
249 static int prop_read(CxProperties *prop, CxPropertiesSource *src, |
| |
250 cxstring *target) { |
| |
251 // copy the address and length of the mapped data to the target |
| |
252 target->ptr = src->data_ptr; |
| |
253 target->length = src->data_size; |
| |
254 // set the new size to zero to indicate that there is no more data |
| |
255 src->data_size = 0; |
| |
256 return 0; |
| |
257 } |
| |
258 |
| |
259 static void prop_unmap(CxProperties *prop, CxPropertiesSource *src) { |
| |
260 // unmap the memory and close the file |
| |
261 munmap(src->data_ptr, src->data_size); |
| |
262 close((int)src->src); |
| |
263 } |
| |
264 |
| |
265 static int prop_sink(CxProperties *prop, CxPropertiesSink *sink, |
| |
266 cxstring key, cxstring value) { |
| |
267 CxMap *map = sink->sink; |
| |
268 // copy the string and store it into the map |
| |
269 cxmutstr v = cx_strdup(value); |
| |
270 int r = cxMapPut(map, key, &v); |
| |
271 if (r != 0) cx_strfree(&v); |
| |
272 return r; |
| |
273 } |
| |
274 |
| |
275 int load_props_from_file(const char *filename, CxMap *map) { |
| |
276 CxProperties prop; |
| |
277 cxPropertiesInitDefault(&prop); |
| |
278 CxPropertiesSource src; |
| |
279 src.src = (void*) filename; |
| |
280 src.read_init_func = prop_mmap; |
| |
281 src.read_func = prop_read; |
| |
282 src.read_clean_func = prop_unmap; |
| |
283 CxPropertiesSink sink; |
| |
284 sink.sink = map; |
| |
285 sink.sink_func = prop_sink; |
| |
286 return cxPropertiesLoad(&prop, sink, src); |
| |
287 } |
| |
288 |
| |
289 int main() { |
| |
290 // in contrast to the default map sink, |
| |
291 // this one here stores the UCX strings by value |
| |
292 CxMap *map = cxHashMapCreateSimple(sizeof(cxmutstr)); |
| |
293 |
| |
294 // automatically free the UCX string when removed from the map |
| |
295 cxDefineDestructor(map, cx_strfree); |
| |
296 |
| |
297 // use our custom load function to load the data from the file |
| |
298 if (load_props_from_file("my-props.properties", map)) { |
| |
299 fputs("Error reading properties.\n", stderr); |
| |
300 return 1; |
| |
301 } |
| |
302 |
| |
303 // output the read key/value pairs for illustration |
| |
304 CxMapIterator iter = cxMapIterator(map); |
| |
305 cx_foreach(CxMapEntry *, entry, iter) { |
| |
306 cxstring k = cx_strn(entry->key->data, entry->key->len); |
| |
307 cxmutstr *v = entry->value; |
| |
308 printf("%.*s = %.*s\n", |
| |
309 (int) k.length, k.ptr, (int) v->length, v->ptr); |
| |
310 } |
| |
311 |
| |
312 // freeing the map also frees the strings |
| |
313 // because we have registered cx_strfree() as destructor function |
| |
314 cxMapFree(map); |
| |
315 |
| |
316 return 0; |
| |
317 } |
| |
318 ``` |
| |
319 |
| |
320 > A cleaner implementation that does not produce a warning for bluntly casting an `int` to a `void*` |
| |
321 > can be achieved by declaring a struct that contains the information, allocate memory for |
| |
322 > that struct, and store the pointer in `data_ptr`. |
| |
323 > For illustrating how properties sources and sinks can be implemented, this was not necessary. |
| |
324 |
| |
325 ### Additional Status Codes |
| |
326 |
| |
327 For sources and sinks there are three additional special status codes, |
| |
328 which only appear as return values for `cxPropertiesLoad()`. |
| |
329 |
| |
330 | Status Code | Meaning | |
| |
331 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
| |
332 | CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. | |
| |
333 | CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. | |
| |
334 | CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. | |
| |
335 |
| 196 |
336 |
| 197 <seealso> |
337 <seealso> |
| 198 <category ref="apidoc"> |
338 <category ref="apidoc"> |
| 199 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> |
339 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> |
| 200 </category> |
340 </category> |