use crate::ir::Constant;
use cranelift_entity::EntityRef;
use std::collections::{BTreeMap, HashMap};
use std::vec::Vec;
pub type ConstantData = Vec<u8>;
pub type ConstantOffset = u32;
#[derive(Clone)]
pub struct ConstantPoolEntry {
data: ConstantData,
offset: Option<ConstantOffset>,
}
impl ConstantPoolEntry {
fn new(data: ConstantData) -> Self {
Self { data, offset: None }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn set_offset(&mut self, offset: ConstantOffset) {
self.offset = Some(offset)
}
}
#[derive(Clone)]
pub struct ConstantPool {
handles_to_values: BTreeMap<Constant, ConstantPoolEntry>,
values_to_handles: HashMap<ConstantData, Constant>,
}
impl ConstantPool {
pub fn new() -> Self {
Self {
handles_to_values: BTreeMap::new(),
values_to_handles: HashMap::new(),
}
}
pub fn clear(&mut self) {
self.handles_to_values.clear();
self.values_to_handles.clear();
}
pub fn insert(&mut self, constant_value: ConstantData) -> Constant {
if self.values_to_handles.contains_key(&constant_value) {
self.values_to_handles.get(&constant_value).unwrap().clone()
} else {
let constant_handle = Constant::new(self.len());
self.values_to_handles
.insert(constant_value.clone(), constant_handle.clone());
self.handles_to_values.insert(
constant_handle.clone(),
ConstantPoolEntry::new(constant_value),
);
constant_handle
}
}
pub fn get(&self, constant_handle: Constant) -> &ConstantData {
assert!(self.handles_to_values.contains_key(&constant_handle));
&self.handles_to_values.get(&constant_handle).unwrap().data
}
pub fn set_offset(&mut self, constant_handle: Constant, constant_offset: ConstantOffset) {
assert!(
self.handles_to_values.contains_key(&constant_handle),
"A constant handle must have already been inserted into the pool; perhaps a \
constant pool was created outside of the pool?"
);
self.handles_to_values
.entry(constant_handle)
.and_modify(|e| e.offset = Some(constant_offset));
}
pub fn get_offset(&self, constant_handle: Constant) -> ConstantOffset {
self.handles_to_values
.get(&constant_handle)
.expect(
"A constant handle must have a corresponding constant value; was a constant \
handle created outside of the pool?",
)
.offset
.expect(
"A constant offset has not yet been set; verify that `set_offset` has been \
called before this point",
)
}
pub fn iter(&self) -> impl Iterator<Item = (&Constant, &ConstantData)> {
self.handles_to_values.iter().map(|(h, e)| (h, &e.data))
}
pub fn entries_mut(&mut self) -> impl Iterator<Item = &mut ConstantPoolEntry> {
self.handles_to_values.values_mut()
}
pub fn len(&self) -> usize {
self.handles_to_values.len()
}
pub fn byte_size(&self) -> usize {
self.values_to_handles.keys().map(|c| c.len()).sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let sut = ConstantPool::new();
assert_eq!(sut.len(), 0);
}
#[test]
fn insert() {
let mut sut = ConstantPool::new();
sut.insert(vec![1, 2, 3]);
sut.insert(vec![4, 5, 6]);
assert_eq!(sut.len(), 2);
}
#[test]
fn insert_duplicate() {
let mut sut = ConstantPool::new();
let a = sut.insert(vec![1, 2, 3]);
sut.insert(vec![4, 5, 6]);
let b = sut.insert(vec![1, 2, 3]);
assert_eq!(a, b);
}
#[test]
fn clear() {
let mut sut = ConstantPool::new();
sut.insert(vec![1, 2, 3]);
assert_eq!(sut.len(), 1);
sut.clear();
assert_eq!(sut.len(), 0);
}
#[test]
fn iteration_order() {
let mut sut = ConstantPool::new();
sut.insert(vec![1, 2, 3]);
sut.insert(vec![4, 5, 6]);
sut.insert(vec![1, 2, 3]);
let data = sut.iter().map(|(_, v)| v).collect::<Vec<&ConstantData>>();
assert_eq!(data, vec![&vec![1, 2, 3], &vec![4, 5, 6]]);
}
#[test]
fn get() {
let mut sut = ConstantPool::new();
let data = vec![1, 2, 3];
let handle = sut.insert(data.clone());
assert_eq!(sut.get(handle), &data);
}
#[test]
#[should_panic]
fn get_nonexistent_constant() {
let sut = ConstantPool::new();
let a = Constant::with_number(42).unwrap();
sut.get(a); }
#[test]
fn get_offset() {
let mut sut = ConstantPool::new();
let a = sut.insert(vec![1]);
sut.set_offset(a, 42);
assert_eq!(sut.get_offset(a), 42)
}
#[test]
#[should_panic]
fn get_nonexistent_offset() {
let mut sut = ConstantPool::new();
let a = sut.insert(vec![1]);
sut.get_offset(a); }
}