diff -r a9f9c59e0b63 -r 781bd188f1c0 docs/Writerside/topics/properties.h.md
--- a/docs/Writerside/topics/properties.h.md Fri Feb 28 19:07:47 2025 +0100
+++ b/docs/Writerside/topics/properties.h.md Sat Mar 01 15:02:57 2025 +0100
@@ -2,10 +2,6 @@
The UCX properties parser can be used to parse line based key/value strings.
-
-New Feature - documentation work in progress!
-
-
## Supported Syntax
Key/value pairs must be line based and separated by a single character delimter.
@@ -79,6 +75,8 @@
Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed,
and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments.
+When all the data from the input buffer was successfully consumed, `cxPropertiesNext()` returns `CX_PROPERTIES_NO_DATA`.
+
> This is all still free of any copies and allocations.
> That means, the pointers in `key` and `value` after `cxPropertiesNext()` returns will point into the input buffer.
> If you intend to store the key and/or the value somewhere else, it is strongly recommended to create a copy with `cx_strdup()`,
@@ -103,7 +101,7 @@
### List of Status Codes
-Below is a full list of error codes for `cxPropertiesNext()`.
+Below is a full list of status codes for `cxPropertiesNext()`.
| Status Code | Meaning |
|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -141,21 +139,46 @@
CxPropertiesSink sink, CxPropertiesSource source);
```
-
-TODO: write documentation
-
+The basic idea of `cxPropertiesLoad()` is that key/value-pairs are extracted from a _source_ and ingested by a _sink_.
+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.
+But you can specify your [own sources and sinks](#creating-own-sources-and-sinks), as well.
-### Additional Status Codes
+The following example shows a simple function which loads all properties data from a file.
+The `chunk_size` argument when creating the file source specifies
+how many bytes are read from the file and filled into the properties parser in one read/sink cycle.
+
+```C
+#include
+#include
-For sources and sinks there are three additional special status codes,
-which only appear as return values for `cxPropertiesLoad()`.
+int load_props_from_file(const char *filename, CxMap *map) {
+ FILE *f = fopen(filename, "r");
+ if (!f) return -1;
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+ CxPropertiesSink sink = cxPropertiesMapSink(map);
+ CxPropertiesSource src = cxPropertiesFileSource(f, 512);
+ CxPropertiesStatus status = cxPropertiesLoad(&prop, sink, src);
+ fclose(f);
+ return status;
+}
-| Status Code | Meaning |
-|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. |
-| CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. |
-| CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. |
+// usage:
+CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS);
+if (load_props_from_file("my-props.properties", map)) {
+ // error handling
+} else {
+ // assuming my-props.properties contains the following line:
+ // my-key = some value
+ char *value = cxMapGet(map, "my-key");
+}
+```
+> The function `cxPropertiesLoad()` should usually not return `CX_PROPERTIES_INCOMPLETE_DATA` because the parser is automatically refilled from the source.
+> If it does, it could mean that the source was unable to provide all the data, or the properties data ended unexpectedly.
+> The most expected status code is `CX_PROPERTIES_NO_ERROR` which means that at least one key/value-pair was found.
+> If `cxPropertiesLoad()` returns `CX_PROPERTIES_NO_DATA` it means that the source did not provide any key/value-pair.
+> There are several special status codes which are documented [below](#additional-status-codes).
### Creating own Sources and Sinks
@@ -190,9 +213,126 @@
} CxPropertiesSink;
```
-
-TODO: write documentation
-
+You can create your own sources and sinks by initializing the respective structures.
+For a source, only the `read_func` is mandatory, the other two functions are optional and used for initialization and cleanup, if required.
+The file source created by `cxPropertiesFileSource()`, for example,
+uses the `read_init_func` to allocate, and the `read_clean_func` to free the read buffer, respectively.
+
+Since the default map sink created by `cxPropertiesMapSink()` stores `char*` pointers into a map,
+the following example uses a different sink, which stores them as `cxmutstr` values, automatically freeing them
+when the map gets destroyed.
+
+```C
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static int prop_mmap(CxProperties *prop, CxPropertiesSource *src) {
+ struct stat s;
+ int fd = open(src->src, O_RDONLY);
+ if (fd < 0) return -1;
+ // re-use the data field to store the fd
+ // there are cleaner ways, but this is just for illustration
+ src->src = (void*) fd;
+ fstat(fd, &s);
+ // memory map the entire file
+ // and store the address and length in the properties source
+ src->data_ptr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ src->data_size = s.st_size;
+ return src->data_ptr == NULL;
+}
+
+static int prop_read(CxProperties *prop, CxPropertiesSource *src,
+ cxstring *target) {
+ // copy the address and length of the mapped data to the target
+ target->ptr = src->data_ptr;
+ target->length = src->data_size;
+ // set the new size to zero to indicate that there is no more data
+ src->data_size = 0;
+ return 0;
+}
+
+static void prop_unmap(CxProperties *prop, CxPropertiesSource *src) {
+ // unmap the memory and close the file
+ munmap(src->data_ptr, src->data_size);
+ close((int)src->src);
+}
+
+static int prop_sink(CxProperties *prop, CxPropertiesSink *sink,
+ cxstring key, cxstring value) {
+ CxMap *map = sink->sink;
+ // copy the string and store it into the map
+ cxmutstr v = cx_strdup(value);
+ int r = cxMapPut(map, key, &v);
+ if (r != 0) cx_strfree(&v);
+ return r;
+}
+
+int load_props_from_file(const char *filename, CxMap *map) {
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+ CxPropertiesSource src;
+ src.src = (void*) filename;
+ src.read_init_func = prop_mmap;
+ src.read_func = prop_read;
+ src.read_clean_func = prop_unmap;
+ CxPropertiesSink sink;
+ sink.sink = map;
+ sink.sink_func = prop_sink;
+ return cxPropertiesLoad(&prop, sink, src);
+}
+
+int main() {
+ // in contrast to the default map sink,
+ // this one here stores the UCX strings by value
+ CxMap *map = cxHashMapCreateSimple(sizeof(cxmutstr));
+
+ // automatically free the UCX string when removed from the map
+ cxDefineDestructor(map, cx_strfree);
+
+ // use our custom load function to load the data from the file
+ if (load_props_from_file("my-props.properties", map)) {
+ fputs("Error reading properties.\n", stderr);
+ return 1;
+ }
+
+ // output the read key/value pairs for illustration
+ CxMapIterator iter = cxMapIterator(map);
+ cx_foreach(CxMapEntry *, entry, iter) {
+ cxstring k = cx_strn(entry->key->data, entry->key->len);
+ cxmutstr *v = entry->value;
+ printf("%.*s = %.*s\n",
+ (int) k.length, k.ptr, (int) v->length, v->ptr);
+ }
+
+ // freeing the map also frees the strings
+ // because we have registered cx_strfree() as destructor function
+ cxMapFree(map);
+
+ return 0;
+}
+```
+
+> A cleaner implementation that does not produce a warning for bluntly casting an `int` to a `void*`
+> can be achieved by declaring a struct that contains the information, allocate memory for
+> that struct, and store the pointer in `data_ptr`.
+> For illustrating how properties sources and sinks can be implemented, this was not necessary.
+
+### Additional Status Codes
+
+For sources and sinks there are three additional special status codes,
+which only appear as return values for `cxPropertiesLoad()`.
+
+| Status Code | Meaning |
+|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. |
+| CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. |
+| CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. |
+