use std::convert::TryInto;
use llvm_support::StrtabRef;
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
use thiserror::Error;
use crate::block::strtab::StrtabError;
use crate::map::{MapError, PartialCtxMappable, PartialMapCtx};
use crate::unroll::UnrolledRecord;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ComdatError {
#[error("unsupported COMDAT record format (v1)")]
V1Unsupported,
#[error("COMDAT record doesn't have enough fields ({0} < 3)")]
TooShort(usize),
#[error("error while accessing COMDAT name: {0}")]
Name(#[from] StrtabError),
#[error("unknown or invalid COMDAT selection kind: {0}")]
SelectionKind(#[from] TryFromPrimitiveError<SelectionKind>),
#[error("mapping error in comdat list")]
Map(#[from] MapError),
}
#[non_exhaustive]
#[derive(Debug, TryFromPrimitive)]
#[repr(u64)]
pub enum SelectionKind {
Any,
ExactMatch,
Largest,
NoDeduplicate,
SameSize,
}
#[non_exhaustive]
#[derive(Debug)]
pub struct Comdat {
pub selection_kind: SelectionKind,
pub name: String,
}
impl PartialCtxMappable<UnrolledRecord> for Comdat {
type Error = ComdatError;
fn try_map(record: &UnrolledRecord, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
if !ctx.use_strtab().map_err(MapError::Context)? {
return Err(ComdatError::V1Unsupported);
}
if record.fields().len() != 3 {
return Err(ComdatError::TooShort(record.fields().len()));
}
let name = {
let sref: StrtabRef = (record.fields()[0], record.fields()[1]).into();
ctx.strtab.try_get(&sref)?.into()
};
let selection_kind: SelectionKind = record.fields()[2].try_into()?;
Ok(Self {
selection_kind,
name: name,
})
}
}