<commit_before>use std::collections::BTreeMap;

use super::common::Value;
use super::network::Network;

#[derive(RustcDecodable)]
pub struct Conf {
    pub group_size: usize,
    pub threshold: Value,
}

pub trait Apply {
    fn apply<'r>(&'r self, conf: &'r Conf) -> Application<'r>;
}

impl<'network> Apply for Network<'network> {
    fn apply<'r>(&'r self, conf: &'r Conf) -> Application<'r> {
        Application::new(self, conf)
    }
}

struct ValuesGroup {
    pub sum: Value,
    pub node: usize,
}

pub struct Application<'r> {
    conf: &'r Conf,
    network: &'r Network<'r>,
}

impl<'r> Application<'r> {
    pub fn new(network: &'r Network<'r>, conf: &'r Conf) -> Self {
        Application {network: network, conf: conf}
    }

    pub fn perform(&self, values: &[Value]) -> Vec<Value> {
        assert!(values.len() >= self.network.inputs.len());
        let group_size = self.conf.group_size as Value;
        let mut result: BTreeMap<usize, Value> = self.network.outputs.iter()
            .map(|&x| (x, 0.0)).collect::<_>();
        {
            let groups = self.network.inputs.iter().zip(values)
                .map(|(&node, value)| {
                    self.perform_one(ValuesGroup {sum: value * group_size, node: node})
                })
                .flat_map(|x| x.into_iter());
            for group in groups {
                *result.get_mut(&group.node).unwrap() += group.sum;
            }
        }
        result.values()
            .map(|x| x / group_size as Value)
            .collect::<Vec<Value>>()
    }

    fn perform_one(&self, group: ValuesGroup) -> Box<Iterator<Item=ValuesGroup>> {
        if self.network.outputs.contains(&group.node) {
            Box::new(vec![group].into_iter())
        } else if group.sum > self.conf.threshold {
            let row = self.network.weights.row(group.node);
            let nodes = row.iter()
                .enumerate()
                .filter(|&(_, &w)| w.abs() > 0.0)
                .map(|(n, &w)| (n, w))
                .collect::<Vec<(usize, Value)>>();
            let new_sum = group.sum / nodes.len() as Value;
            Box::new(nodes.iter()
                .map(|&(node, weight)| {
                    self.perform_one(ValuesGroup {sum: new_sum * weight, node: node})
                })
                .flat_map(|x| x.into_iter())
                .collect::<Vec<_>>()
                .into_iter())
        } else {
            Box::new(vec![].into_iter())
        }
    }
}

#[test]
fn test_apply_network_contains_one_arc_with_positive_weight_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weight = 0.4;
    let weights_values = [
        0.0, weight,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * weight]);
}

#[test]
fn test_apply_network_contains_one_arc_with_negative_weight_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weight = -0.4;
    let weights_values = [
        0.0, weight,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * weight]);
}

#[test]
fn test_apply_network_with_two_arcs_and_two_inputs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w13 = 0.4;
    let w23 = 0.2;
    let weights_values = [
        0.0, 0.0, w13,
        0.0, 0.0, w23,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0, 1].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let i1 = 0.6;
    let i2 = 0.7;
    assert_eq!(&network.apply(&conf).perform(&[i1, i2])[..], &[i1 * w13 + i2 * w23]);
}

#[test]
fn test_apply_network_with_two_arcs_and_two_outputs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w12 = 0.4;
    let w13 = 0.2;
    let weights_values = [
        0.0, w12, w13,
        0.0, 0.0, 0.0,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1, 2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..],
               &[input * w12 / 2.0, input * w13 / 2.0]);
}

#[test]
fn test_apply_network_with_two_arcs_and_one_middle_node_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w12 = 0.4;
    let w23 = 0.2;
    let weights_values = [
        0.0, w12, 0.0,
        0.0, 0.0, w23,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * w12 * w23]);
}

#[test]
fn test_apply_network_with_two_arcs_and_self_add_arced_input_node_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w11 = 0.2;
    let w12 = 0.4;
    let weights_values = [
        w11, w12,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1000, threshold: 1e-8};
    let input = 0.6;
    let actual = network.apply(&conf).perform(&[input]);
    let expected = [input * w12 * (1.0 + w11 / (2.0 - w11)) / 2.0];
    assert_eq!(actual.len(), expected.len());
    assert!((actual[0] - expected[0]).abs() <= 1e-8);
}

#[test]
fn test_apply_network_without_arcs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weights_values = [
        0.0, 0.0,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1000, threshold: 1e-8};
    assert_eq!(&network.apply(&conf).perform(&[1.0]), &[0.0]);
}
<commit_msg>Return vec instead of iterator in box<commit_after>use std::collections::BTreeMap;

use super::common::Value;
use super::network::Network;

#[derive(RustcDecodable)]
pub struct Conf {
    pub group_size: usize,
    pub threshold: Value,
}

pub trait Apply {
    fn apply<'r>(&'r self, conf: &'r Conf) -> Application<'r>;
}

impl<'network> Apply for Network<'network> {
    fn apply<'r>(&'r self, conf: &'r Conf) -> Application<'r> {
        Application::new(self, conf)
    }
}

struct ValuesGroup {
    pub sum: Value,
    pub node: usize,
}

pub struct Application<'r> {
    conf: &'r Conf,
    network: &'r Network<'r>,
}

impl<'r> Application<'r> {
    pub fn new(network: &'r Network<'r>, conf: &'r Conf) -> Self {
        Application {network: network, conf: conf}
    }

    pub fn perform(&self, values: &[Value]) -> Vec<Value> {
        assert!(values.len() >= self.network.inputs.len());
        let group_size = self.conf.group_size as Value;
        let mut result: BTreeMap<usize, Value> = self.network.outputs.iter()
            .map(|&x| (x, 0.0)).collect::<_>();
        {
            let groups = self.network.inputs.iter().zip(values)
                .map(|(&node, value)| {
                    self.perform_one(ValuesGroup {sum: value * group_size, node: node})
                })
                .flat_map(|x| x.into_iter());
            for group in groups {
                *result.get_mut(&group.node).unwrap() += group.sum;
            }
        }
        result.values()
            .map(|x| x / group_size as Value)
            .collect::<Vec<Value>>()
    }

