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> |