docs/Writerside/topics/properties.h.md

changeset 1555
8972247f54e8
parent 1553
7c46531efd52
--- a/docs/Writerside/topics/properties.h.md	Sun Dec 07 15:33:16 2025 +0100
+++ b/docs/Writerside/topics/properties.h.md	Sun Dec 07 15:34:46 2025 +0100
@@ -61,9 +61,15 @@
 
 CxPropertiesStatus cxPropertiesNext(CxProperties *prop,
         cxstring *key, cxstring *value);
-        
+
 void cxPropertiesUseStack(CxProperties *prop,
         char *buf, size_t capacity);
+
+CxPropertiesStatus cxPropertiesLoad(CxPropertiesConfig config,
+        AnyStr filename, CxMap *target);
+
+CxPropertiesStatus cxPropertiesLoadDefault(
+        AnyStr filename, CxMap *target);
 ```
 
 The first step is to initialize a `CxProperties` structure with a call to `cxPropertiesInit()` using the desired config.
@@ -105,242 +111,32 @@
 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser,
 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`.
 
+All the above operations are combined in the function `cxPropertiesLoad()`,
+which opens the file designated by the `filename` and loads all properties from that file into the specified `CxMap`.
+The convenience macro `cxPropertiesLoadDefault()` uses the default parser configuration for this.
+The target map must either store pointers of type `char*` or elements of type `cxmutstr`.
+
+> The stack buffers used by `cxPropertiesLoad()` can be changed when building UCX from sources
+> by setting the `CX_PROPERTIES_LOAD_FILL_SIZE` and `CX_PROPERTIES_LOAD_BUF_SIZE` macros
+> (see [](install.md#small-buffer-optimizations)).
+
 ### List of Status Codes
 
-Below is a full list of status codes for `cxPropertiesNext()`.
+Below is a full list of status codes for `cxPropertiesNext()` and `cxPropertiesLoad()`.
 
 | Status Code                             | Meaning                                                                                                                                                                                                                 |
 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | CX_PROPERTIES_NO_ERROR                  | A key/value pair was found and returned.                                                                                                                                                                                |
-| CX_PROPERTIES_NO_DATA                   | The input buffer does not contain more data.                                                                                                                                                                            |
+| CX_PROPERTIES_NO_DATA                   | The input buffer does not contain data.                                                                                                                                                                                 |
 | CX_PROPERTIES_INCOMPLETE_DATA           | The input ends unexpectedly. This can happen when the last line does not terminate with a line break, or when the input ends with a parsed key but no value. Use `cxPropertiesFill()` to add more data before retrying. |
 | CX_PROPERTIES_NULL_INPUT                | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once.                                                                                                                 |
 | CX_PROPERTIES_INVALID_EMPTY_KEY         | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty.                                                                                                                            |
 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter.                                                                                                                                                                                 |
 | CX_PROPERTIES_BUFFER_ALLOC_FAILED       | More internal buffer was needed, but could not be allocated.                                                                                                                                                            |
-
-
-## Sources and Sinks
-
-```C
-#include <cx/properties.h>
-
-CxPropertiesSource
-cxPropertiesStringSource(cxstring str);
-
-CxPropertiesSource
-cxPropertiesCstrSource(const char *str);
-
-CxPropertiesSource
-cxPropertiesCstrnSource(const char *str, size_t len);
-
-CxPropertiesSource
-cxPropertiesFileSource(FILE *file, size_t chunk_size);
-        
-CxPropertiesSink
-cxPropertiesMapSink(CxMap *map);
-
-CxPropertiesStatus
-cxPropertiesLoad(CxProperties *prop,
-        CxPropertiesSink sink, CxPropertiesSource source);
-```
-
-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.
-
-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 <stdio.h>
-#include <cx/properties.h>
-
-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;
-}
-
-// 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 that are documented [below](#additional-status-codes). 
-
-### Creating own Sources and Sinks
-
-```C
-#include <cx/properties.h>
-
-typedef int(*cx_properties_read_init_func)(CxProperties *prop,
-        CxPropertiesSource *src);
-
-typedef int(*cx_properties_read_func)(CxProperties *prop,
-        CxPropertiesSource *src, cxstring *target);
-
-typedef void(*cx_properties_read_clean_func)(CxProperties *prop,
-        CxPropertiesSource *src);
-
-typedef int(*cx_properties_sink_func)(CxProperties *prop,
-        CxPropertiesSink *sink, cxstring key, cxstring value);
-
-typedef struct cx_properties_source_s {
-    void *src;
-    void *data_ptr;
-    size_t data_size;
-    cx_properties_read_func read_func;
-    cx_properties_read_init_func read_init_func;
-    cx_properties_read_clean_func read_clean_func;
-} CxPropertiesSource;
-
-typedef struct cx_properties_sink_s {
-    void *sink;
-    void *data;
-    cx_properties_sink_func sink_func;
-} CxPropertiesSink;
-```
-
-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.
-And instead of reading the data from a file with `fread()`, it uses `mmap()` to map the file into memory for reading.
+| CX_PROPERTIES_FILE_ERROR                | A file operation failed (only for `cxPropertiesLoad()`).                                                                                                                                                                |
 
-```C
-#include <stdio.h> 
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <cx/properties.h>
-#include <cx/hash_map.h>
-
-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("%" CX_PRIstr " = %" CX_PRIstr "\n",
-            CX_SFMT(k), CX_SFMT(*v));
-    }
-
-    // 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.                                                                                                                                    |
-
+For `cxPropertiesLoad()` the status code `CX_PROPERTIES_NO_ERROR` means that at least one property was loaded into the map,
+while `CX_PROPERTIES_NO_DATA` means that the file is syntactically fine but does not contain any properties.
 
 <seealso>
 <category ref="apidoc">

mercurial