    fn perform_one(&self, group: ValuesGroup) -> Vec<ValuesGroup> {
        if self.network.outputs.contains(&group.node) {
            vec![group]
        } else if group.sum > self.conf.threshold {
            let row = self.network.weights.row(group.node);
            let nodes = row.iter()
                .enumerate()
                .filter(|&(_, &w)| w.abs() > 0.0)
                .map(|(n, &w)| (n, w))
                .collect::<Vec<(usize, Value)>>();
            let new_sum = group.sum / nodes.len() as Value;
            nodes.iter()
                .map(|&(node, weight)| {
                    self.perform_one(ValuesGroup {sum: new_sum * weight, node: node})
                })
                .flat_map(|x| x.into_iter())
                .collect::<Vec<_>>()
        } else {
            vec![]
        }
    }
}

#[test]
fn test_apply_network_contains_one_arc_with_positive_weight_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weight = 0.4;
    let weights_values = [
        0.0, weight,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * weight]);
}

#[test]
fn test_apply_network_contains_one_arc_with_negative_weight_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weight = -0.4;
    let weights_values = [
        0.0, weight,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * weight]);
}

#[test]
fn test_apply_network_with_two_arcs_and_two_inputs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w13 = 0.4;
    let w23 = 0.2;
    let weights_values = [
        0.0, 0.0, w13,
        0.0, 0.0, w23,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0, 1].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let i1 = 0.6;
    let i2 = 0.7;
    assert_eq!(&network.apply(&conf).perform(&[i1, i2])[..], &[i1 * w13 + i2 * w23]);
}

#[test]
fn test_apply_network_with_two_arcs_and_two_outputs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w12 = 0.4;
    let w13 = 0.2;
    let weights_values = [
        0.0, w12, w13,
        0.0, 0.0, 0.0,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1, 2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..],
               &[input * w12 / 2.0, input * w13 / 2.0]);
}

#[test]
fn test_apply_network_with_two_arcs_and_one_middle_node_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w12 = 0.4;
    let w23 = 0.2;
    let weights_values = [
        0.0, w12, 0.0,
        0.0, 0.0, w23,
        0.0, 0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [2].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(3, &weights_values);
    let nodes = (0..3).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1, threshold: 1e-3};
    let input = 0.6;
    assert_eq!(&network.apply(&conf).perform(&[input])[..], &[input * w12 * w23]);
}

#[test]
fn test_apply_network_with_two_arcs_and_self_add_arced_input_node_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let w11 = 0.2;
    let w12 = 0.4;
    let weights_values = [
        w11, w12,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1000, threshold: 1e-8};
    let input = 0.6;
    let actual = network.apply(&conf).perform(&[input]);
    let expected = [input * w12 * (1.0 + w11 / (2.0 - w11)) / 2.0];
    assert_eq!(actual.len(), expected.len());
    assert!((actual[0] - expected[0]).abs() <= 1e-8);
}

#[test]
fn test_apply_network_without_arcs_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use super::common::Node;
    use super::matrix::Matrix;
    let weights_values = [
        0.0, 0.0,
        0.0, 0.0,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    let weights = Matrix::new(2, &weights_values);
    let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
    let network = Network {inputs: &inputs, outputs: &outputs, weights: weights, nodes: &nodes};
    let conf = Conf {group_size: 1000, threshold: 1e-8};
    assert_eq!(&network.apply(&conf).perform(&[1.0]), &[0.0]);
}
<|endoftext|>


<commit_before>extern crate bobyqa;

use self::bobyqa::Bobyqa;

use super::common::Value;
use super::error::Error;
use super::error;
use super::matrix::Matrix;
use super::network::{Network, NetworkMut};

pub struct Conf<'r> {
    pub error_conf: &'r error::Conf<'r>,
}

pub trait Train {
    fn train<'r>(&mut self, conf: &Conf) -> Value;
}

impl<'network> Train for NetworkMut<'network> {
    fn train<'r>(&mut self, conf: &Conf) -> Value {
        use std::iter::repeat;
        let nodes_count = self.weights.column_len();
        let inputs = &self.inputs;
        let outputs = &self.outputs;
        let nodes = &self.nodes;
        let error_function = |weights_values: &[Value]| {
            assert!(weights_values.len() >= nodes_count * nodes_count);
            let weights = Matrix::new(nodes_count, weights_values);
            let network = Network {inputs: inputs, outputs: outputs,
                                   weights: weights, nodes: nodes};
            network.error(conf.error_conf)
        };
        let variables_count = self.weights.values().len();
        let lower_bound = repeat(0.0).take(variables_count).collect::<Vec<_>>();
        let upper_bound = repeat(1.0).take(variables_count).collect::<Vec<_>>();
        Bobyqa::new()
            .variables_count(variables_count)
//            .number_of_interpolation_conditions((variables_count + 1)*(variables_count + 2)/2)
            .number_of_interpolation_conditions(variables_count + 2)
            .lower_bound(&lower_bound[..])
            .upper_bound(&upper_bound[..])
            .perform(self.weights.values(), &error_function)
    }
}
<commit_msg>Add test for train<commit_after>extern crate bobyqa;

use self::bobyqa::Bobyqa;

use super::common::Value;
use super::error::Error;
use super::error;
use super::matrix::Matrix;
use super::network::{Network, NetworkMut};

pub struct Conf<'r> {
    pub error_conf: &'r error::Conf<'r>,
}

pub trait Train {
    fn train<'r>(&mut self, conf: &Conf) -> Value;
}

impl<'network> Train for NetworkMut<'network> {
    fn train<'r>(&mut self, conf: &Conf) -> Value {
        use std::iter::repeat;
        let nodes_count = self.weights.column_len();
        let inputs = &self.inputs;
        let outputs = &self.outputs;
        let nodes = &self.nodes;
        let error_function = |weights_values: &[Value]| {
            assert!(weights_values.len() >= nodes_count * nodes_count);
            let weights = Matrix::new(nodes_count, weights_values);
            let network = Network {inputs: inputs, outputs: outputs,
                                   weights: weights, nodes: nodes};
            network.error(conf.error_conf)
        };
        let variables_count = self.weights.values().len();
        let lower_bound = repeat(0.0).take(variables_count).collect::<Vec<_>>();
        let upper_bound = repeat(1.0).take(variables_count).collect::<Vec<_>>();
        Bobyqa::new()
            .variables_count(variables_count)
//            .number_of_interpolation_conditions((variables_count + 1)*(variables_count + 2)/2)
            .number_of_interpolation_conditions(variables_count + 2)
            .lower_bound(&lower_bound[..])
            .upper_bound(&upper_bound[..])
            .perform(self.weights.values(), &error_function)
    }
}

