mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-21 18:01:15 +00:00
[util] Keep track of plist dictionary key order
The order in which keys are added to the dictionary object is maintained. Adding a key after removing an old key adds the new key to the end of the list rather than reusing the old key's spot.
This commit is contained in:
parent
0dcd946063
commit
5535d1f8e6
2 changed files with 109 additions and 76 deletions
|
@ -197,12 +197,24 @@ plitem_t *PL_ObjectForKey (const plitem_t *dict, const char *key);
|
|||
*/
|
||||
plitem_t *PL_RemoveObjectForKey (const plitem_t *dict, const char *key);
|
||||
|
||||
/** Retrieve a key from a dictionary object.
|
||||
|
||||
\param dict The dictionary to get the key from
|
||||
\param index The index of the key
|
||||
\return the key at the specified index, or NULL if index is out of range or
|
||||
dict is not a dictionaly
|
||||
isn't an array.
|
||||
\note You are NOT responsible for freeing the returned object. It will
|
||||
be destroyed when its container is.
|
||||
*/
|
||||
const char *PL_KeyAtIndex (const plitem_t *array, int index) __attribute__((pure));
|
||||
|
||||
/** Retrieve a value from an array object.
|
||||
|
||||
\param array The array to get the value from
|
||||
\param index The index within the array to retrieve
|
||||
\return the value associated with the key, or NULL if not found or array
|
||||
isn't an array.
|
||||
\return the value at the specified index, or NULL if index is out of range
|
||||
or array is not an array.
|
||||
\note You are NOT responsible for freeing the returned object. It will
|
||||
be destroyed when its container is.
|
||||
*/
|
||||
|
|
|
@ -64,6 +64,12 @@ struct dictkey_s {
|
|||
};
|
||||
typedef struct dictkey_s dictkey_t;
|
||||
|
||||
struct pldict_s {
|
||||
hashtab_t *tab;
|
||||
struct DARRAY_TYPE (dictkey_t *) keys;
|
||||
};
|
||||
typedef struct pldict_s pldict_t;
|
||||
|
||||
/*
|
||||
Arrays
|
||||
*/
|
||||
|
@ -161,8 +167,9 @@ VISIBLE plitem_t *
|
|||
PL_NewDictionary (hashlink_t **hashlinks)
|
||||
{
|
||||
plitem_t *item = PL_NewItem (QFDictionary);
|
||||
hashtab_t *dict = Hash_NewTable (1021, dict_get_key, dict_free, NULL,
|
||||
hashlinks);
|
||||
pldict_t *dict = malloc (sizeof (pldict_t));
|
||||
dict->tab = Hash_NewTable (1021, dict_get_key, dict_free, NULL, hashlinks);
|
||||
DARRAY_INIT (&dict->keys, 8);
|
||||
item->data = dict;
|
||||
return item;
|
||||
}
|
||||
|
@ -205,18 +212,25 @@ PL_NewString (const char *str)
|
|||
VISIBLE void
|
||||
PL_Free (plitem_t *item)
|
||||
{
|
||||
pldict_t *dict;
|
||||
plarray_t *array;
|
||||
|
||||
switch (item->type) {
|
||||
case QFDictionary:
|
||||
Hash_DelTable (item->data);
|
||||
dict = item->data;
|
||||
Hash_DelTable (dict->tab);
|
||||
DARRAY_CLEAR (&dict->keys);
|
||||
break;
|
||||
|
||||
case QFArray: {
|
||||
int i = ((plarray_t *) item->data)->numvals;
|
||||
case QFArray:
|
||||
{
|
||||
array = item->data;
|
||||
int i = array->numvals;
|
||||
|
||||
while (i-- > 0) {
|
||||
PL_Free (((plarray_t *) item->data)->values[i]);
|
||||
PL_Free (array->values[i]);
|
||||
}
|
||||
free (((plarray_t *) item->data)->values);
|
||||
free (array->values);
|
||||
free (item->data);
|
||||
}
|
||||
break;
|
||||
|
@ -264,67 +278,85 @@ PL_String (const plitem_t *string)
|
|||
}
|
||||
|
||||
VISIBLE plitem_t *
|
||||
PL_ObjectForKey (const plitem_t *dict, const char *key)
|
||||
PL_ObjectForKey (const plitem_t *item, const char *key)
|
||||
{
|
||||
hashtab_t *table = (hashtab_t *) dict->data;
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
dictkey_t *k;
|
||||
|
||||
if (dict->type != QFDictionary)
|
||||
if (item->type != QFDictionary)
|
||||
return NULL;
|
||||
|
||||
k = (dictkey_t *) Hash_Find (table, key);
|
||||
k = (dictkey_t *) Hash_Find (dict->tab, key);
|
||||
return k ? k->value : NULL;
|
||||
}
|
||||
|
||||
VISIBLE plitem_t *
|
||||
PL_RemoveObjectForKey (const plitem_t *dict, const char *key)
|
||||
VISIBLE const char *
|
||||
PL_KeyAtIndex (const plitem_t *item, int index)
|
||||
{
|
||||
hashtab_t *table = (hashtab_t *) dict->data;
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
|
||||
if (item->type != QFDictionary)
|
||||
return NULL;
|
||||
if (index < 0 || (size_t) index >= dict->keys.size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dict->keys.a[index]->key;
|
||||
}
|
||||
|
||||
VISIBLE plitem_t *
|
||||
PL_RemoveObjectForKey (const plitem_t *item, const char *key)
|
||||
{
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
dictkey_t *k;
|
||||
plitem_t *value;
|
||||
|
||||
if (dict->type != QFDictionary)
|
||||
if (item->type != QFDictionary)
|
||||
return NULL;
|
||||
|
||||
k = (dictkey_t *) Hash_Del (table, key);
|
||||
k = (dictkey_t *) Hash_Del (dict->tab, key);
|
||||
if (!k)
|
||||
return NULL;
|
||||
value = k->value;
|
||||
k->value = 0;
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
if (dict->keys.a[i] == k) {
|
||||
DARRAY_REMOVE_AT (&dict->keys, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dict_free (k, 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
VISIBLE plitem_t *
|
||||
PL_D_AllKeys (plitem_t *dict)
|
||||
PL_D_AllKeys (plitem_t *item)
|
||||
{
|
||||
void **list, **l;
|
||||
dictkey_t *current;
|
||||
plitem_t *array;
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
dictkey_t *current;
|
||||
plitem_t *array;
|
||||
|
||||
if (dict->type != QFDictionary)
|
||||
return NULL;
|
||||
|
||||
if (!(l = list = Hash_GetList ((hashtab_t *) dict->data)))
|
||||
if (item->type != QFDictionary)
|
||||
return NULL;
|
||||
|
||||
if (!(array = PL_NewArray ()))
|
||||
return NULL;
|
||||
|
||||
while ((current = (dictkey_t *) *l++)) {
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
current = dict->keys.a[i];
|
||||
PL_A_AddObject (array, PL_NewString (current->key));
|
||||
}
|
||||
free (list);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
VISIBLE int
|
||||
PL_D_NumKeys (const plitem_t *dict)
|
||||
PL_D_NumKeys (const plitem_t *item)
|
||||
{
|
||||
if (dict->type != QFDictionary)
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
if (item->type != QFDictionary)
|
||||
return 0;
|
||||
return Hash_NumElements ((hashtab_t *) dict->data);
|
||||
return Hash_NumElements (dict->tab);
|
||||
}
|
||||
|
||||
VISIBLE plitem_t *
|
||||
|
@ -339,14 +371,15 @@ PL_ObjectAtIndex (const plitem_t *array, int index)
|
|||
}
|
||||
|
||||
VISIBLE qboolean
|
||||
PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value)
|
||||
PL_D_AddObject (plitem_t *item, const char *key, plitem_t *value)
|
||||
{
|
||||
pldict_t *dict = (pldict_t *) item->data;
|
||||
dictkey_t *k;
|
||||
|
||||
if (dict->type != QFDictionary)
|
||||
if (item->type != QFDictionary)
|
||||
return false;
|
||||
|
||||
if ((k = Hash_Find ((hashtab_t *)dict->data, key))) {
|
||||
if ((k = Hash_Find (dict->tab, key))) {
|
||||
PL_Free ((plitem_t *) k->value);
|
||||
k->value = value;
|
||||
} else {
|
||||
|
@ -358,7 +391,8 @@ PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value)
|
|||
k->key = strdup (key);
|
||||
k->value = value;
|
||||
|
||||
Hash_Add ((hashtab_t *)dict->data, k);
|
||||
Hash_Add (dict->tab, k);
|
||||
DARRAY_APPEND (&dict->keys, k);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1024,24 +1058,24 @@ write_string (dstring_t *dstr, const char *str)
|
|||
static void
|
||||
write_item (dstring_t *dstr, plitem_t *item, int level)
|
||||
{
|
||||
void **list, **l;
|
||||
dictkey_t *current;
|
||||
plarray_t *array;
|
||||
pldict_t *dict;
|
||||
plbinary_t *binary;
|
||||
int i;
|
||||
|
||||
switch (item->type) {
|
||||
case QFDictionary:
|
||||
write_string_len (dstr, "{\n", 2);
|
||||
l = list = Hash_GetList ((hashtab_t *) item->data);
|
||||
while ((current = (dictkey_t *) *l++)) {
|
||||
dict = (pldict_t *) item->data;
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
current = dict->keys.a[i];
|
||||
write_tabs (dstr, level + 1);
|
||||
write_string (dstr, current->key);
|
||||
write_string_len (dstr, " = ", 3);
|
||||
write_item (dstr, current->value, level + 1);
|
||||
write_string_len (dstr, ";\n", 2);
|
||||
}
|
||||
free (list);
|
||||
write_tabs (dstr, level);
|
||||
write_string_len (dstr, "}", 1);
|
||||
break;
|
||||
|
@ -1128,7 +1162,7 @@ pl_default_parser (const plfield_t *field, const plitem_t *item, void *data,
|
|||
switch (field->type) {
|
||||
case QFDictionary:
|
||||
{
|
||||
*(hashtab_t **)data = (hashtab_t *)item->data;
|
||||
*(hashtab_t **)data = ((pldict_t *)item->data)->tab;
|
||||
}
|
||||
return 1;
|
||||
case QFArray:
|
||||
|
@ -1198,23 +1232,23 @@ PL_TypeMismatch (plitem_t *messages, const plitem_t *item, const char *name,
|
|||
}
|
||||
|
||||
VISIBLE int
|
||||
PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, void *data,
|
||||
PL_ParseStruct (const plfield_t *fields, const plitem_t *item, void *data,
|
||||
plitem_t *messages, void *context)
|
||||
{
|
||||
void **list, **l;
|
||||
pldict_t *dict = item->data;
|
||||
dictkey_t *current;
|
||||
int result = 1;
|
||||
plparser_t parser;
|
||||
|
||||
if (dict->type != QFDictionary) {
|
||||
PL_Message (messages, dict, "error: not a dictionary object");
|
||||
if (item->type != QFDictionary) {
|
||||
PL_Message (messages, item, "error: not a dictionary object");
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = list = Hash_GetList ((hashtab_t *) dict->data);
|
||||
|
||||
while ((current = (dictkey_t *) *l++)) {
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
const plfield_t *f;
|
||||
current = dict->keys.a[i];
|
||||
for (f = fields; f->name; f++) {
|
||||
if (strcmp (f->name, current->key) == 0) {
|
||||
plitem_t *item = current->value;
|
||||
|
@ -1238,12 +1272,11 @@ PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, void *data,
|
|||
}
|
||||
}
|
||||
if (!f->name) {
|
||||
PL_Message (messages, dict, "error: unknown field %s",
|
||||
PL_Message (messages, item, "error: unknown field %s",
|
||||
current->key);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
free (list);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1297,10 +1330,10 @@ PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data,
|
|||
}
|
||||
|
||||
VISIBLE int
|
||||
PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict,
|
||||
PL_ParseLabeledArray (const plfield_t *field, const plitem_t *item,
|
||||
void *data, plitem_t *messages, void *context)
|
||||
{
|
||||
void **list, **l;
|
||||
pldict_t *dict = item->data;
|
||||
dictkey_t *current;
|
||||
int result = 1;
|
||||
plparser_t parser;
|
||||
|
@ -1309,8 +1342,8 @@ PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict,
|
|||
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");
|
||||
if (item->type != QFDictionary) {
|
||||
PL_Message (messages, item, "error: not a dictionary object");
|
||||
return 0;
|
||||
}
|
||||
if (f.parser) {
|
||||
|
@ -1319,28 +1352,21 @@ PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict,
|
|||
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,
|
||||
arr = DARRAY_ALLOCFIXED_OBJ (arr_t, dict->keys.size * 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;
|
||||
arr->size = arr->maxSize = dict->keys.size;
|
||||
|
||||
for (int i = 0; i < numvals; i++) {
|
||||
current = list[i];
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
current = dict->keys.a[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);
|
||||
snprintf (index, sizeof(index) - 1, "%zd", i);
|
||||
index[15] = 0;
|
||||
PL_TypeMismatch (messages, item, index, element->type, item->type);
|
||||
result = 0;
|
||||
|
@ -1355,10 +1381,10 @@ PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict,
|
|||
}
|
||||
|
||||
VISIBLE int
|
||||
PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data,
|
||||
PL_ParseSymtab (const plfield_t *field, const plitem_t *item, void *data,
|
||||
plitem_t *messages, void *context)
|
||||
{
|
||||
void **list, **l;
|
||||
pldict_t *dict = item->data;
|
||||
dictkey_t *current;
|
||||
int result = 1;
|
||||
plparser_t parser;
|
||||
|
@ -1367,26 +1393,22 @@ PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data,
|
|||
plelement_t *element = (plelement_t *) field->data;
|
||||
plfield_t f = { 0, 0, element->type, element->parser, element->data };
|
||||
|
||||
if (dict->type != QFDictionary) {
|
||||
PL_Message (messages, dict, "error: not a dictionary object");
|
||||
if (item->type != QFDictionary) {
|
||||
PL_Message (messages, item, "error: not a dictionary object");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f.parser) {
|
||||
parser = f.parser;
|
||||
} else {
|
||||
PL_Message (messages, dict, "no parser set");
|
||||
PL_Message (messages, item, "no parser set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(l = list = Hash_GetList ((hashtab_t *) dict->data))) {
|
||||
// empty struct: leave as default
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *obj = element->alloc (context, element->stride);
|
||||
memset (obj, 0, element->stride);
|
||||
while ((current = (dictkey_t *) *l++)) {
|
||||
for (size_t i = 0; i < dict->keys.size; i++) {
|
||||
current = dict->keys.a[i];
|
||||
const char *key = current->key;
|
||||
plitem_t *item = current->value;
|
||||
|
||||
|
@ -1410,6 +1432,5 @@ PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data,
|
|||
}
|
||||
}
|
||||
Hash_Free (tab, obj);
|
||||
free (list);
|
||||
return result;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue