dfhack/plugins/devel/check-structures-sanity/types.cpp

190 lines
5.1 KiB
C++

#include "check-structures-sanity.h"
QueueItem::QueueItem(const std::string & path, const void *ptr) :
path(path),
ptr(ptr)
{
}
QueueItem::QueueItem(const QueueItem & parent, const std::string & member, const void *ptr) :
QueueItem(parent.path + "." + member, ptr)
{
}
QueueItem::QueueItem(const QueueItem & parent, size_t index, const void *ptr) :
QueueItem(parent.path + stl_sprintf("[%zu]", index), ptr)
{
}
CheckedStructure::CheckedStructure() :
CheckedStructure(nullptr, 0)
{
}
CheckedStructure::CheckedStructure(type_identity *identity, size_t count) :
CheckedStructure(identity, count, nullptr, false)
{
}
CheckedStructure::CheckedStructure(type_identity *identity, size_t count, enum_identity *eid, bool inside_structure) :
identity(identity),
count(count),
allocated_count(0),
eid(eid),
ptr_is_array(false),
inside_structure(inside_structure)
{
}
CheckedStructure::CheckedStructure(const struct_field_info *field) :
CheckedStructure()
{
if (!field || field->mode == struct_field_info::END)
{
UNEXPECTED;
}
identity = field->type;
eid = field->extra ? field->extra->index_enum : nullptr;
inside_structure = true;
switch (field->mode)
{
case struct_field_info::END:
UNEXPECTED;
break;
case struct_field_info::PRIMITIVE:
if (field->count || !field->type)
{
UNEXPECTED;
}
break;
case struct_field_info::STATIC_STRING:
if (!field->count || field->type)
{
UNEXPECTED;
}
identity = df::identity_traits<char>::get();
count = field->count;
break;
case struct_field_info::POINTER:
if (field->count & PTRFLAG_IS_ARRAY)
{
ptr_is_array = true;
}
identity = Checker::wrap_in_pointer(field->type);
break;
case struct_field_info::STATIC_ARRAY:
if (!field->count || !field->type)
{
UNEXPECTED;
}
count = field->count;
break;
case struct_field_info::SUBSTRUCT:
case struct_field_info::CONTAINER:
if (field->count || !field->type)
{
UNEXPECTED;
}
break;
case struct_field_info::STL_VECTOR_PTR:
if (field->count)
{
UNEXPECTED;
}
identity = Checker::wrap_in_stl_ptr_vector(field->type);
break;
case struct_field_info::OBJ_METHOD:
case struct_field_info::CLASS_METHOD:
UNEXPECTED;
break;
}
}
size_t CheckedStructure::full_size() const
{
size_t size = identity->byte_size();
if (count)
{
size *= count;
}
return size;
}
bool CheckedStructure::has_type_at_offset(const CheckedStructure & type, size_t offset) const
{
if (!identity)
return false;
if (offset == 0 && identity == type.identity)
return true;
if (offset >= identity->byte_size() && offset < full_size())
offset %= identity->byte_size();
else if (offset >= identity->byte_size())
return false;
if (identity->type() == IDTYPE_BUFFER)
{
auto target = static_cast<container_identity *>(identity)->getItemType();
return CheckedStructure(target, 0).has_type_at_offset(type, offset % target->byte_size());
}
auto st = dynamic_cast<struct_identity *>(identity);
if (!st)
{
return false;
}
for (auto p = st; p; p = p->getParent())
{
auto fields = p->getFields();
if (!fields)
continue;
for (auto field = fields; field->mode != struct_field_info::END; field++)
{
if (field->mode == struct_field_info::OBJ_METHOD || field->mode == struct_field_info::CLASS_METHOD)
continue;
if (field->offset > offset)
continue;
if (CheckedStructure(field).has_type_at_offset(type, offset - field->offset))
return true;
}
}
return false;
}
type_identity *Checker::wrap_in_stl_ptr_vector(type_identity *base)
{
static std::map<type_identity *, std::unique_ptr<df::stl_ptr_vector_identity>> wrappers;
auto it = wrappers.find(base);
if (it != wrappers.end())
{
return it->second.get();
}
return (wrappers[base] = std::make_unique<df::stl_ptr_vector_identity>(base, nullptr)).get();
}
type_identity *Checker::wrap_in_pointer(type_identity *base)
{
static std::map<type_identity *, std::unique_ptr<df::pointer_identity>> wrappers;
auto it = wrappers.find(base);
if (it != wrappers.end())
{
return it->second.get();
}
return (wrappers[base] = std::make_unique<df::pointer_identity>(base)).get();
}
std::map<size_t, std::vector<std::string>> known_types_by_size;
void build_size_table()
{
for (auto & ident : compound_identity::getTopScope())
{
if (ident->byte_size() >= MIN_SIZE_FOR_SUGGEST)
{
known_types_by_size[ident->byte_size()].push_back(ident->getFullName());
}
}
}