#[test]
fn test_train_should_succeed() {
    use std::collections::{BTreeSet, HashMap, HashSet};
    use neural_network::common::Node;
    use neural_network::matrix::MatrixMut;
    use neural_network::apply::{Conf as ApplyConf};
    use neural_network::error::{Conf as ErrorConf, Sample};
    let mut weights_values = [
        0.1, 0.1,
        0.1, 0.1,
    ];
    let inputs = [0].iter().cloned().collect::<BTreeSet<usize>>();
    let outputs = [1].iter().cloned().collect::<HashSet<usize>>();
    {
        let weights = MatrixMut::new(2, &mut weights_values);
        let nodes = (0..2).map(|x| (x, Node(x))).collect::<HashMap<usize, Node>>();
        let mut network = NetworkMut {
            inputs: &inputs,
            outputs: &outputs,
            weights: weights,
            nodes: &nodes
        };
        let apply_conf = ApplyConf {
            group_size: 1000,
            threshold: 1e-4,
        };
        let samples = [
            Sample {input: &[0.5], output: &[0.4]},
        ];
        let error_conf = ErrorConf {
            apply_conf: &apply_conf,
            samples: &samples,
        };
        let conf = Conf {
            error_conf: &error_conf,
        };
        let error = network.train(&conf);
        assert_eq!(error, 0.0000000024013835364655733);
    }
    assert_eq!(weights_values, [
        0.7500085532432279, 0.9999933043615582,
        0.986926343197382, 0.9995644922815343,
    ]);
}
<|endoftext|>


<commit_before>use std::collections::{RingBuf, HashSet};
use std::mem;
use std::ptr;

use block_allocator::BlockAllocator;
use block_info::BlockInfo;
use constants::{BLOCK_SIZE, LINE_SIZE};
use gc_object::GCObject;

pub struct LineAllocator {
    block_allocator: BlockAllocator,
    object_map: HashSet<*mut GCObject>,
    unavailable_blocks: RingBuf<*mut BlockInfo>,
    recyclable_blocks: RingBuf<*mut BlockInfo>,
    current_block: Option<(*mut BlockInfo, u16, u16)>,
}

impl LineAllocator {
    pub fn new(block_allocator: BlockAllocator) -> LineAllocator {
        return LineAllocator {
            block_allocator: block_allocator,
            object_map: HashSet::new(),
            unavailable_blocks: RingBuf::new(),
            recyclable_blocks: RingBuf::new(),
            current_block: None,
        };
    }

    pub fn is_gc_object(&self, object: *mut GCObject) -> bool {
        return self.object_map.contains(&object);
    }

    pub fn allocate(&mut self, size: uint, variables: uint) -> Option<*mut GCObject> {
        debug!("Request to allocate an object of size {}", size);
        let block_tuple = self.current_block
                              .and_then(|tp| self.scan_for_hole(size, tp))
                              .or_else(|| self.scan_recyclables(size))
                              .or_else(|| self.get_new_block());
        return match block_tuple {
            None => None,
            Some((block, low, high)) => {
                self.current_block = Some((block, low + size as u16, high));
                let object = unsafe { (*block).offset(low as uint) };
                self.object_map.insert(object);
                unsafe { ptr::write(object, GCObject::new(size, variables)); }
                debug!("Allocated object {} of size {} in {}", object, size, block);
                Some(object)
            }
        };
    }

    fn scan_for_hole(&mut self, size: uint, tuple: (*mut BlockInfo, u16, u16))
        -> Option<(*mut BlockInfo, u16, u16)> {
            let (block, low, high) = tuple;
            return match (high - low) as uint >= size {
                true => {
                    debug!("Found hole in block {:p}", block);
                    Some(tuple)
                },
                false => match unsafe{ (*block).scan_block(high) } {
                    None => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        self.unavailable_blocks.push_back(block);
                        None
                    },
                    Some((low, high)) =>
                        self.scan_for_hole(size, (block, low, high)),
                }
            };
        }

    fn scan_recyclables(&mut self, size: uint) -> Option<(*mut BlockInfo, u16, u16)> {
        return match self.recyclable_blocks.pop_front() {
            None => None,
            Some(block) => match unsafe{ (*block).scan_block((LINE_SIZE - 1) as u16) } {
                None => {
                    debug!("Push block {:p} into unavailable_blocks", block);
                    self.unavailable_blocks.push_back(block);
                    self.scan_recyclables(size)
                },
                Some((low, high)) => self.scan_for_hole(size, (block, low, high))
                                         .or_else(|| self.scan_recyclables(size)),
            }
        };
    }

    fn get_new_block(&mut self) -> Option<(*mut BlockInfo, u16, u16)> {
        debug!("Request new block from block_allocator");
        return self.block_allocator.get_block()
                   .map(|block| (block, LINE_SIZE as u16, (BLOCK_SIZE - 1) as u16));
    }

    pub fn return_empty_blocks(&mut self) {
        let mut recyclable_blocks = RingBuf::new();
        for block in self.current_block.take().map(|(b, _, _)| b).into_iter()
                         .chain(self.recyclable_blocks.drain())
                         .chain(self.unavailable_blocks.drain()) {
            if unsafe{ (*block).is_empty() } {
                debug!("Return block {:p} to global block allocator", block);
                self.block_allocator.return_block(block);
            } else {
                debug!("Recycle block {:p}", block);
                recyclable_blocks.push_back(block);
            }
        }
        self.recyclable_blocks.extend(recyclable_blocks.into_iter());
    }

    unsafe fn get_block_ptr(&mut self, object: *mut GCObject) -> *mut BlockInfo {
        let block_offset = object as uint % BLOCK_SIZE;
        return mem::transmute((object as *mut u8).offset(-(block_offset as int)));
    }

    pub fn decrement_lines(&mut self, object: *mut GCObject) {
        unsafe{
            let block_ptr = self.get_block_ptr(object);
            (*block_ptr).decrement_lines(object);
        }
    }

    pub fn increment_lines(&mut self, object: *mut GCObject) {
        unsafe{
            let block_ptr = self.get_block_ptr(object);
            (*block_ptr).increment_lines(object);
        }

    pub fn clear_line_counts(&mut self) {
        // This will only be called after the RCCollector did his work and
        // self.return_empty_blocks() was invoked. So every managed block is
        // in self.recyclable_blocks.
        for block in self.recyclable_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
        }
    }
}
<commit_msg>Clean up LineAllocator.{decrement,increment}_lines()<commit_after>use std::collections::{RingBuf, HashSet};
use std::mem;
use std::ptr;

