use std::error::Error;
use std::fmt::{Display, Formatter};
use crate::util::Utf16String;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecoupledInjectedAttributeError {
NullBuffer,
NegativeArraySize(i32),
ArrayIndexOutOfBounds,
StringIndexOutOfBounds {
offset: i32,
length: i32,
buffer_length: usize,
},
}
impl DecoupledInjectedAttributeError {
#[must_use]
pub const fn class_name(&self) -> &'static str {
match self {
Self::NullBuffer => "java.lang.NullPointerException",
Self::NegativeArraySize(_) => "java.lang.NegativeArraySizeException",
Self::ArrayIndexOutOfBounds => "java.lang.ArrayIndexOutOfBoundsException",
Self::StringIndexOutOfBounds { .. } => "java.lang.StringIndexOutOfBoundsException",
}
}
}
impl Display for DecoupledInjectedAttributeError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::NullBuffer => formatter.write_str("null"),
Self::NegativeArraySize(length) => write!(formatter, "{length}"),
Self::ArrayIndexOutOfBounds => {
formatter.write_str("arraycopy: source index out of bounds")
}
Self::StringIndexOutOfBounds {
offset,
length,
buffer_length,
} => write!(
formatter,
"offset {offset}, count {length}, length {buffer_length}"
),
}
}
}
impl Error for DecoupledInjectedAttributeError {}
pub struct DecoupledInjectedAttribute {
buffer: Vec<u16>,
name_offset: i32,
name_len: i32,
operator_offset: i32,
operator_len: i32,
value_content_offset: i32,
value_content_len: i32,
value_outer_offset: i32,
value_outer_len: i32,
}
impl DecoupledInjectedAttribute {
#[allow(clippy::too_many_arguments)]
pub fn create_attribute(
buffer: Option<&[u16]>,
name_offset: i32,
name_len: i32,
operator_offset: i32,
operator_len: i32,
value_content_offset: i32,
value_content_len: i32,
value_outer_offset: i32,
value_outer_len: i32,
) -> Result<Self, DecoupledInjectedAttributeError> {
let new_length = name_len
.wrapping_add(operator_len)
.wrapping_add(value_outer_len);
if new_length < 0 {
return Err(DecoupledInjectedAttributeError::NegativeArraySize(
new_length,
));
}
let buffer = buffer.ok_or(DecoupledInjectedAttributeError::NullBuffer)?;
let mut new_buffer = vec![0; new_length as usize];
copy_java_range(buffer, name_offset, name_len, &mut new_buffer, 0)?;
copy_java_range(
buffer,
operator_offset,
operator_len,
&mut new_buffer,
name_len,
)?;
copy_java_range(
buffer,
value_outer_offset,
value_outer_len,
&mut new_buffer,
name_len.wrapping_add(operator_len),
)?;
Ok(Self {
buffer: new_buffer,
name_offset: 0,
name_len,
operator_offset: operator_offset.wrapping_sub(name_offset),
operator_len,
value_content_offset: value_content_offset.wrapping_sub(name_offset),
value_content_len,
value_outer_offset: value_outer_offset.wrapping_sub(name_offset),
value_outer_len,
})
}
pub fn get_name(&self) -> Result<Utf16String, DecoupledInjectedAttributeError> {
self.slice(self.name_offset, self.name_len)
}
pub fn get_operator(&self) -> Result<Utf16String, DecoupledInjectedAttributeError> {
self.slice(self.operator_offset, self.operator_len)
}
pub fn get_value_content(&self) -> Result<Utf16String, DecoupledInjectedAttributeError> {
self.slice(self.value_content_offset, self.value_content_len)
}
pub fn get_value_outer(&self) -> Result<Utf16String, DecoupledInjectedAttributeError> {
self.slice(self.value_outer_offset, self.value_outer_len)
}
#[must_use]
pub fn to_utf16_string(&self) -> Utf16String {
Utf16String::from_utf16(self.buffer.clone())
}
#[allow(clippy::type_complexity)]
pub(crate) fn parser_parts(&self) -> (&[u16], i32, i32, i32, i32, i32, i32, i32, i32) {
(
&self.buffer,
self.name_offset,
self.name_len,
self.operator_offset,
self.operator_len,
self.value_content_offset,
self.value_content_len,
self.value_outer_offset,
self.value_outer_len,
)
}
fn slice(&self, offset: i32, len: i32) -> Result<Utf16String, DecoupledInjectedAttributeError> {
let range_is_invalid = offset < 0
|| len < 0
|| usize::try_from(offset)
.ok()
.zip(usize::try_from(len).ok())
.is_none_or(|(start, length)| {
start > self.buffer.len().saturating_sub(length) || length > self.buffer.len()
});
if range_is_invalid {
return Err(DecoupledInjectedAttributeError::StringIndexOutOfBounds {
offset,
length: len,
buffer_length: self.buffer.len(),
});
}
let start = offset as usize;
let end = start + len as usize;
Ok(Utf16String::from_utf16(self.buffer[start..end].to_vec()))
}
}
fn copy_java_range(
source: &[u16],
source_offset: i32,
length: i32,
target: &mut [u16],
target_offset: i32,
) -> Result<(), DecoupledInjectedAttributeError> {
let source_end = source_offset.wrapping_add(length);
let target_end = target_offset.wrapping_add(length);
if source_offset < 0
|| target_offset < 0
|| length < 0
|| source_end < 0
|| target_end < 0
|| source_end as usize > source.len()
|| target_end as usize > target.len()
{
return Err(DecoupledInjectedAttributeError::ArrayIndexOutOfBounds);
}
target[target_offset as usize..target_end as usize]
.copy_from_slice(&source[source_offset as usize..source_end as usize]);
Ok(())
}