use std::hash::{Hash, Hasher};
use std::collections::HashMap;
use std::cmp::{Eq, PartialEq};
use std::rc::Rc;
use std::slice::Iter;
use result::*;
#[derive(Debug, Clone)]
pub enum Item {
UTF8(String),
Integer(i32),
Float(f32),
Long(i64),
Double(f64),
Class(u16),
String(u16),
FieldRef {
class: u16,
name_and_type: u16,
},
MethodRef {
class: u16,
name_and_type: u16,
},
InterfaceMethodRef {
class: u16,
name_and_type: u16,
},
NameAndType {
name: u16,
desc: u16,
},
MethodHandle {
kind: ReferenceKind,
index: u16,
},
MethodType(u16),
InvokeDynamic {
bootstrap_method: u16,
name_and_type: u16,
},
Module(u16),
Package(u16),
}
impl Item {
fn is_double(&self) -> bool {
match *self {
Item::Long(_) | Item::Double(_) => true,
_ => false,
}
}
}
impl Hash for Item {
fn hash<H: Hasher>(&self, state: &mut H) {
match *self {
Item::UTF8(ref s) => {
state.write_u8(1);
s.hash(state);
}
Item::Integer(i) => {
state.write_u8(3);
i.hash(state);
}
Item::Float(f) => {
state.write_u8(4);
f.to_bits().hash(state);
}
Item::Long(i) => {
state.write_u8(5);
i.hash(state);
}
Item::Double(f) => {
state.write_u8(6);
f.to_bits().hash(state);
}
Item::Class(ptr) => {
state.write_u8(7);
ptr.hash(state);
}
Item::String(ptr) => {
state.write_u8(8);
ptr.hash(state);
}
Item::FieldRef {
class,
name_and_type,
} => {
state.write_u8(9);
class.hash(state);
name_and_type.hash(state);
}
Item::MethodRef {
class,
name_and_type,
} => {
state.write_u8(10);
class.hash(state);
name_and_type.hash(state);
}
Item::InterfaceMethodRef {
class,
name_and_type,
} => {
state.write_u8(11);
class.hash(state);
name_and_type.hash(state);
}
Item::NameAndType { name, desc } => {
state.write_u8(12);
name.hash(state);
desc.hash(state);
}
Item::MethodHandle { ref kind, index } => {
state.write_u8(15);
kind.hash(state);
index.hash(state);
}
Item::MethodType(ptr) => {
state.write_u8(16);
ptr.hash(state);
}
Item::InvokeDynamic {
bootstrap_method,
name_and_type,
} => {
state.write_u8(18);
bootstrap_method.hash(state);
name_and_type.hash(state);
}
Item::Module(ptr) => {
state.write_u8(19);
ptr.hash(state);
}
Item::Package(ptr) => {
state.write_u8(20);
ptr.hash(state);
}
}
}
}
impl PartialEq for Item {
fn eq(&self, other: &Item) -> bool {
match (self, other) {
(&Item::UTF8(ref str1), &Item::UTF8(ref str2)) => *str1 == *str2,
(&Item::Integer(i1), &Item::Integer(i2)) => i1 == i2,
(&Item::Float(f1), &Item::Float(f2)) => f1.to_bits() == f2.to_bits(),
(&Item::Long(i1), &Item::Long(i2)) => i1 == i2,
(&Item::Double(f1), &Item::Double(f2)) => f1.to_bits() == f2.to_bits(),
(&Item::Class(i1), &Item::Class(i2)) | (&Item::String(i1), &Item::String(i2)) => {
i1 == i2
}
(
&Item::FieldRef {
class: class1,
name_and_type: nat1,
},
&Item::FieldRef {
class: class2,
name_and_type: nat2,
},
)
| (
&Item::MethodRef {
class: class1,
name_and_type: nat1,
},
&Item::MethodRef {
class: class2,
name_and_type: nat2,
},
)
| (
&Item::InterfaceMethodRef {
class: class1,
name_and_type: nat1,
},
&Item::InterfaceMethodRef {
class: class2,
name_and_type: nat2,
},
) => class1 == class2 && nat1 == nat2,
(
&Item::NameAndType {
name: name1,
desc: desc1,
},
&Item::NameAndType {
name: name2,
desc: desc2,
},
) => name1 == name2 && desc1 == desc2,
(
&Item::MethodHandle {
kind: ref kind1,
index: index1,
},
&Item::MethodHandle {
kind: ref kind2,
index: index2,
},
) => kind1 == kind2 && index1 == index2,
(
&Item::InvokeDynamic {
bootstrap_method: bma1,
name_and_type: nat1,
},
&Item::InvokeDynamic {
bootstrap_method: bma2,
name_and_type: nat2,
},
) => bma1 == bma2 && nat1 == nat2,
(&Item::Package(index1), &Item::Package(index2))
| (&Item::Module(index1), &Item::Module(index2))
| (&Item::MethodType(index1), &Item::MethodType(index2)) => index1 == index2,
_ => false,
}
}
}
impl Eq for Item {}
#[derive(Eq, PartialEq, Hash, Debug, Clone)]
pub enum ReferenceKind {
GetField,
GetStatic,
PutField,
PutStatic,
InvokeVirtual,
InvokeStatic,
InvokeSpecial,
NewInvokeSpecial,
InvokeInterface,
}
#[derive(Default)]
pub struct Pool {
length: u16,
by_index: Vec<Option<Rc<Item>>>,
by_entry: HashMap<Rc<Item>, u16>,
}
impl Pool {
pub fn new() -> Self {
Pool {
length: 1,
by_index: Vec::new(),
by_entry: HashMap::new(),
}
}
pub fn with_capacity(cap: u16) -> Self {
Pool {
length: 1,
by_index: Vec::with_capacity(cap as usize),
by_entry: HashMap::with_capacity(cap as usize),
}
}
#[inline]
pub fn len(&self) -> u16 {
self.length
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 1
}
pub fn get(&self, index: u16) -> Result<&Item> {
if index != 0 && index <= self.len() {
if let Some(ref item) = self.by_index[index as usize - 1] {
return Ok(item);
}
}
Err(Error::InvalidCPItem(index))
}
pub fn get_utf8(&self, index: u16) -> Result<String> {
if let Item::UTF8(ref s) = *self.get(index)? {
Ok(s.clone())
} else {
Err(Error::InvalidCPItem(index))
}
}
pub fn get_class_name_opt(&self, index: u16) -> Result<Option<String>> {
if let Item::Class(utf_index) = *self.get(index)? {
if utf_index == 0 {
Ok(None)
} else {
Ok(Some(self.get_utf8(utf_index)?))
}
} else {
Err(Error::InvalidCPItem(index))
}
}
pub fn get_class_name(&self, index: u16) -> Result<String> {
if let Item::Class(utf_index) = *self.get(index)? {
self.get_utf8(utf_index)
} else {
Err(Error::InvalidCPItem(index))
}
}
pub fn push(&mut self, item: Item) -> Result<u16> {
if self.len() == u16::max_value() {
return Err(Error::CPTooLarge);
}
let double = item.is_double();
let length = &mut self.length;
let rc_item = Rc::new(item);
let rc_item1 = Rc::clone(&rc_item);
let by_index = &mut self.by_index;
Ok(*self.by_entry.entry(rc_item).or_insert_with(move || {
by_index.push(Some(Rc::clone(&rc_item1)));
let prev_length = *length;
if double {
by_index.push(None);
*length += 2;
} else {
*length += 1;
}
prev_length
}))
}
pub fn push_duplicate(&mut self, item: Item) -> Result<u16> {
if self.len() == u16::max_value() {
return Err(Error::CPTooLarge);
}
let double = item.is_double();
let length = self.length;
let rc_item = Rc::new(item);
self.by_index.push(Some(Rc::clone(&rc_item)));
if double {
self.by_index.push(None);
self.length += 2;
} else {
self.length += 1;
}
self.by_entry.insert(rc_item, length);
Ok(length)
}
pub fn push_utf8(&mut self, content: String) -> Result<u16> {
self.push(Item::UTF8(content))
}
pub fn push_class(&mut self, name: String) -> Result<u16> {
let name_index = self.push_utf8(name)?;
self.push(Item::Class(name_index))
}
pub fn iter(&self) -> PoolIter {
PoolIter {
iter: self.by_index.iter(),
index: 0,
}
}
}
pub struct PoolIter<'a> {
iter: Iter<'a, Option<Rc<Item>>>,
index: u16,
}
impl<'a> Iterator for PoolIter<'a> {
type Item = (u16, &'a Item);
fn next(&mut self) -> Option<Self::Item> {
self.index += 1;
if let Some(rc_item) = self.iter.next() {
if let Some(ref item) = *rc_item {
Some((self.index, item))
} else {
self.next()
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_and_get() {
let mut pool = Pool::new();
assert_eq!(pool.push(Item::Integer(123)).unwrap(), 1);
assert_eq!(pool.push(Item::Long(32767)).unwrap(), 2);
assert_eq!(pool.push(Item::Long(65535)).unwrap(), 4);
assert_eq!(pool.push(Item::Float(3.8)).unwrap(), 6);
assert_eq!(pool.len(), 7);
assert_eq!(pool.push(Item::Integer(123)).unwrap(), 1);
assert_eq!(pool.len(), 7);
assert_eq!(pool.get(1).unwrap(), &Item::Integer(123));
assert_eq!(pool.get(2).unwrap(), &Item::Long(32767));
assert_eq!(pool.get(4).unwrap(), &Item::Long(65535));
assert_eq!(pool.get(6).unwrap(), &Item::Float(3.8));
let mut iter = pool.iter();
assert_eq!(iter.next(), Some((1, &Item::Integer(123))));
assert_eq!(iter.next(), Some((2, &Item::Long(32767))));
assert_eq!(iter.next(), Some((4, &Item::Long(65535))));
assert_eq!(iter.next(), Some((6, &Item::Float(3.8))));
assert_eq!(iter.next(), None);
}
#[test]
fn push_duplicate() {
let mut pool = Pool::new();
assert_eq!(pool.push_duplicate(Item::Integer(123)).unwrap(), 1);
assert_eq!(pool.push_duplicate(Item::Long(32767)).unwrap(), 2);
assert_eq!(pool.push_duplicate(Item::Long(65535)).unwrap(), 4);
assert_eq!(pool.push_duplicate(Item::Float(3.8)).unwrap(), 6);
assert_eq!(pool.len(), 7);
assert_eq!(pool.push_duplicate(Item::Integer(123)).unwrap(), 7);
assert_eq!(pool.len(), 8);
assert_eq!(pool.get(1).unwrap(), &Item::Integer(123));
assert_eq!(pool.get(2).unwrap(), &Item::Long(32767));
assert_eq!(pool.get(4).unwrap(), &Item::Long(65535));
assert_eq!(pool.get(6).unwrap(), &Item::Float(3.8));
assert_eq!(pool.get(7).unwrap(), &Item::Integer(123));
}
}