use block_allocator::BlockAllocator;
use block_info::BlockInfo;
use constants::{BLOCK_SIZE, LINE_SIZE};
use gc_object::GCObject;

pub struct LineAllocator {
    block_allocator: BlockAllocator,
    object_map: HashSet<*mut GCObject>,
    unavailable_blocks: RingBuf<*mut BlockInfo>,
    recyclable_blocks: RingBuf<*mut BlockInfo>,
    current_block: Option<(*mut BlockInfo, u16, u16)>,
}

impl LineAllocator {
    pub fn new(block_allocator: BlockAllocator) -> LineAllocator {
        return LineAllocator {
            block_allocator: block_allocator,
            object_map: HashSet::new(),
            unavailable_blocks: RingBuf::new(),
            recyclable_blocks: RingBuf::new(),
            current_block: None,
        };
    }

    pub fn is_gc_object(&self, object: *mut GCObject) -> bool {
        return self.object_map.contains(&object);
    }

    pub fn allocate(&mut self, size: uint, variables: uint) -> Option<*mut GCObject> {
        debug!("Request to allocate an object of size {}", size);
        let block_tuple = self.current_block
                              .and_then(|tp| self.scan_for_hole(size, tp))
                              .or_else(|| self.scan_recyclables(size))
                              .or_else(|| self.get_new_block());
        return match block_tuple {
            None => None,
            Some((block, low, high)) => {
                self.current_block = Some((block, low + size as u16, high));
                let object = unsafe { (*block).offset(low as uint) };
                self.object_map.insert(object);
                unsafe { ptr::write(object, GCObject::new(size, variables)); }
                debug!("Allocated object {} of size {} in {}", object, size, block);
                Some(object)
            }
        };
    }

    fn scan_for_hole(&mut self, size: uint, tuple: (*mut BlockInfo, u16, u16))
        -> Option<(*mut BlockInfo, u16, u16)> {
            let (block, low, high) = tuple;
            return match (high - low) as uint >= size {
                true => {
                    debug!("Found hole in block {:p}", block);
                    Some(tuple)
                },
                false => match unsafe{ (*block).scan_block(high) } {
                    None => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        self.unavailable_blocks.push_back(block);
                        None
                    },
                    Some((low, high)) =>
                        self.scan_for_hole(size, (block, low, high)),
                }
            };
        }

    fn scan_recyclables(&mut self, size: uint) -> Option<(*mut BlockInfo, u16, u16)> {
        return match self.recyclable_blocks.pop_front() {
            None => None,
            Some(block) => match unsafe{ (*block).scan_block((LINE_SIZE - 1) as u16) } {
                None => {
                    debug!("Push block {:p} into unavailable_blocks", block);
                    self.unavailable_blocks.push_back(block);
                    self.scan_recyclables(size)
                },
                Some((low, high)) => self.scan_for_hole(size, (block, low, high))
                                         .or_else(|| self.scan_recyclables(size)),
            }
        };
    }

    fn get_new_block(&mut self) -> Option<(*mut BlockInfo, u16, u16)> {
        debug!("Request new block from block_allocator");
        return self.block_allocator.get_block()
                   .map(|block| (block, LINE_SIZE as u16, (BLOCK_SIZE - 1) as u16));
    }

    pub fn return_empty_blocks(&mut self) {
        let mut recyclable_blocks = RingBuf::new();
        for block in self.current_block.take().map(|(b, _, _)| b).into_iter()
                         .chain(self.recyclable_blocks.drain())
                         .chain(self.unavailable_blocks.drain()) {
            if unsafe{ (*block).is_empty() } {
                debug!("Return block {:p} to global block allocator", block);
                self.block_allocator.return_block(block);
            } else {
                debug!("Recycle block {:p}", block);
                recyclable_blocks.push_back(block);
            }
        }
        self.recyclable_blocks.extend(recyclable_blocks.into_iter());
    }

    unsafe fn get_block_ptr(&mut self, object: *mut GCObject) -> *mut BlockInfo {
        let block_offset = object as uint % BLOCK_SIZE;
        return mem::transmute((object as *mut u8).offset(-(block_offset as int)));
    }

    pub fn decrement_lines(&mut self, object: *mut GCObject) {
        unsafe{ (*self.get_block_ptr(object)).decrement_lines(object); }
    }

    pub fn increment_lines(&mut self, object: *mut GCObject) {
        unsafe{ (*self.get_block_ptr(object)).increment_lines(object); }
    }

    pub fn clear_line_counts(&mut self) {
        // This will only be called after the RCCollector did his work and
        // self.return_empty_blocks() was invoked. So every managed block is
        // in self.recyclable_blocks.
        for block in self.recyclable_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
        }
    }
}
<|endoftext|>


<commit_before>// emulator
use crate::{emulator::Emulator, host::Host, zx::tape::TapeImpl, Result};
use rustzx_z80::{RegName16, Z80Bus, FLAG_CARRY, FLAG_ZERO};

