use super::table::{HasKind, Kind};
use bitvec::access::BitSafeU8;
use bitvec::slice::BitSlice;
use num_traits::{FromPrimitive, ToPrimitive};
use scroll::{
ctx::{TryFromCtx, TryIntoCtx},
Pread, Pwrite,
};
use std::{cmp::Ordering, collections::HashMap, marker::PhantomData};
use dotnetdll_macros::coded_index;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TokenTarget {
Table(Kind),
UserString,
}
#[derive(Clone, Copy, Debug)]
pub struct Token {
pub target: TokenTarget,
pub index: usize,
}
impl TryFromCtx<'_> for Token {
type Error = scroll::Error;
fn try_from_ctx(from: &[u8], (): ()) -> Result<(Self, usize), Self::Error> {
let offset = &mut 0;
let num: u32 = from.gread_with(offset, scroll::LE)?;
let tag = (num >> 24) as u8;
let index = (num & 0x00FF_FFFF) as usize;
Ok((
Token {
target: if tag == 0x70 {
TokenTarget::UserString
} else {
TokenTarget::Table(Kind::from_u8(tag).unwrap())
},
index,
},
*offset,
))
}
}
try_into_ctx!(Token, |self, into| {
let offset = &mut 0;
let tag = match self.target {
TokenTarget::Table(k) => k.to_u8().unwrap(),
TokenTarget::UserString => 0x70,
};
into.gwrite_with(((tag as u32) << 24) | (self.index as u32), offset, scroll::LE)?;
Ok(*offset)
});
#[derive(Clone, Copy, Debug)]
pub struct Sizes<'a> {
pub heap: &'a BitSlice<BitSafeU8>,
pub tables: &'a HashMap<Kind, u32>,
}
macro_rules! uint_impl {
($ty:ty) => {
impl<'a> TryFromCtx<'a, Sizes<'a>> for $ty {
type Error = scroll::Error;
fn try_from_ctx(from: &'a [u8], _: Sizes<'a>) -> Result<(Self, usize), Self::Error> {
TryFromCtx::try_from_ctx(from, scroll::LE)
}
}
impl<'a> TryIntoCtx<Sizes<'a>> for $ty {
type Error = scroll::Error;
fn try_into_ctx(self, into: &mut [u8], _: Sizes<'a>) -> Result<usize, Self::Error> {
self.try_into_ctx(into, scroll::LE)
}
}
};
}
uint_impl!(u8);
uint_impl!(u16);
uint_impl!(u32);
uint_impl!(u64);
macro_rules! heap_index {
($name:ident, $idx:literal) => {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct $name(pub usize);
impl<'a> TryFromCtx<'a, Sizes<'a>> for $name {
type Error = scroll::Error;
fn try_from_ctx(from: &'a [u8], sizes: Sizes<'a>) -> Result<(Self, usize), Self::Error> {
let offset = &mut 0;
let idx = if sizes.heap[$idx] {
from.gread_with::<u32>(offset, scroll::LE)? as usize
} else {
from.gread_with::<u16>(offset, scroll::LE)? as usize
};
Ok(($name(idx), *offset))
}
}
impl<'a> TryIntoCtx<Sizes<'a>> for $name {
type Error = scroll::Error;
fn try_into_ctx(self, into: &mut [u8], sizes: Sizes<'a>) -> Result<usize, Self::Error> {
let offset = &mut 0;
if sizes.heap[$idx] {
into.gwrite_with(self.0 as u32, offset, scroll::LE)?;
} else {
into.gwrite_with(self.0 as u16, offset, scroll::LE)?;
}
Ok(*offset)
}
}
impl From<usize> for $name {
fn from(i: usize) -> Self {
$name(i)
}
}
impl $name {
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
};
}
heap_index!(String, 0);
heap_index!(GUID, 1);
heap_index!(Blob, 2);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Simple<T>(pub usize, PhantomData<T>);
impl<'a, T: 'a + HasKind> TryFromCtx<'a, Sizes<'a>> for Simple<T> {
type Error = scroll::Error;
fn try_from_ctx(from: &'a [u8], sizes: Sizes<'a>) -> Result<(Self, usize), Self::Error> {
let offset = &mut 0;
let idx = if *sizes.tables.get(&T::kind()).unwrap_or(&0) < (1 << 16) {
from.gread_with::<u16>(offset, scroll::LE)? as usize
} else {
from.gread_with::<u32>(offset, scroll::LE)? as usize
};
Ok((Simple(idx, PhantomData), *offset))
}
}
impl<'a, T: 'a + HasKind> TryIntoCtx<Sizes<'a>> for Simple<T> {
type Error = scroll::Error;
fn try_into_ctx(self, into: &mut [u8], sizes: Sizes<'a>) -> Result<usize, Self::Error> {
let offset = &mut 0;
if *sizes.tables.get(&T::kind()).unwrap_or(&0) < (1 << 16) {
into.gwrite_with(self.0 as u16, offset, scroll::LE)?;
} else {
into.gwrite_with(self.0 as u32, offset, scroll::LE)?;
}
Ok(*offset)
}
}
impl<T: Eq> PartialOrd for Simple<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: Eq> Ord for Simple<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T> From<usize> for Simple<T> {
fn from(val: usize) -> Self {
Simple(val, PhantomData)
}
}
impl<T> Simple<T> {
pub fn is_null(&self) -> bool {
self.0 == 0
}
}
coded_index!(TypeDefOrRef, {
TypeDef,
TypeRef,
TypeSpec
});
coded_index!(HasConstant, {
Field,
Param,
Property
});
coded_index!(HasCustomAttribute, {
MethodDef,
Field,
TypeRef,
TypeDef,
Param,
InterfaceImpl,
MemberRef,
Module,
DeclSecurity,
Property,
Event,
StandAloneSig,
ModuleRef,
TypeSpec,
Assembly,
AssemblyRef,
File,
ExportedType,
ManifestResource,
GenericParam,
GenericParamConstraint,
MethodSpec
});
coded_index!(HasFieldMarshal, {
Field,
Param
});
coded_index!(HasDeclSecurity, {
TypeDef,
MethodDef,
Assembly
});
coded_index!(MemberRefParent, {
TypeDef,
TypeRef,
ModuleRef,
MethodDef,
TypeSpec
});
coded_index!(HasSemantics, {
Event,
Property
});
coded_index!(MethodDefOrRef, {
MethodDef,
MemberRef
});
coded_index!(MemberForwarded, {
Field,
MethodDef
});
coded_index!(Implementation, {
File,
AssemblyRef,
ExportedType
});
coded_index!(CustomAttributeType, {
Unused,
Unused,
MethodDef,
MemberRef,
Unused
});
coded_index!(ResolutionScope, {
Module,
ModuleRef,
AssemblyRef,
TypeRef
});
coded_index!(TypeOrMethodDef, {
TypeDef,
MethodDef
});