From 0dcd946063e1b602c4b30850feab0407094c2e85 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 9 Feb 2021 15:24:28 +0900 Subject: [PATCH] [util] Add plist parser for dictionary -> array PL_ParseLabeledArray works the same way as PL_ParseArray, but instead takes a dictionary object. The keys of the items are ignored, and the order is not preserved (at this stage), but this is a cleaner solution to getting an array of objects when the definitions of those objects need to be accessible by name as well. --- include/QF/qfplist.h | 39 +++++++++++++++++++++++++++ libs/util/qfplist.c | 63 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/include/QF/qfplist.h b/include/QF/qfplist.h index 67eb68588..3e1086be0 100644 --- a/include/QF/qfplist.h +++ b/include/QF/qfplist.h @@ -396,6 +396,45 @@ int PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, int PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data, plitem_t *messages, void *context); +/** Parse a dictionary object into a dynamic array (see darray.h). + + This is useful when the dictionary object is meant to be a labeled list + rather than a representation of a structure. + + For each object in the array, the field item is used to determine how to + parse the object. If the array is empty, the destination will be + initialized to an empty array. + + When an error occurs (incorrect item type (item type does not match the + type specified in the element object) or the element object's \a parser + returns 0), processing continues but the error result is returned. + + Can be used recursively to parse deep hierarchies. + + \param field Pointer to a single field that has the field data pointer + set to reference a plelement_t object used to describe + the contents of the array. + \param dict The dict object to parse + \param data Pointer to the pointer to which the dynamic array will + be written. The dynamic array is allocated using + DARRAY_ALLOCFIXED_OBJ(). + \param messages Array object supplied by the caller used for storing + messages. The messages may or may not indicate errors (its + contents are not checked). This function itself will add + only string objects. + If there are any errors, suitable messages will be found in + the \a messages object. However, just because there are no + errors doesn't mean that \a messages will remain empty as + a field's \a parser may add other messages. The standard + message format is "[line number]: [message]". If the line + number is 0, then the actual line is unknown (due to the + source item not being parsed from a file or string). + \param context Additional context data passed to the parser and allocator. + \return 0 if there are any errors, 1 if there are no errors. +*/ +int PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict, + void *data, plitem_t *messages, void *context); + /** Parse a dictionary object into a hash table. For each key in the dictionary, the element object is used to determine diff --git a/libs/util/qfplist.c b/libs/util/qfplist.c index 4b7e34e7a..6bee78c3f 100644 --- a/libs/util/qfplist.c +++ b/libs/util/qfplist.c @@ -1211,10 +1211,7 @@ PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, void *data, return 0; } - if (!(l = list = Hash_GetList ((hashtab_t *) dict->data))) { - // empty struct: leave as default - return 1; - } + l = list = Hash_GetList ((hashtab_t *) dict->data); while ((current = (dictkey_t *) *l++)) { const plfield_t *f; @@ -1299,6 +1296,64 @@ PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data, return result; } +VISIBLE int +PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict, + void *data, plitem_t *messages, void *context) +{ + void **list, **l; + dictkey_t *current; + int result = 1; + plparser_t parser; + plelement_t *element = (plelement_t *) field->data; + typedef struct arr_s DARRAY_TYPE(byte) arr_t; + arr_t *arr; + plfield_t f = { 0, 0, element->type, element->parser, element->data }; + + if (dict->type != QFDictionary) { + PL_Message (messages, dict, "error: not a dictionary object"); + return 0; + } + if (f.parser) { + parser = f.parser; + } else { + parser = pl_default_parser; + } + + list = Hash_GetList ((hashtab_t *) dict->data); + + int numvals = 0; + for (l = list; *l; l++) { + numvals++; + } + + arr = DARRAY_ALLOCFIXED_OBJ (arr_t, numvals * element->stride, + element->alloc, context); + memset (arr->a, 0, arr->size); + // the array is allocated using bytes, but need the actual number of + // elements in the array + arr->size = arr->maxSize = numvals; + + for (int i = 0; i < numvals; i++) { + current = list[i]; + plitem_t *item = current->value; + void *eledata = &arr->a[i * element->stride]; + + if (!PL_CheckType (element->type, item->type)) { + char index[16]; + snprintf (index, sizeof(index) - 1, "%d", i); + index[15] = 0; + PL_TypeMismatch (messages, item, index, element->type, item->type); + result = 0; + } else { + if (!parser (&f, item, eledata, messages, context)) { + result = 0; + } + } + } + *(arr_t **) data = arr; + return result; +} + VISIBLE int PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data, plitem_t *messages, void *context)