pub fn fast_load_tap<H: Host>(emulator: &mut Emulator<H>) -> Result<()> {
    // So, at current moment we at 0x056C in 48K Rom.
    // AF contains some garbage. so we need to swap if wtih A'F'
    emulator.cpu.regs.swap_af_alt();
    // now we have type of block at A and flags before LD-BYTES at F
    let mut f = emulator.cpu.regs.get_flags();
    let mut acc = emulator.cpu.regs.get_acc();
    // variable to store resulting flags
    let mut result_flags;
    // destination address in RAM
    let mut dest = emulator.cpu.regs.get_reg_16(RegName16::IX);
    // remaining length
    let mut length = emulator.cpu.regs.get_reg_16(RegName16::DE);
    // parity accumulator and current byte (h, l) regs
    let (mut parity_acc, mut current_byte) = (0, 0);
    // move to next block
    if !emulator.controller.tape.next_block()? {
        return Ok(());
    }

    'loader: loop {
        // if we still on block
        if let Some(byte) = emulator.controller.tape.next_block_byte()? {
            // set current byte, shift position and do parity check iteration
            current_byte = byte;
            parity_acc ^= current_byte;
            // no bytes left, set A to parity accumulator (works as in ROM)
            // and check parity last time
            if length == 0 {
                acc = parity_acc;
                // consider we CAN have parity error
                result_flags = Some(0);
                // if checksum correct set carry to prevent error
                if acc == 0 {
                    result_flags = Some(FLAG_CARRY);
                }
                break 'loader;
            }
            // block type check, first byte
            if (f & FLAG_ZERO) == 0 {
                acc ^= current_byte;
                // if type wrong
                if acc != 0 {
                    result_flags = Some(0);
                    break 'loader;
                }
                // type check passed, go to next byte;
                f |= FLAG_ZERO;
                continue;
            }
            // LOAD
            if (f & FLAG_CARRY) != 0 {
                emulator.controller.write_internal(dest, current_byte);
            // VERIFY
            } else {
                // check for parity each byte, if this fails - set flags to error state
                acc = emulator.controller.memory.read(dest) ^ current_byte;
                if acc != 0 {
                    result_flags = Some(0);
                    break 'loader;
                }
            }
            // move destination pointer and decrease count of remaining bytes
            dest += 1;
            length -= 1;
        } else {
            // this happens if requested length and provided are not matched
            result_flags = Some(FLAG_ZERO);
            break 'loader;
        }
    }
    // set regs to new state
    emulator.cpu.regs.set_reg_16(RegName16::IX, dest);
    emulator.cpu.regs.set_reg_16(RegName16::DE, length);
    emulator
        .cpu
        .regs
        .set_hl(u16::from_le_bytes([current_byte, parity_acc]));
    emulator.cpu.regs.set_acc(acc);
    // set new flag, if something changed
    if let Some(new_flags) = result_flags {
        f = new_flags;
        // perform RET
        emulator.cpu.pop_pc_from_stack(&mut emulator.controller);
    }
    emulator.cpu.regs.set_flags(f);
    Ok(())
}
<commit_msg>Fix dest address overflow in tap loader<commit_after>// emulator
use crate::{emulator::Emulator, host::Host, zx::tape::TapeImpl, Result};
use rustzx_z80::{RegName16, Z80Bus, FLAG_CARRY, FLAG_ZERO};

pub fn fast_load_tap<H: Host>(emulator: &mut Emulator<H>) -> Result<()> {
    // So, at current moment we at 0x056C in 48K Rom.
    // AF contains some garbage. so we need to swap if wtih A'F'
    emulator.cpu.regs.swap_af_alt();
    // now we have type of block at A and flags before LD-BYTES at F
    let mut f = emulator.cpu.regs.get_flags();
    let mut acc = emulator.cpu.regs.get_acc();
    // variable to store resulting flags
    let mut result_flags;
    // destination address in RAM
    let mut dest = emulator.cpu.regs.get_reg_16(RegName16::IX);
    // remaining length
    let mut length = emulator.cpu.regs.get_reg_16(RegName16::DE);
    // parity accumulator and current byte (h, l) regs
    let (mut parity_acc, mut current_byte) = (0, 0);
    // move to next block
    if !emulator.controller.tape.next_block()? {
        return Ok(());
    }

    'loader: loop {
        // if we still on block
        if let Some(byte) = emulator.controller.tape.next_block_byte()? {
            // set current byte, shift position and do parity check iteration
            current_byte = byte;
            parity_acc ^= current_byte;
            // no bytes left, set A to parity accumulator (works as in ROM)
            // and check parity last time
            if length == 0 {
                acc = parity_acc;
                // consider we CAN have parity error
                result_flags = Some(0);
                // if checksum correct set carry to prevent error
                if acc == 0 {
                    result_flags = Some(FLAG_CARRY);
                }
                break 'loader;
            }
            // block type check, first byte
            if (f & FLAG_ZERO) == 0 {
                acc ^= current_byte;
                // if type wrong
                if acc != 0 {
                    result_flags = Some(0);
                    break 'loader;
                }
                // type check passed, go to next byte;
                f |= FLAG_ZERO;
                continue;
            }
            // LOAD
            if (f & FLAG_CARRY) != 0 {
                emulator.controller.write_internal(dest, current_byte);
            // VERIFY
            } else {
                // check for parity each byte, if this fails - set flags to error state
                acc = emulator.controller.memory.read(dest) ^ current_byte;
                if acc != 0 {
                    result_flags = Some(0);
                    break 'loader;
                }
            }
            // move destination pointer and decrease count of remaining bytes
            dest = dest.wrapping_add(1);
            length -= 1;
        } else {
            // this happens if requested length and provided are not matched
            result_flags = Some(FLAG_ZERO);
            break 'loader;
        }
    }
    // set regs to new state
    emulator.cpu.regs.set_reg_16(RegName16::IX, dest);
    emulator.cpu.regs.set_reg_16(RegName16::DE, length);
    emulator
        .cpu
        .regs
        .set_hl(u16::from_le_bytes([current_byte, parity_acc]));
    emulator.cpu.regs.set_acc(acc);
    // set new flag, if something changed
    if let Some(new_flags) = result_flags {
        f = new_flags;
        // perform RET
        emulator.cpu.pop_pc_from_stack(&mut emulator.controller);
    }
    emulator.cpu.regs.set_flags(f);
    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Common handling for the specified value CSS url() values.

use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::root::mozilla::css::URLValue;
use crate::gecko_bindings::structs::root::mozilla::CORSMode;
use crate::gecko_bindings::structs::root::nsStyleImageRequest;
use crate::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI};
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::parser::{Parse, ParserContext};
use crate::stylesheets::UrlExtraData;
use crate::values::computed::{Context, ToComputedValue};
use cssparser::Parser;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use servo_arc::Arc;
use std::collections::HashMap;
use std::fmt::{self, Write};
use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    #[inline]
    pub fn is_fragment(&self) -> bool {
        self.0.is_fragment()
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

#[cfg(debug_assertions)]
impl Drop for CssUrlData {
    fn drop(&mut self) {
        assert!(
            !URL_VALUE_TABLE
                .read()
                .unwrap()
                .contains_key(&CssUrlDataKey(self as *mut _ as *const _)),
            "All CssUrlData objects used as keys in URL_VALUE_TABLE should be \
             from shared memory style sheets, and so should never be dropped",
        );
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A key type for URL_VALUE_TABLE.
#[derive(Hash, PartialEq, Eq)]
struct CssUrlDataKey(*const CssUrlData);

unsafe impl Sync for CssUrlDataKey {}
unsafe impl Send for CssUrlDataKey {}

/// The source of a Gecko URLValue object for a SpecifiedUrl.
#[derive(Clone, Debug)]
pub enum URLValueSource {
    /// A strong reference to a Gecko URLValue object.
    URLValue(RefPtr<URLValue>),
    /// A CORSMode value used to lazily construct a Gecko URLValue object.
    ///
    /// The lazily created object will be stored in URL_VALUE_TABLE.
    CORSMode(CORSMode),
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    ///
    /// Box this to avoid SpecifiedUrl getting bigger than two words,
    /// and increasing the size of PropertyDeclaration.
    #[css(skip)]
    url_value: Box<URLValueSource>,
}

fn make_url_value(url: &CssUrl, cors_mode: CORSMode) -> RefPtr<URLValue> {
    unsafe {
        let ptr = bindings::Gecko_URLValue_Create(url.0.clone().into_strong(), cors_mode);
        // We do not expect Gecko_URLValue_Create returns null.
        debug_assert!(!ptr.is_null());
        RefPtr::from_addrefed(ptr)
    }
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = Box::new(URLValueSource::URLValue(make_url_value(&url, cors)));
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }

    fn with_url_value<F, T>(&self, f: F) -> T
    where
        F: FnOnce(&RefPtr<URLValue>) -> T,
    {
        match *self.url_value {
            URLValueSource::URLValue(ref r) => f(r),
            URLValueSource::CORSMode(cors_mode) => {
                {
                    let guard = URL_VALUE_TABLE.read().unwrap();
                    if let Some(r) = guard.get(&(CssUrlDataKey(&*self.url.0 as *const _))) {
                        return f(r);
                    }
                }
                let mut guard = URL_VALUE_TABLE.write().unwrap();
                let r = guard
                    .entry(CssUrlDataKey(&*self.url.0 as *const _))
                    .or_insert_with(|| make_url_value(&self.url, cors_mode));
                f(r)
            },
        }
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        self.with_url_value(RefPtr::clone)
    }

    /// Get a raw pointer to the URLValue held by this SpecifiedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.with_url_value(RefPtr::get)
    }
}

/// Clears URL_VALUE_TABLE.  Entries in this table, which are for specified URL
/// values that come from shared memory style sheets, would otherwise persist
/// until the end of the process and be reported as leaks.
pub fn shutdown() {
    URL_VALUE_TABLE.write().unwrap().clear();
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value_ptr()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.0
            .with_url_value(|r| serialize_computed_url(r, dest, bindings::Gecko_GetComputedURLSpec))
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl {
            url,
            url_value: Box::new(URLValueSource::URLValue(url_value)),
        })
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        self.0.clone_url_value()
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value_ptr()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        (self.0).0.with_url_value(|r| {
            serialize_computed_url(r, dest, bindings::Gecko_GetComputedImageURLSpec)
        })
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl {
            url,
            url_value: Box::new(URLValueSource::URLValue(url_value)),
        }))
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        (self.0).0.clone_url_value()
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value_ptr()
    }
}

lazy_static! {
    /// A table mapping CssUrlData objects to their lazily created Gecko
    /// URLValue objects.
    static ref URL_VALUE_TABLE: RwLock<HashMap<CssUrlDataKey, RefPtr<URLValue>>> = {
        Default::default()
    };
}
<commit_msg>style: Add ToShmem impl for URLValueSource.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Common handling for the specified value CSS url() values.

