use alloc::{collections::btree_set::BTreeSet, sync::Arc, vec::Vec};
use core::hash;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
use crate::{
addresses::{AddressRange, Vram},
collections::{unordered_map::UnorderedMap, unordered_set::UnorderedSet},
config::Compiler,
context::Context,
metadata::ParentSectionMetadata,
parent_segment_info::ParentSegmentInfo,
section_type::SectionType,
sections::{processed::NobitsSectionProcessed, EmptySectionError, SectionPreprocessed},
symbols::{
before_proc::{nobits_sym::NobitsSymProperties, NobitsSym},
Symbol, SymbolPreprocessed,
},
};
use crate::sections::{Section, SectionCreationError, SectionPostProcessError};
#[derive(Debug, Clone)]
#[must_use]
pub struct NobitsSection {
name: Arc<str>,
vram_range: AddressRange<Vram>,
parent_segment_info: ParentSegmentInfo,
nobits_symbols: Vec<NobitsSym>,
symbol_vrams: UnorderedSet<Vram>,
}
impl NobitsSection {
pub(crate) fn new(
context: &mut Context,
settings: &NobitsSectionSettings,
name: Arc<str>,
vram_range: AddressRange<Vram>,
parent_segment_info: ParentSegmentInfo,
) -> Result<Self, SectionCreationError> {
if vram_range.size().inner() == 0 {
return Err(EmptySectionError::new(name, vram_range.start()).into());
}
let mut nobits_symbols = Vec::new();
let mut symbol_vrams = UnorderedSet::new();
let owned_segment = context.find_owned_segment(&parent_segment_info)?;
let mut symbols_info = BTreeSet::new();
symbols_info.insert(vram_range.start());
let mut auto_pads: UnorderedMap<Vram, Vram> = UnorderedMap::new();
for reference in owned_segment.find_references_range(vram_range) {
if !owned_segment.is_vram_ignored(reference.vram()) {
symbols_info.insert(reference.vram());
}
if let Some(size) = reference.user_declared_size() {
let next_vram = reference.vram() + size;
if next_vram != vram_range.end() && !owned_segment.is_vram_ignored(next_vram) {
symbols_info.insert(next_vram);
auto_pads.insert(next_vram, reference.vram());
}
}
}
let symbols_info_vec: Vec<Vram> = symbols_info.into_iter().collect();
for (i, new_sym_vram) in symbols_info_vec.iter().enumerate() {
let new_sym_vram_end = if i + 1 < symbols_info_vec.len() {
symbols_info_vec[i + 1]
} else {
vram_range.end()
};
debug_assert!(
*new_sym_vram < new_sym_vram_end,
"{vram_range:?} {new_sym_vram} {new_sym_vram_end}"
);
symbol_vrams.insert(*new_sym_vram);
let properties = NobitsSymProperties {
parent_metadata: ParentSectionMetadata::new(
name.clone(),
vram_range.start(),
parent_segment_info.clone(),
),
compiler: settings.compiler,
auto_pad_by: auto_pads.get(new_sym_vram).copied(),
};
let sym = NobitsSym::new(
context,
AddressRange::new(*new_sym_vram, new_sym_vram_end),
parent_segment_info.clone(),
properties,
)?;
nobits_symbols.push(sym);
}
Ok(Self {
name,
vram_range,
parent_segment_info,
nobits_symbols,
symbol_vrams,
})
}
}
impl NobitsSection {
pub fn nobits_symbols(&self) -> &[NobitsSym] {
&self.nobits_symbols
}
}
impl NobitsSection {
pub fn post_process(
self,
context: &mut Context,
) -> Result<NobitsSectionProcessed, SectionPostProcessError> {
NobitsSectionProcessed::new(
context,
self.name,
self.vram_range,
self.parent_segment_info,
self.nobits_symbols,
self.symbol_vrams,
)
}
}
impl Section for NobitsSection {
fn name(&self) -> Arc<str> {
self.name.clone()
}
fn vram_range(&self) -> &AddressRange<Vram> {
&self.vram_range
}
fn parent_segment_info(&self) -> &ParentSegmentInfo {
&self.parent_segment_info
}
fn section_type(&self) -> SectionType {
SectionType::Bss
}
fn symbol_list(&self) -> &[impl Symbol] {
&self.nobits_symbols
}
fn symbols_vrams(&self) -> &UnorderedSet<Vram> {
&self.symbol_vrams
}
}
impl SectionPreprocessed for NobitsSection {
fn symbol_list(&self) -> &[impl SymbolPreprocessed] {
&self.nobits_symbols
}
}
impl hash::Hash for NobitsSection {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.parent_segment_info.hash(state);
self.vram_range.hash(state);
}
}
impl PartialEq for NobitsSection {
fn eq(&self, other: &Self) -> bool {
self.parent_segment_info == other.parent_segment_info && self.vram_range == other.vram_range
}
}
impl PartialOrd for NobitsSection {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
match self
.parent_segment_info
.partial_cmp(&other.parent_segment_info)
{
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.vram_range.partial_cmp(&other.vram_range)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "pyo3", pyclass(module = "spimdisasm"))]
pub struct NobitsSectionSettings {
compiler: Option<Compiler>,
}
impl NobitsSectionSettings {
pub fn new(compiler: Option<Compiler>) -> Self {
Self { compiler }
}
}
#[cfg(feature = "pyo3")]
pub(crate) mod python_bindings {
use super::*;
#[pymethods]
impl NobitsSectionSettings {
#[new]
#[pyo3(signature = (compiler))]
pub fn py_new(compiler: Option<Compiler>) -> Self {
Self::new(compiler)
}
}
}