use alloc::vec::Vec;
use core::hash::Hash;
use rabbitizer::access_type::AccessType;
use crate::{
addresses::{Size, Vram},
collections::{addended_ordered_map::SizedValue, unordered_map::UnorderedMap},
metadata::SymbolType,
};
#[derive(Debug, Clone)]
pub struct ReferencedAddress {
vram: Vram,
user_declared: bool,
referenced_by: Vec<Vram>,
access_types: UnorderedMap<AccessType, u32>,
user_declared_type: Option<SymbolType>,
autodetected_types: UnorderedMap<SymbolType, u32>,
user_declared_size: Option<Size>,
autodetected_size: Option<Size>,
table_labels: Vec<Vram>,
add_gp_to_pointed_data: bool,
}
impl ReferencedAddress {
pub(crate) fn new(vram: Vram) -> Self {
Self {
vram,
user_declared: false,
referenced_by: Vec::new(),
access_types: UnorderedMap::new(),
user_declared_type: None,
autodetected_types: UnorderedMap::new(),
user_declared_size: None,
autodetected_size: None,
table_labels: Vec::new(),
add_gp_to_pointed_data: false,
}
}
pub(crate) fn new_user_declared(vram: Vram) -> Self {
Self {
user_declared: true,
..Self::new(vram)
}
}
pub const fn vram(&self) -> Vram {
self.vram
}
pub const fn user_declared(&self) -> bool {
self.user_declared
}
pub fn referenced_by(&self) -> &[Vram] {
&self.referenced_by
}
pub fn access_type(&self) -> Option<AccessType> {
if self.access_types.len() == 1 {
self.access_types
.iter()
.next()
.map(|(access, _count)| *access)
} else {
None
}
}
pub(crate) fn all_access_types(&self) -> &UnorderedMap<AccessType, u32> {
&self.access_types
}
pub fn sym_type(&self) -> Option<SymbolType> {
if let Some(typ) = self.user_declared_type {
Some(typ)
} else if self.autodetected_types.len() == 1 {
self.autodetected_types
.iter()
.next()
.map(|(typ, _count)| *typ)
} else {
self.access_type().and_then(SymbolType::from_access_type)
}
}
pub fn size(&self) -> Option<Size> {
if let Some(size) = self.user_declared_size {
Some(size)
} else if self.user_declared_type.is_some() {
None
} else if let Some(size) = self.autodetected_size {
Some(size)
} else if !self.autodetected_types.is_empty() {
None
} else {
self.access_type()
.and_then(|x| x.min_size().map(|x| Size::new(x.into())))
}
}
pub fn user_declared_size(&self) -> Option<Size> {
self.user_declared_size
}
pub fn autodetected_size(&self) -> Option<Size> {
self.autodetected_size
}
pub fn alignment(&self) -> Option<u8> {
if self.user_declared_type.is_some() || !self.autodetected_types.is_empty() {
None
} else {
self.access_type().and_then(|x| x.min_alignment())
}
}
pub fn reference_counter(&self) -> usize {
self.referenced_by.len()
}
pub(crate) fn is_trustable_function(&self) -> bool {
match self.sym_type() {
Some(SymbolType::Function) => true,
None => {
self.user_declared
}
Some(_) => false,
}
}
pub fn table_labels(&self) -> &[Vram] {
&self.table_labels
}
#[must_use]
pub(crate) fn add_gp_to_pointed_data(&self) -> bool {
self.add_gp_to_pointed_data
}
}
impl ReferencedAddress {
pub(crate) fn add_referenced_by(&mut self, specific_address: Vram) {
self.referenced_by.push(specific_address);
}
pub(crate) fn set_access_type(&mut self, access_type: AccessType) {
*self.access_types.entry(access_type).or_default() += 1;
}
pub(crate) fn set_user_declared_type(&mut self, typ: SymbolType) {
self.user_declared_type = Some(typ);
}
pub(crate) fn set_sym_type(&mut self, sym_type: SymbolType) {
*self.autodetected_types.entry(sym_type).or_default() += 1;
}
pub(crate) fn set_user_declared_size(&mut self, size: Size) {
self.user_declared_size = Some(size);
}
pub(crate) fn set_autodetected_size(&mut self, size: Size) {
self.autodetected_size = Some(size);
}
pub(crate) fn add_table_label(&mut self, label: Vram) {
self.table_labels.push(label);
}
pub(crate) fn set_from_other_reference(&mut self, other: Self) {
for other_vram in other.referenced_by {
self.add_referenced_by(other_vram);
}
for (access_type, count) in other.access_types {
*self.access_types.entry(access_type).or_default() += count;
}
if self.user_declared_type.is_none() {
self.user_declared_type = other.user_declared_type;
}
for (sym_type, count) in other.autodetected_types {
*self.autodetected_types.entry(sym_type).or_default() += count;
}
if self.user_declared_size.is_none() {
self.user_declared_size = other.user_declared_size;
}
if self.autodetected_size.is_none() {
self.autodetected_size = other.autodetected_size;
}
for label in other.table_labels {
self.add_table_label(label);
}
}
pub(crate) fn set_add_gp_to_pointed_data(&mut self) {
self.add_gp_to_pointed_data = true;
}
}
impl PartialEq for ReferencedAddress {
fn eq(&self, other: &Self) -> bool {
self.vram == other.vram
}
}
impl PartialOrd for ReferencedAddress {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.vram.partial_cmp(&other.vram)
}
}
impl Hash for ReferencedAddress {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.vram.hash(state);
}
}
impl SizedValue for ReferencedAddress {
fn size(&self) -> Size {
self.size().unwrap_or(Size::new(1))
}
}