docs/Writerside/topics/properties.h.md

changeset 1555
8972247f54e8
parent 1553
7c46531efd52
equal deleted inserted replaced
1554:91fb10c89611 1555:8972247f54e8
59 59
60 int cxPropertiesFill(CxProperties *prop, AnyStr string); 60 int cxPropertiesFill(CxProperties *prop, AnyStr string);
61 61
62 CxPropertiesStatus cxPropertiesNext(CxProperties *prop, 62 CxPropertiesStatus cxPropertiesNext(CxProperties *prop,
63 cxstring *key, cxstring *value); 63 cxstring *key, cxstring *value);
64 64
65 void cxPropertiesUseStack(CxProperties *prop, 65 void cxPropertiesUseStack(CxProperties *prop,
66 char *buf, size_t capacity); 66 char *buf, size_t capacity);
67
68 CxPropertiesStatus cxPropertiesLoad(CxPropertiesConfig config,
69 AnyStr filename, CxMap *target);
70
71 CxPropertiesStatus cxPropertiesLoadDefault(
72 AnyStr filename, CxMap *target);
67 ``` 73 ```
68 74
69 The first step is to initialize a `CxProperties` structure with a call to `cxPropertiesInit()` using the desired config. 75 The first step is to initialize a `CxProperties` structure with a call to `cxPropertiesInit()` using the desired config.
70 The shorthand `cxPropertiesInitDefault()` creates a default configuration with the equals sign `'='` as delimiter 76 The shorthand `cxPropertiesInitDefault()` creates a default configuration with the equals sign `'='` as delimiter
71 and the hash-symbol `'#'` as comment symbol (the other two comment symbols remain unused in the default config). 77 and the hash-symbol `'#'` as comment symbol (the other two comment symbols remain unused in the default config).
103 Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser. 109 Otherwise, you should always call `cxPropertiesDestroy()` when you are done with the parser.
104 110
105 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser, 111 > It is strongly recommended to always call `cxPropertiesDestroy()` when you are done with the parser,
106 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`. 112 > even if you did not expect any allocations because you used `cxPropertiesUseStack()`.
107 113
114 All the above operations are combined in the function `cxPropertiesLoad()`,
115 which opens the file designated by the `filename` and loads all properties from that file into the specified `CxMap`.
116 The convenience macro `cxPropertiesLoadDefault()` uses the default parser configuration for this.
117 The target map must either store pointers of type `char*` or elements of type `cxmutstr`.
118
119 > The stack buffers used by `cxPropertiesLoad()` can be changed when building UCX from sources
120 > by setting the `CX_PROPERTIES_LOAD_FILL_SIZE` and `CX_PROPERTIES_LOAD_BUF_SIZE` macros
121 > (see [](install.md#small-buffer-optimizations)).
122
108 ### List of Status Codes 123 ### List of Status Codes
109 124
110 Below is a full list of status codes for `cxPropertiesNext()`. 125 Below is a full list of status codes for `cxPropertiesNext()` and `cxPropertiesLoad()`.
111 126
112 | Status Code | Meaning | 127 | Status Code | Meaning |
113 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 128 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
114 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. | 129 | CX_PROPERTIES_NO_ERROR | A key/value pair was found and returned. |
115 | CX_PROPERTIES_NO_DATA | The input buffer does not contain more data. | 130 | CX_PROPERTIES_NO_DATA | The input buffer does not contain data. |
116 | 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. | 131 | 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. |
117 | CX_PROPERTIES_NULL_INPUT | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once. | 132 | CX_PROPERTIES_NULL_INPUT | The input buffer was never initialized. Probably you forgot to call `cxPropertiesFill()` at least once. |
118 | CX_PROPERTIES_INVALID_EMPTY_KEY | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty. | 133 | CX_PROPERTIES_INVALID_EMPTY_KEY | Only white-spaces were found on the left hand-side of the delimiter. Keys must not be empty. |
119 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter. | 134 | CX_PROPERTIES_INVALID_MISSING_DELIMITER | A line contains data, but no delimiter. |
120 | CX_PROPERTIES_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. | 135 | CX_PROPERTIES_BUFFER_ALLOC_FAILED | More internal buffer was needed, but could not be allocated. |
136 | CX_PROPERTIES_FILE_ERROR | A file operation failed (only for `cxPropertiesLoad()`). |
121 137
122 138 For `cxPropertiesLoad()` the status code `CX_PROPERTIES_NO_ERROR` means that at least one property was loaded into the map,
123 ## Sources and Sinks 139 while `CX_PROPERTIES_NO_DATA` means that the file is syntactically fine but does not contain any properties.
124
125 ```C
126 #include <cx/properties.h>
127
128 CxPropertiesSource
129 cxPropertiesStringSource(cxstring str);
130
131 CxPropertiesSource
132 cxPropertiesCstrSource(const char *str);
133
134 CxPropertiesSource
135 cxPropertiesCstrnSource(const char *str, size_t len);
136
137 CxPropertiesSource
138 cxPropertiesFileSource(FILE *file, size_t chunk_size);
139
140 CxPropertiesSink
141 cxPropertiesMapSink(CxMap *map);
142
143 CxPropertiesStatus
144 cxPropertiesLoad(CxProperties *prop,
145 CxPropertiesSink sink, CxPropertiesSource source);
146 ```
147
148 The basic idea of `cxPropertiesLoad()` is that key/value-pairs are extracted from a _source_ and ingested by a _sink_.
149 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.
150 But you can specify your [own sources and sinks](#creating-own-sources-and-sinks), as well.
151
152 The following example shows a simple function which loads all properties data from a file.
153 The `chunk_size` argument when creating the file source specifies
154 how many bytes are read from the file and filled into the properties parser in one read/sink cycle.
155
156 ```C
157 #include <stdio.h>
158 #include <cx/properties.h>
159
160 int load_props_from_file(const char *filename, CxMap *map) {
161 FILE *f = fopen(filename, "r");
162 if (!f) return -1;
163 CxProperties prop;
164 cxPropertiesInitDefault(&prop);
165 CxPropertiesSink sink = cxPropertiesMapSink(map);
166 CxPropertiesSource src = cxPropertiesFileSource(f, 512);
167 CxPropertiesStatus status = cxPropertiesLoad(&prop, sink, src);
168 fclose(f);
169 return status;
170 }
171
172 // usage:
173 CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS);
174 if (load_props_from_file("my-props.properties", map)) {
175 // error handling
176 } else {
177 // assuming my-props.properties contains the following line:
178 // my-key = some value
179 char *value = cxMapGet(map, "my-key");
180 }
181 ```
182
183 > The function `cxPropertiesLoad()` should usually not return `CX_PROPERTIES_INCOMPLETE_DATA` because the parser is automatically refilled from the source.
184 > If it does, it could mean that the source was unable to provide all the data, or the properties data ended unexpectedly.
185 > The most expected status code is `CX_PROPERTIES_NO_ERROR` which means that at least one key/value-pair was found.
186 > If `cxPropertiesLoad()` returns `CX_PROPERTIES_NO_DATA` it means that the source did not provide any key/value-pair.
187 > There are several special status codes that are documented [below](#additional-status-codes).
188
189 ### Creating own Sources and Sinks
190
191 ```C
192 #include <cx/properties.h>
193
194 typedef int(*cx_properties_read_init_func)(CxProperties *prop,
195 CxPropertiesSource *src);
196
197 typedef int(*cx_properties_read_func)(CxProperties *prop,
198 CxPropertiesSource *src, cxstring *target);
199
200 typedef void(*cx_properties_read_clean_func)(CxProperties *prop,
201 CxPropertiesSource *src);
202
203 typedef int(*cx_properties_sink_func)(CxProperties *prop,
204 CxPropertiesSink *sink, cxstring key, cxstring value);
205
206 typedef struct cx_properties_source_s {
207 void *src;
208 void *data_ptr;
209 size_t data_size;
210 cx_properties_read_func read_func;
211 cx_properties_read_init_func read_init_func;
212 cx_properties_read_clean_func read_clean_func;
213 } CxPropertiesSource;
214
215 typedef struct cx_properties_sink_s {
216 void *sink;
217 void *data;
218 cx_properties_sink_func sink_func;
219 } CxPropertiesSink;
220 ```
221
222 You can create your own sources and sinks by initializing the respective structures.
223
224 For a source, only the `read_func` is mandatory, the other two functions are optional and used for initialization and cleanup, if required.
225 The file source created by `cxPropertiesFileSource()`, for example,
226 uses the `read_init_func` to allocate, and the `read_clean_func` to free the read buffer, respectively.
227
228 Since the default map sink created by `cxPropertiesMapSink()` stores `char*` pointers into a map,
229 the following example uses a different sink, which stores them as `cxmutstr` values, automatically freeing them
230 when the map gets destroyed.
231 And instead of reading the data from a file with `fread()`, it uses `mmap()` to map the file into memory for reading.
232
233 ```C
234 #include <stdio.h>
235 #include <unistd.h>
236 #include <fcntl.h>
237 #include <sys/stat.h>
238 #include <sys/mman.h>
239 #include <cx/properties.h>
240 #include <cx/hash_map.h>
241
242 static int prop_mmap(CxProperties *prop, CxPropertiesSource *src) {
243 struct stat s;
244 int fd = open(src->src, O_RDONLY);
245 if (fd < 0) return -1;
246 // re-use the data field to store the fd
247 // there are cleaner ways, but this is just for illustration
248 src->src = (void*) fd;
249 fstat(fd, &s);
250 // memory map the entire file
251 // and store the address and length in the properties source
252 src->data_ptr = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
253 src->data_size = s.st_size;
254 return src->data_ptr == NULL;
255 }
256
257 static int prop_read(CxProperties *prop, CxPropertiesSource *src,
258 cxstring *target) {
259 // copy the address and length of the mapped data to the target
260 target->ptr = src->data_ptr;
261 target->length = src->data_size;
262 // set the new size to zero to indicate that there is no more data
263 src->data_size = 0;
264 return 0;
265 }
266
267 static void prop_unmap(CxProperties *prop, CxPropertiesSource *src) {
268 // unmap the memory and close the file
269 munmap(src->data_ptr, src->data_size);
270 close((int)src->src);
271 }
272
273 static int prop_sink(CxProperties *prop, CxPropertiesSink *sink,
274 cxstring key, cxstring value) {
275 CxMap *map = sink->sink;
276 // copy the string and store it into the map
277 cxmutstr v = cx_strdup(value);
278 int r = cxMapPut(map, key, &v);
279 if (r != 0) cx_strfree(&v);
280 return r;
281 }
282
283 int load_props_from_file(const char *filename, CxMap *map) {
284 CxProperties prop;
285 cxPropertiesInitDefault(&prop);
286 CxPropertiesSource src;
287 src.src = (void*) filename;
288 src.read_init_func = prop_mmap;
289 src.read_func = prop_read;
290 src.read_clean_func = prop_unmap;
291 CxPropertiesSink sink;
292 sink.sink = map;
293 sink.sink_func = prop_sink;
294 return cxPropertiesLoad(&prop, sink, src);
295 }
296
297 int main() {
298 // in contrast to the default map sink,
299 // this one here stores the UCX strings by value
300 CxMap *map = cxHashMapCreateSimple(sizeof(cxmutstr));
301
302 // automatically free the UCX string when removed from the map
303 cxDefineDestructor(map, cx_strfree);
304
305 // use our custom load function to load the data from the file
306 if (load_props_from_file("my-props.properties", map)) {
307 fputs("Error reading properties.\n", stderr);
308 return 1;
309 }
310
311 // output the read key/value pairs for illustration
312 CxMapIterator iter = cxMapIterator(map);
313 cx_foreach(CxMapEntry *, entry, iter) {
314 cxstring k = cx_strn(entry->key->data, entry->key->len);
315 cxmutstr *v = entry->value;
316 printf("%" CX_PRIstr " = %" CX_PRIstr "\n",
317 CX_SFMT(k), CX_SFMT(*v));
318 }
319
320 // freeing the map also frees the strings
321 // because we have registered cx_strfree() as destructor function
322 cxMapFree(map);
323
324 return 0;
325 }
326 ```
327
328 > A cleaner implementation that does not produce a warning for bluntly casting an `int` to a `void*`
329 > can be achieved by declaring a struct that contains the information, allocate memory for
330 > that struct, and store the pointer in `data_ptr`.
331 > For illustrating how properties sources and sinks can be implemented, this was not necessary.
332
333 ### Additional Status Codes
334
335 For sources and sinks there are three additional special status codes,
336 which only appear as return values for `cxPropertiesLoad()`.
337
338 | Status Code | Meaning |
339 |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
340 | CX_PROPERTIES_READ_INIT_FAILED | Initializing the properties source failed and the `cx_properties_read_init_func` returned non-zero. |
341 | CX_PROPERTIES_READ_FAILED | Reading from a properties source failed and the `cx_properties_read_func` returned non-zero. |
342 | CX_PROPERTIES_SINK_FAILED | Sinking a key/value-pair failed and the `cx_properties_sink_func` returned non-zero. |
343
344 140
345 <seealso> 141 <seealso>
346 <category ref="apidoc"> 142 <category ref="apidoc">
347 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a> 143 <a href="https://ucx.sourceforge.io/api/properties_8h.html">properties.h</a>
348 </category> 144 </category>

mercurial