forked from Imagelibrary/binutils-gdb
libctf, include: new functions for looking up enumerators
Three new functions for looking up the enum type containing a given
enumeration constant, and optionally that constant's value.
The simplest, ctf_lookup_enumerator, looks up a root-visible enumerator by
name in one dict: if the dict contains multiple such constants (which is
possible for dicts created by older versions of the libctf deduplicator),
ECTF_DUPLICATE is returned.
The next simplest, ctf_lookup_enumerator_next, is an iterator which returns
all enumerators with a given name in a given dict, whether root-visible or
not.
The most elaborate, ctf_arc_lookup_enumerator_next, finds all
enumerators with a given name across all dicts in an entire CTF archive,
whether root-visible or not, starting looking in the shared parent dict;
opened dicts are cached (as with all other ctf_arc_*lookup functions) so
that repeated use does not incur repeated opening costs.
All three of these return enumerator values as int64_t: unfortunately, API
compatibility concerns prevent us from doing the same with the other older
enum-related functions, which all return enumerator constant values as ints.
We may be forced to add symbol-versioning compatibility aliases that fix the
other functions in due course, bumping the soname for platforms that do not
support such things.
ctf_arc_lookup_enumerator_next is implemented as a nested ctf_archive_next
iterator, and inside that, a nested ctf_lookup_enumerator_next iterator
within each dict. To aid in this, add support to ctf_next_t iterators for
iterators that are implemented in terms of two simultaneous nested iterators
at once. (It has always been possible for callers to use as many nested or
semi-overlapping ctf_next_t iterators as they need, which is one of the
advantages of this style over the _iter style that calls a function for each
thing iterated over: the iterator change here permits *ctf_next_t iterators
themselves* to be implemented by iterating using multiple other iterators as
part of their internal operation, transparently to the caller.)
Also add a testcase that tests all these functions (which is fairly easy
because ctf_arc_lookup_enumerator_next is implemented in terms of
ctf_lookup_enumerator_next) in addition to enumeration addition in
ctf_open()ed dicts, ctf_add_enumerator duplicate enumerator addition, and
conflicting enumerator constant deduplication.
include/
* ctf-api.h (ctf_lookup_enumerator): New.
(ctf_lookup_enumerator_next): Likewise.
(ctf_arc_lookup_enumerator_next): Likewise.
libctf/
* libctf.ver: Add them.
* ctf-impl.h (ctf_next_t) <ctn_next_inner>: New.
* ctf-util.c (ctf_next_copy): Copy it.
(ctf_next_destroy): Destroy it.
* ctf-lookup.c (ctf_lookup_enumerator): New.
(ctf_lookup_enumerator_next): New.
* ctf-archive.c (ctf_arc_lookup_enumerator_next): New.
* testsuite/libctf-lookup/enumerator-iteration.*: New test.
* testsuite/libctf-lookup/enum-ctf-2.c: New test CTF, used by the
above.
This commit is contained in:
@@ -413,6 +413,151 @@ ctf_lookup_variable (ctf_dict_t *fp, const char *name)
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Look up a single enumerator by enumeration constant name. Returns the ID of
|
||||
the enum it is contained within and optionally its value. Error out with
|
||||
ECTF_DUPLICATE if multiple exist (which can happen in some older dicts). See
|
||||
ctf_lookup_enumerator_next in that case. Enumeration constants in non-root
|
||||
types are not returned, but constants in parents are, if not overridden by
|
||||
an enum in the child.. */
|
||||
|
||||
ctf_id_t
|
||||
ctf_lookup_enumerator (ctf_dict_t *fp, const char *name, int64_t *enum_value)
|
||||
{
|
||||
ctf_id_t type;
|
||||
int enum_int_value;
|
||||
|
||||
if (ctf_dynset_lookup (fp->ctf_conflicting_enums, name))
|
||||
return (ctf_set_typed_errno (fp, ECTF_DUPLICATE));
|
||||
|
||||
/* CTF_K_UNKNOWN suffices for things like enumeration constants that aren't
|
||||
actually types at all (ending up in the global name table). */
|
||||
type = ctf_lookup_by_rawname (fp, CTF_K_UNKNOWN, name);
|
||||
/* Nonexistent type? It may be in the parent. */
|
||||
if (type == 0 && fp->ctf_parent)
|
||||
{
|
||||
if ((type = ctf_lookup_enumerator (fp->ctf_parent, name, enum_value)) == 0)
|
||||
return ctf_set_typed_errno (fp, ECTF_NOENUMNAM);
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Nothing more to do if this type didn't exist or we don't have to look up
|
||||
the enum value. */
|
||||
if (type == 0)
|
||||
return ctf_set_typed_errno (fp, ECTF_NOENUMNAM);
|
||||
|
||||
if (enum_value == NULL)
|
||||
return type;
|
||||
|
||||
if (ctf_enum_value (fp, type, name, &enum_int_value) < 0)
|
||||
return CTF_ERR;
|
||||
*enum_value = enum_int_value;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* Return all enumeration constants with a given name in a given dict, similar
|
||||
to ctf_lookup_enumerator above but capable of returning multiple values.
|
||||
Enumerators in parent dictionaries are not returned: enumerators in
|
||||
hidden types *are* returned. */
|
||||
|
||||
ctf_id_t
|
||||
ctf_lookup_enumerator_next (ctf_dict_t *fp, const char *name,
|
||||
ctf_next_t **it, int64_t *val)
|
||||
{
|
||||
ctf_next_t *i = *it;
|
||||
int found = 0;
|
||||
|
||||
/* We use ctf_type_next() to iterate across all types, but then traverse each
|
||||
enumerator found by hand: traversing enumerators is very easy, and it would
|
||||
probably be more confusing to use two nested iterators than to do it this
|
||||
way. We use ctn_next to work over enums, then ctn_en and ctn_n to work
|
||||
over enumerators within each enum. */
|
||||
if (!i)
|
||||
{
|
||||
if ((i = ctf_next_create ()) == NULL)
|
||||
return ctf_set_typed_errno (fp, ENOMEM);
|
||||
|
||||
i->cu.ctn_fp = fp;
|
||||
i->ctn_iter_fun = (void (*) (void)) ctf_lookup_enumerator_next;
|
||||
i->ctn_increment = 0;
|
||||
i->ctn_tp = NULL;
|
||||
i->u.ctn_en = NULL;
|
||||
i->ctn_n = 0;
|
||||
*it = i;
|
||||
}
|
||||
|
||||
if ((void (*) (void)) ctf_lookup_enumerator_next != i->ctn_iter_fun)
|
||||
return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN));
|
||||
|
||||
if (fp != i->cu.ctn_fp)
|
||||
return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP));
|
||||
|
||||
do
|
||||
{
|
||||
const char *this_name;
|
||||
|
||||
/* At end of enum? Traverse to next one, if any are left. */
|
||||
|
||||
if (i->u.ctn_en == NULL || i->ctn_n == 0)
|
||||
{
|
||||
const ctf_type_t *tp;
|
||||
ctf_dtdef_t *dtd;
|
||||
|
||||
do
|
||||
i->ctn_type = ctf_type_next (i->cu.ctn_fp, &i->ctn_next, NULL, 1);
|
||||
while (i->ctn_type != CTF_ERR
|
||||
&& ctf_type_kind_unsliced (i->cu.ctn_fp, i->ctn_type)
|
||||
!= CTF_K_ENUM);
|
||||
|
||||
if (i->ctn_type == CTF_ERR)
|
||||
{
|
||||
/* Conveniently, when the iterator over all types is done, so is the
|
||||
iteration as a whole: so we can just pass all errors from the
|
||||
internal iterator straight back out.. */
|
||||
ctf_next_destroy (i);
|
||||
*it = NULL;
|
||||
return CTF_ERR; /* errno is set for us. */
|
||||
}
|
||||
|
||||
if ((tp = ctf_lookup_by_id (&fp, i->ctn_type)) == NULL)
|
||||
return CTF_ERR; /* errno is set for us. */
|
||||
i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
||||
|
||||
dtd = ctf_dynamic_type (fp, i->ctn_type);
|
||||
|
||||
if (dtd == NULL)
|
||||
{
|
||||
(void) ctf_get_ctt_size (fp, tp, NULL, &i->ctn_increment);
|
||||
i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp +
|
||||
i->ctn_increment);
|
||||
}
|
||||
else
|
||||
i->u.ctn_en = (const ctf_enum_t *) dtd->dtd_vlen;
|
||||
}
|
||||
|
||||
this_name = ctf_strptr (fp, i->u.ctn_en->cte_name);
|
||||
|
||||
i->ctn_n--;
|
||||
|
||||
if (strcmp (name, this_name) == 0)
|
||||
{
|
||||
if (val)
|
||||
*val = i->u.ctn_en->cte_value;
|
||||
found = 1;
|
||||
|
||||
/* Constant found in this enum: try the next one. (Constant names
|
||||
cannot be duplicated within a given enum.) */
|
||||
|
||||
i->ctn_n = 0;
|
||||
}
|
||||
|
||||
i->u.ctn_en++;
|
||||
}
|
||||
while (!found);
|
||||
|
||||
return i->ctn_type;
|
||||
}
|
||||
|
||||
typedef struct ctf_symidx_sort_arg_cb
|
||||
{
|
||||
ctf_dict_t *fp;
|
||||
|
||||
Reference in New Issue
Block a user