use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::root::mozilla::css::URLValue;
use crate::gecko_bindings::structs::root::mozilla::CORSMode;
use crate::gecko_bindings::structs::root::nsStyleImageRequest;
use crate::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI};
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::parser::{Parse, ParserContext};
use crate::stylesheets::UrlExtraData;
use crate::values::computed::{Context, ToComputedValue};
use cssparser::Parser;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use servo_arc::Arc;
use std::collections::HashMap;
use std::fmt::{self, Write};
use std::mem::ManuallyDrop;
use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    #[inline]
    pub fn is_fragment(&self) -> bool {
        self.0.is_fragment()
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

#[cfg(debug_assertions)]
impl Drop for CssUrlData {
    fn drop(&mut self) {
        assert!(
            !URL_VALUE_TABLE
                .read()
                .unwrap()
                .contains_key(&CssUrlDataKey(self as *mut _ as *const _)),
            "All CssUrlData objects used as keys in URL_VALUE_TABLE should be \
             from shared memory style sheets, and so should never be dropped",
        );
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A key type for URL_VALUE_TABLE.
#[derive(Hash, PartialEq, Eq)]
struct CssUrlDataKey(*const CssUrlData);

unsafe impl Sync for CssUrlDataKey {}
unsafe impl Send for CssUrlDataKey {}

/// The source of a Gecko URLValue object for a SpecifiedUrl.
#[derive(Clone, Debug)]
pub enum URLValueSource {
    /// A strong reference to a Gecko URLValue object.
    URLValue(RefPtr<URLValue>),
    /// A CORSMode value used to lazily construct a Gecko URLValue object.
    ///
    /// The lazily created object will be stored in URL_VALUE_TABLE.
    CORSMode(CORSMode),
}

impl ToShmem for URLValueSource {
    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> {
        ManuallyDrop::new(match self {
            URLValueSource::URLValue(r) => URLValueSource::CORSMode(r.mCORSMode),
            URLValueSource::CORSMode(c) => URLValueSource::CORSMode(*c),
        })
    }
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    ///
    /// Box this to avoid SpecifiedUrl getting bigger than two words,
    /// and increasing the size of PropertyDeclaration.
    #[css(skip)]
    url_value: Box<URLValueSource>,
}

fn make_url_value(url: &CssUrl, cors_mode: CORSMode) -> RefPtr<URLValue> {
    unsafe {
        let ptr = bindings::Gecko_URLValue_Create(url.0.clone().into_strong(), cors_mode);
        // We do not expect Gecko_URLValue_Create returns null.
        debug_assert!(!ptr.is_null());
        RefPtr::from_addrefed(ptr)
    }
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = Box::new(URLValueSource::URLValue(make_url_value(&url, cors)));
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }

    fn with_url_value<F, T>(&self, f: F) -> T
    where
        F: FnOnce(&RefPtr<URLValue>) -> T,
    {
        match *self.url_value {
            URLValueSource::URLValue(ref r) => f(r),
            URLValueSource::CORSMode(cors_mode) => {
                {
                    let guard = URL_VALUE_TABLE.read().unwrap();
                    if let Some(r) = guard.get(&(CssUrlDataKey(&*self.url.0 as *const _))) {
                        return f(r);
                    }
                }
                let mut guard = URL_VALUE_TABLE.write().unwrap();
                let r = guard
                    .entry(CssUrlDataKey(&*self.url.0 as *const _))
                    .or_insert_with(|| make_url_value(&self.url, cors_mode));
                f(r)
            },
        }
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        self.with_url_value(RefPtr::clone)
    }

    /// Get a raw pointer to the URLValue held by this SpecifiedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.with_url_value(RefPtr::get)
    }
}

/// Clears URL_VALUE_TABLE.  Entries in this table, which are for specified URL
/// values that come from shared memory style sheets, would otherwise persist
/// until the end of the process and be reported as leaks.
pub fn shutdown() {
    URL_VALUE_TABLE.write().unwrap().clear();
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value_ptr()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.0
            .with_url_value(|r| serialize_computed_url(r, dest, bindings::Gecko_GetComputedURLSpec))
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl {
            url,
            url_value: Box::new(URLValueSource::URLValue(url_value)),
        })
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        self.0.clone_url_value()
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value_ptr()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        (self.0).0.with_url_value(|r| {
            serialize_computed_url(r, dest, bindings::Gecko_GetComputedImageURLSpec)
        })
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl {
            url,
            url_value: Box::new(URLValueSource::URLValue(url_value)),
        }))
    }

    /// Clone a new, strong reference to the Gecko URLValue.
    pub fn clone_url_value(&self) -> RefPtr<URLValue> {
        (self.0).0.clone_url_value()
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value_ptr()
    }
}

lazy_static! {
    /// A table mapping CssUrlData objects to their lazily created Gecko
    /// URLValue objects.
    static ref URL_VALUE_TABLE: RwLock<HashMap<CssUrlDataKey, RefPtr<URLValue>>> = {
        Default::default()
    };
}
<|endoftext|>


<commit_before>use crate::{Assist, AssistCtx, AssistId};

use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
use ra_fmt::unwrap_trivial_block;
use ra_syntax::{ast, AstNode, TextRange, T};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
    let l_curly_token = ctx.find_token_at_offset(T!['{'])?;

    let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) {
        // if expression
        let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
        let expr_to_unwrap = expr_to_unwrap?;
        // Find if we are in a else if block
        let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);

        if let Some(ancestor) = ancestor {
            Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap))
        } else {
            Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap))
        }
    } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
        // for expression
        let block_expr = for_expr.loop_body()?;
        extract_expr(ctx.frange.range, block_expr)
            .map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap))
    } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
        // while expression
        let block_expr = while_expr.loop_body()?;
        extract_expr(ctx.frange.range, block_expr)
            .map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap))
    } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
        // loop expression
        let block_expr = loop_expr.loop_body()?;
        extract_expr(ctx.frange.range, block_expr)
            .map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap))
    } else {
        None
    };

    let (expr, expr_to_unwrap) = res?;
    ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
        edit.set_cursor(expr.syntax().text_range().start());
        edit.target(expr_to_unwrap.syntax().text_range());

        let pat_start: &[_] = &[' ', '{', '\n'];
        let expr_to_unwrap = expr_to_unwrap.to_string();
        let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
        let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
        expr_string_lines.pop(); // Delete last line

        let expr_string = expr_string_lines
            .into_iter()
            .map(|line| line.replacen("    ", "", 1)) // Delete indentation
            .collect::<Vec<String>>()
            .join("\n");

        edit.replace(expr.syntax().text_range(), expr_string);
    })
}

fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option<Expr> {
    let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);

    if cursor_in_range {
        Some(unwrap_trivial_block(block))
    } else {
        None
    }
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                <|>foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                <|>println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                <|>println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    <|>foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>Simplify<commit_after>use crate::{Assist, AssistCtx, AssistId};

use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
use ra_fmt::unwrap_trivial_block;
use ra_syntax::{ast, AstNode, TextRange, T};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
    let l_curly_token = ctx.find_token_at_offset(T!['{'])?;

    let (expr, expr_to_unwrap) = if let Some(if_expr) =
        l_curly_token.ancestors().find_map(IfExpr::cast)
    {
        // if expression
        let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
        let expr_to_unwrap = expr_to_unwrap?;
        // Find if we are in a else if block
        let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);

        match ancestor {
            None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap),
            Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap),
        }
    } else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
        // for expression
        let block_expr = for_expr.loop_body()?;
        let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
        (ast::Expr::ForExpr(for_expr), expr_to_unwrap)
    } else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
        // while expression
        let block_expr = while_expr.loop_body()?;
        let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
        (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
    } else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
        // loop expression
        let block_expr = loop_expr.loop_body()?;
        let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
        (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
    } else {
        return None;
    };

    ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
        edit.set_cursor(expr.syntax().text_range().start());
        edit.target(expr_to_unwrap.syntax().text_range());

        let pat_start: &[_] = &[' ', '{', '\n'];
        let expr_to_unwrap = expr_to_unwrap.to_string();
        let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
        let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
        expr_string_lines.pop(); // Delete last line

        let expr_string = expr_string_lines
            .into_iter()
            .map(|line| line.replacen("    ", "", 1)) // Delete indentation
            .collect::<Vec<String>>()
            .join("\n");

        edit.replace(expr.syntax().text_range(), expr_string);
    })
}

fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option<Expr> {
    let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);

    if cursor_in_range {
        Some(unwrap_trivial_block(block))
    } else {
        None
    }
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                <|>foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                <|>println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                <|>println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    <|>foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                <|>if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::path::{Path, PathBuf};

use cargo_metadata::{CargoOpt, MetadataCommand};
use ra_arena::{impl_arena_id, Arena, RawId};
use ra_db::Edition;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::Result;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Arena<Package, PackageData>,
    targets: Arena<Target, TargetData>,
    pub(crate) workspace_root: PathBuf,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct CargoFeatures {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Package(RawId);
impl_arena_id!(Package);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Target(RawId);
impl_arena_id!(Target);

#[derive(Debug, Clone)]
struct PackageData {
    name: String,
    manifest: PathBuf,
    targets: Vec<Target>,
    is_member: bool,
    dependencies: Vec<PackageDependency>,
    edition: Edition,
    features: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone)]
struct TargetData {
    pkg: Package,
    name: String,
    root: PathBuf,
    kind: TargetKind,
    is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl Package {
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.packages[self].name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.packages[self].manifest.parent().unwrap()
    }
    pub fn edition(self, ws: &CargoWorkspace) -> Edition {
        ws.packages[self].edition
    }
    pub fn features(self, ws: &CargoWorkspace) -> &[String] {
        &ws.packages[self].features
    }
    pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
        ws.packages[self].targets.iter().cloned()
    }
    #[allow(unused)]
    pub fn is_member(self, ws: &CargoWorkspace) -> bool {
        ws.packages[self].is_member
    }
    pub fn dependencies<'a>(
        self,
        ws: &'a CargoWorkspace,
    ) -> impl Iterator<Item = &'a PackageDependency> + 'a {
        ws.packages[self].dependencies.iter()
    }
}

impl Target {
    pub fn package(self, ws: &CargoWorkspace) -> Package {
        ws.targets[self].pkg
    }
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.targets[self].name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.targets[self].root.as_path()
    }
    pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
        ws.targets[self].kind
    }
    pub fn is_proc_macro(self, ws: &CargoWorkspace) -> bool {
        ws.targets[self].is_proc_macro
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &Path,
        cargo_features: &CargoFeatures,
    ) -> Result<CargoWorkspace> {
        let mut meta = MetadataCommand::new();
        meta.manifest_path(cargo_toml);
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            meta.features(CargoOpt::NoDefaultFeatures);
        } else {
            meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent);
        }
        let meta = meta.exec().map_err(|e| format!("cargo metadata failed: {}", e))?;
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition.parse::<Edition>()?;
            let pkg = packages.alloc(PackageData {
                name,
                manifest: manifest_path,
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == &["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    pkg,
                    name: meta_tgt.name,
                    root: meta_tgt.src_path.clone(),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for node in resolve.nodes {
            let source = pkg_by_id[&node.id];
            for dep_node in node.deps {
                let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next()
    }
}
<commit_msg>Use all-features by default<commit_after>//! FIXME: write short doc here

use std::path::{Path, PathBuf};

use cargo_metadata::{CargoOpt, MetadataCommand};
use ra_arena::{impl_arena_id, Arena, RawId};
use ra_db::Edition;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::Result;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
#[derive(Debug, Clone)]
pub struct CargoWorkspace {
    packages: Arena<Package, PackageData>,
    targets: Arena<Target, TargetData>,
    pub(crate) workspace_root: PathBuf,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase", default)]
pub struct CargoFeatures {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,
}

impl Default for CargoFeatures {
    fn default() -> Self {
        CargoFeatures { no_default_features: false, all_features: true, features: Vec::new() }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Package(RawId);
impl_arena_id!(Package);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Target(RawId);
impl_arena_id!(Target);

#[derive(Debug, Clone)]
struct PackageData {
    name: String,
    manifest: PathBuf,
    targets: Vec<Target>,
    is_member: bool,
    dependencies: Vec<PackageDependency>,
    edition: Edition,
    features: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone)]
struct TargetData {
    pkg: Package,
    name: String,
    root: PathBuf,
    kind: TargetKind,
    is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl Package {
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.packages[self].name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.packages[self].manifest.parent().unwrap()
    }
    pub fn edition(self, ws: &CargoWorkspace) -> Edition {
        ws.packages[self].edition
    }
    pub fn features(self, ws: &CargoWorkspace) -> &[String] {
        &ws.packages[self].features
    }
    pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
        ws.packages[self].targets.iter().cloned()
    }
    #[allow(unused)]
    pub fn is_member(self, ws: &CargoWorkspace) -> bool {
        ws.packages[self].is_member
    }
    pub fn dependencies<'a>(
        self,
        ws: &'a CargoWorkspace,
    ) -> impl Iterator<Item = &'a PackageDependency> + 'a {
        ws.packages[self].dependencies.iter()
    }
}

impl Target {
    pub fn package(self, ws: &CargoWorkspace) -> Package {
        ws.targets[self].pkg
    }
    pub fn name(self, ws: &CargoWorkspace) -> &str {
        ws.targets[self].name.as_str()
    }
    pub fn root(self, ws: &CargoWorkspace) -> &Path {
        ws.targets[self].root.as_path()
    }
    pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
        ws.targets[self].kind
    }
    pub fn is_proc_macro(self, ws: &CargoWorkspace) -> bool {
        ws.targets[self].is_proc_macro
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &Path,
        cargo_features: &CargoFeatures,
    ) -> Result<CargoWorkspace> {
        let mut meta = MetadataCommand::new();
        meta.manifest_path(cargo_toml);
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            meta.features(CargoOpt::NoDefaultFeatures);
        } else {
            meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent);
        }
        let meta = meta.exec().map_err(|e| format!("cargo metadata failed: {}", e))?;
        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition.parse::<Edition>()?;
            let pkg = packages.alloc(PackageData {
                name,
                manifest: manifest_path,
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == &["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    pkg,
                    name: meta_tgt.name,
                    root: meta_tgt.src_path.clone(),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for node in resolve.nodes {
            let source = pkg_by_id[&node.id];
            for dep_node in node.deps {
                let dep = PackageDependency { name: dep_node.name, pkg: pkg_by_id[&dep_node.pkg] };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
        self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next()
    }
}
<|endoftext|>