docs/Writerside/topics/properties.h.md

changeset 1232
781bd188f1c0
parent 1231
a9f9c59e0b63
child 1233
29e1c48d1a6c
equal deleted inserted replaced
1231:a9f9c59e0b63 1232:781bd188f1c0
1 # Properties 1 # Properties
2 2
3 The UCX properties parser can be used to parse line based key/value strings. 3 The UCX properties parser can be used to parse line based key/value strings.
4
5 <warning>
6 New Feature - documentation work in progress!
7 </warning>
8 4
9 ## Supported Syntax 5 ## Supported Syntax
10 6
11 Key/value pairs must be line based and separated by a single character delimter. 7 Key/value pairs must be line based and separated by a single character delimter.
12 The parser supports up to three different characters which introduce comments. 8 The parser supports up to three different characters which introduce comments.
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"}
101 > It is strongly recommended to always call `cxPropertiesDestroy` when you are done with the parser, 99 > It is strongly recommended to always call `cxPropertiesDestroy` when you are done with the parser,
102 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`. 100 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`.
103 101
104 ### List of Status Codes 102 ### List of Status Codes
105 103
106 Below is a full list of error codes for `cxPropertiesNext()`. 104 Below is a full list of status codes for `cxPropertiesNext()`.
107 105
108 | Status Code | Meaning | 106 | Status Code | Meaning |
109 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 107 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
110 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. | 108 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. |
111 | CX_PROPERTIES_NO_DATA | The input buffer does not contain more data. | 109 | CX_PROPERTIES_NO_DATA | The input buffer does not contain more data. |
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>

mercurial