<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;
use super::*;
use super::wrap;

use super::super::cli::SocketLend;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

const RATIO_TO_COCATEGORIZE: f64 = 0.8;
const COCATEGORY_TRAVEL_DISTANCE: i32 = 4;
const COCATEGORIZE_MAGNITUDE: i32 = 32;

impl<R: rand::Rng> Lexicon<R> {
    /// Make a new lexion. It needs its own Rng for internal purposes of learning.
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            cocategorization_ratio: RATIO_TO_COCATEGORIZE,
            cocategory_travel_distance: COCATEGORY_TRAVEL_DISTANCE,
            cocategorize_magnitude: COCATEGORIZE_MAGNITUDE,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    /// Get a source by its unique name.
    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Get an author identifier from a particular source.
    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Tell a message to the lexicon and potentially get a response back.
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) {
        let conversation = match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        // Add message to conversation
        {
            let mut cb = conversation.borrow_mut();
            cb.messages.push(message.clone());
        }

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);
    }

    /// Switch conversations
    pub fn switch(&mut self, source: SourceCell) {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());
    }

    /// Say something based on the conversation context
    pub fn respond(&mut self, source: SourceCell) -> Option<(String, String)> {
        use std::collections::VecDeque;
        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        // Make a double-ended vec for building the message out of categories
        let mut instances = VecDeque::new();

        let mut orig_index = if let Some(con) = self.active_conversations.get(&source) {
            let cb = con.borrow();
            if let Some(conm) = cb.messages.last() {
                let mb = conm.borrow();
                instances.push_back(self.rng.choose(&mb.instances[..]).unwrap().clone());
                0
            } else {
                instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
                8192
            }
        } else {
            instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
            8192
        };

        let forward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.precocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.precocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        let backward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.postcocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.postcocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        // Iterate forwards weaving between messages and adding instances to the vec
        loop {
            let ins = instances.back().unwrap().borrow().next_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_back(forward_instance_chooser(&b.category.borrow(), &mut self.rng));
            } else {
                break;
            }
        }
        // Iterate backwards to reach the beginning of the message
        loop {
            let ins = instances.front().unwrap().borrow().prev_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_front(backward_instance_chooser(&b.category.borrow(), &mut self.rng));
                orig_index += 1;
            } else {
                break;
            }
        }

        Some((
            instances.iter()
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            instances.iter()
                .cloned()
                .enumerate()
                .map(|(index, mut instance)| {
                    if index != orig_index {
                        for _ in 0..self.cocategory_travel_distance {
                            instance = {
                                let b = instance.borrow();
                                if self.rng.gen_range(0, 2) == 0 {
                                    backward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                } else {
                                    forward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                }
                            };
                        }
                    }
                    instance
                })
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" ")
        ))
    }

    /// Have seifmios attempt to initiate a conversation at a source, but it may fail.
    pub fn initiate(&mut self, source: SourceCell) -> Option<(String, String)> {
        self.switch(source.clone());
        self.respond(source)
    }

    /// Thinks one iteration
    pub fn think(&mut self) {
        // Learn a random message if there are some
        let m = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return,
        };

        self.learn(m.clone());

        for _ in 0..self.cocategorize_magnitude {
            // Get two random categories (we already know messages exist from above)
            Category::cocategorize((
                {
                    let b = m.borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
            ), self.cocategorization_ratio);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self, socket: &mut SocketLend) {
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(ib.category.clone());
            }
        }

        for cat in set {
            let catr = cat.borrow();
            if catr.instances.len() != 1 {
                socket.msg("Category:");
                for cocategory in &catr.precocategories {
                    socket.msg("\tPre-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for cocategory in &catr.postcocategories {
                    socket.msg("\tPost-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                }
            }
        }
    }

    pub fn find_relation(&self, words: (String, String), socket: &mut SocketLend) {
        let wls = (self.words.get(&words.0), self.words.get(&words.1));
        match wls {
            (Some(w0), Some(w1)) => {
                let b = w0.borrow();
                for instance in &b.instances {
                    let b = instance.borrow();
                    let cb = b.category.borrow();

                    for cins in &cb.instances {
                        if cins.borrow().word == *w1 {
                            socket.msg("Category:");
                            for instance in &cb.instances {
                                let ib = instance.borrow();
                                socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                            }
                            break;
                        }
                    }

                    // Check all the precategories
                    for prec in &cb.precocategories {
                        let cb = prec.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Pre-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }

                    // Check all the postcategories
                    for postc in &cb.postcocategories {
                        let cb = postc.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Post-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }
                }
            },
            (Some(_), None) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.0));
            },
            (None, Some(_)) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.1));
            },
            (None, None) => {
                socket.msg("Ignored: Neither word could be found");
            },
        }
    }
}
<commit_msg>move some cocategorization learning to the learn function so it learns a message better before responding<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;
use super::*;
use super::wrap;

use super::super::cli::SocketLend;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

const RATIO_TO_COCATEGORIZE: f64 = 0.8;
const COCATEGORY_TRAVEL_DISTANCE: i32 = 4;
const COCATEGORIZE_MAGNITUDE: i32 = 32;

impl<R: rand::Rng> Lexicon<R> {
    /// Make a new lexion. It needs its own Rng for internal purposes of learning.
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            cocategorization_ratio: RATIO_TO_COCATEGORIZE,
            cocategory_travel_distance: COCATEGORY_TRAVEL_DISTANCE,
            cocategorize_magnitude: COCATEGORIZE_MAGNITUDE,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    /// Get a source by its unique name.
    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Get an author identifier from a particular source.
    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    /// Tell a message to the lexicon and potentially get a response back.
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) {
        let conversation = match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        // Add message to conversation
        {
            let mut cb = conversation.borrow_mut();
            cb.messages.push(message.clone());
        }

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);
    }

    /// Switch conversations
    pub fn switch(&mut self, source: SourceCell) {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(source.clone()) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());
    }

    /// Say something based on the conversation context
    pub fn respond(&mut self, source: SourceCell) -> Option<(String, String)> {
        use std::collections::VecDeque;
        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        // Make a double-ended vec for building the message out of categories
        let mut instances = VecDeque::new();

        let mut orig_index = if let Some(con) = self.active_conversations.get(&source) {
            let cb = con.borrow();
            if let Some(conm) = cb.messages.last() {
                let mb = conm.borrow();
                instances.push_back(self.rng.choose(&mb.instances[..]).unwrap().clone());
                0
            } else {
                instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
                8192
            }
        } else {
            instances.push_back(self.rng.choose(&base.borrow().instances[..]).unwrap().clone());
            8192
        };

        let forward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.precocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.precocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        let backward_instance_chooser = |b: &Category, rng: &mut R| {
            // Find the count of how many word instances exist total
            let mut count = b.instances.len();
            for cocategory in &b.postcocategories {
                count += cocategory.borrow().instances.len();
            }
            // Generate an index based on the count
            let mut i = rng.gen_range(0, count);
            match b.instances.get(i) {
                // It was in the original category
                Some(ins) => ins.clone(),
                // It was in a cocategory
                None => {
                    // Subtract the cocategory length from the index
                    i -= b.instances.len();
                    for cocategory in &b.postcocategories {
                        let b = cocategory.borrow();
                        // If it was in this category
                        if let Some(ins) = b.instances.get(i) {
                            // Clone the instance and return it
                            return ins.clone();
                        }
                        // Otherwise subtract by the amount of instances in this cocategory
                        i -= b.instances.len();
                    }
                    // The index should point to some category, so this is unreachable
                    unreachable!();
                },
            }
        };

        // Iterate forwards weaving between messages and adding instances to the vec
        loop {
            let ins = instances.back().unwrap().borrow().next_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_back(forward_instance_chooser(&b.category.borrow(), &mut self.rng));
            } else {
                break;
            }
        }
        // Iterate backwards to reach the beginning of the message
        loop {
            let ins = instances.front().unwrap().borrow().prev_instance();
            if let Some(i) = ins {
                let b = i.borrow();
                instances.push_front(backward_instance_chooser(&b.category.borrow(), &mut self.rng));
                orig_index += 1;
            } else {
                break;
            }
        }

        Some((
            instances.iter()
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            instances.iter()
                .cloned()
                .enumerate()
                .map(|(index, mut instance)| {
                    if index != orig_index {
                        for _ in 0..self.cocategory_travel_distance {
                            instance = {
                                let b = instance.borrow();
                                if self.rng.gen_range(0, 2) == 0 {
                                    backward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                } else {
                                    forward_instance_chooser(&b.category.borrow(), &mut self.rng)
                                }
                            };
                        }
                    }
                    instance
                })
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" ")
        ))
    }

    /// Have seifmios attempt to initiate a conversation at a source, but it may fail.
    pub fn initiate(&mut self, source: SourceCell) -> Option<(String, String)> {
        self.switch(source.clone());
        self.respond(source)
    }

    /// Thinks one iteration
    pub fn think(&mut self) {
        // Learn a random message if there are some
        let m = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return,
        };

        self.learn(m.clone());
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }

        for _ in 0..self.cocategorize_magnitude {
            // Get two random categories (we already know messages exist from above)
            Category::cocategorize((
                {
                    let b = message.borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
                {
                    let b = self.rng.choose(&self.messages[..]).unwrap().borrow();
                    let b = self.rng.choose(&b.instances[..]).unwrap().borrow();
                    b.category.clone()
                },
            ), self.cocategorization_ratio);
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self, socket: &mut SocketLend) {
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(ib.category.clone());
            }
        }

        for cat in set {
            let catr = cat.borrow();
            if catr.instances.len() != 1 {
                socket.msg("Category:");
                for cocategory in &catr.precocategories {
                    socket.msg("\tPre-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for cocategory in &catr.postcocategories {
                    socket.msg("\tPost-Cocategory:");
                    let catr = cocategory.borrow();
                    for instance in &catr.instances {
                        let ib = instance.borrow();
                        socket.msg(&format!("\t\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                    }
                }
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                }
            }
        }
    }

    pub fn find_relation(&self, words: (String, String), socket: &mut SocketLend) {
        let wls = (self.words.get(&words.0), self.words.get(&words.1));
        match wls {
            (Some(w0), Some(w1)) => {
                let b = w0.borrow();
                for instance in &b.instances {
                    let b = instance.borrow();
                    let cb = b.category.borrow();

                    for cins in &cb.instances {
                        if cins.borrow().word == *w1 {
                            socket.msg("Category:");
                            for instance in &cb.instances {
                                let ib = instance.borrow();
                                socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                            }
                            break;
                        }
                    }

                    // Check all the precategories
                    for prec in &cb.precocategories {
                        let cb = prec.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Pre-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }

                    // Check all the postcategories
                    for postc in &cb.postcocategories {
                        let cb = postc.borrow();
                        for cins in &cb.instances {
                            if cins.borrow().word == *w1 {
                                socket.msg("Post-Cocategory:");
                                for instance in &cb.instances {
                                    let ib = instance.borrow();
                                    socket.msg(&format!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow()));
                                }
                                break;
                            }
                        }
                    }
                }
            },
            (Some(_), None) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.0));
            },
            (None, Some(_)) => {
                socket.msg(&format!("Ignored: Word \"{}\" coldn't be found", words.1));
            },
            (None, None) => {
                socket.msg("Ignored: Neither word could be found");
            },
        }
    }
}
<|endoftext|>


<commit_before>//! rustdoc handler

use super::pool::Pool;
use super::file::File;
use super::latest_version;
use super::crate_details::CrateDetails;
use iron::prelude::*;
use iron::{status, Url};
use iron::modifiers::Redirect;
use router::Router;
use super::{match_version, MatchVersion};
use super::error::Nope;
use super::page::Page;
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use iron::headers::{Expires, HttpDate, CacheControl, CacheDirective};
use time;
use iron::Handler;
use utils;

#[derive(Debug)]
struct RustdocPage {
    pub head: String,
    pub body: String,
    pub body_class: String,
    pub name: String,
    pub full: String,
    pub version: String,
    pub description: Option<String>,
    pub crate_details: Option<CrateDetails>,
}

impl Default for RustdocPage {
    fn default() -> RustdocPage {
        RustdocPage {
            head: String::new(),
            body: String::new(),
            body_class: String::new(),
            name: String::new(),
            full: String::new(),
            version: String::new(),
            description: None,
            crate_details: None,
        }
    }
}

impl ToJson for RustdocPage {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("rustdoc_head".to_string(), self.head.to_json());
        m.insert("rustdoc_body".to_string(), self.body.to_json());
        m.insert("rustdoc_body_class".to_string(), self.body_class.to_json());
        m.insert("rustdoc_full".to_string(), self.full.to_json());
        m.insert("rustdoc_status".to_string(), true.to_json());
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("crate_details".to_string(), self.crate_details.to_json());
        m.to_json()
    }
}

/// Handler called for `/:crate` and `/:crate/:version` URLs. Automatically redirects to the docs
/// or crate details page based on whether the given crate version was successfully built.
pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {

    fn redirect_to_doc(req: &Request,
                       name: &str,
                       vers: &str,
                       target_name: &str)
                       -> IronResult<Response> {
        let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}/",
                                            req.url.scheme(),
                                            req.url.host(),
                                            req.url.port(),
                                            name,
                                            vers,
                                            target_name)[..]));
        let mut resp = Response::with((status::Found, Redirect(url)));
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    fn redirect_to_crate(req: &Request,
                         name: &str,
                         vers: &str)
                         -> IronResult<Response> {
        let url = ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
                                            req.url.scheme(),
                                            req.url.host(),
                                            req.url.port(),
                                            name,
                                            vers)[..]));

        let mut resp = Response::with((status::Found, Redirect(url)));
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    // this unwrap is safe because iron urls are always able to use `path_segments`
    // i'm using this instead of `req.url.path()` to avoid allocating the Vec, and also to avoid
    // keeping the borrow alive into the return statement
    if req.url.as_ref().path_segments().unwrap().last().map_or(false, |s| s.ends_with(".js")) {
        // javascript files should be handled by the file server instead of erroneously
        // redirecting to the crate root page
        return rustdoc_html_server_handler(req);
    } else if req.url.as_ref().path_segments().unwrap().last().map_or(false, |s| s.ends_with(".ico")) {
        // route .ico files into their dedicated handler so that docs.rs's favicon is always
        // displayed
        return super::ico_handler(req);
    }

    let router = extension!(req, Router);
    // this handler should never called without crate pattern
    let crate_name = cexpect!(router.find("crate"));
    let req_version = router.find("version");

    let conn = extension!(req, Pool);

    // it doesn't matter if the version that was given was exact or not, since we're redirecting
    // anyway
    let version = match match_version(&conn, &crate_name, req_version).into_option() {
        Some(v) => v,
        None => return Err(IronError::new(Nope::CrateNotFound, status::NotFound)),
    };

    // get target name and whether it has docs
    // FIXME: This is a bit inefficient but allowing us to use less code in general
    let (target_name, has_docs): (String, bool) = {
        let rows = ctry!(conn.query("SELECT target_name, rustdoc_status
                                     FROM releases
                                     INNER JOIN crates ON crates.id = releases.crate_id
                                     WHERE crates.name = $1 AND releases.version = $2",
                                    &[&crate_name, &version]));

        (rows.get(0).get(0), rows.get(0).get(1))
    };

    if has_docs {
        redirect_to_doc(req, &crate_name, &version, &target_name)
    } else {
        redirect_to_crate(req, &crate_name, &version)
    }
}

/// Serves documentation generated by rustdoc.
///
/// This includes all HTML files for an individual crate, as well as the `search-index.js`, which is
/// also crate-specific.
pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {

    let router = extension!(req, Router);
    let name = router.find("crate").unwrap_or("").to_string();
    let url_version = router.find("version");
    let version; // pre-declaring it to enforce drop order relative to `req_path`
    let conn = extension!(req, Pool);

    let mut req_path = req.url.path();

    // remove name and version from path
    for _ in 0..2 {
        req_path.remove(0);
    }

    version = match match_version(&conn, &name, url_version) {
        MatchVersion::Exact(v) => v,
        MatchVersion::Semver(v) => {
            // to prevent cloudfront caching the wrong artifacts on URLs with loose semver
            // versions, redirect the browser to the returned version instead of loading it
            // immediately
            let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}",
                                                req.url.scheme(),
                                                req.url.host(),
                                                req.url.port(),
                                                name,
                                                v,
                                                req_path.join("/"))[..]));
            return Ok(super::redirect(url));
        }
        MatchVersion::None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    // docs have "rustdoc" prefix in database
    req_path.insert(0, "rustdoc");

    // add crate name and version
    req_path.insert(1, &name);
    req_path.insert(2, &version);

    let path = {
        let mut path = req_path.join("/");
        if path.ends_with("/") {
            path.push_str("index.html");
            req_path.push("index.html");
        }
        path
    };

    let file = match File::from_path(&conn, &path) {
        Some(f) => f,
        None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    // serve file directly if it's not html
    if !path.ends_with(".html") {
        return Ok(file.serve());
    }

    let mut content = RustdocPage::default();

    let file_content = ctry!(String::from_utf8(file.content));

    let (head, body, mut body_class) = ctry!(utils::extract_head_and_body(&file_content));
    content.head = head;
    content.body = body;

    if body_class.is_empty() {
        body_class = "rustdoc container-rustdoc".to_string();
    } else {
        // rustdoc adds its own "rustdoc" class to the body
        body_class.push_str(" container-rustdoc");
    }
    content.body_class = body_class;

    content.full = file_content;
    let crate_details = cexpect!(CrateDetails::new(&conn, &name, &version));
    let latest_version = latest_version(&crate_details.versions, &version);

    content.crate_details = Some(crate_details);

    Page::new(content)
        .set_true("show_package_navigation")
        .set_true("package_navigation_documentation_tab")
        .set_true("package_navigation_show_platforms_tab")
        .set_bool("is_latest_version", latest_version.is_none())
        .set("latest_version", &latest_version.unwrap_or(String::new()))
        .to_resp("rustdoc")
}

pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
    use iron::headers::ContentType;
    use params::{Params, Value};
    use badge::{Badge, BadgeOptions};

    let version = {
        let params = ctry!(req.get_ref::<Params>());
        match params.find(&["version"]) {
            Some(&Value::String(ref version)) => version.clone(),
            _ => "*".to_owned(),
        }
    };

    let name = cexpect!(extension!(req, Router).find("crate"));
    let conn = extension!(req, Pool);

    let options = match match_version(&conn, &name, Some(&version)) {
        MatchVersion::Exact(version) => {
            let rows = ctry!(conn.query("SELECT rustdoc_status
                                         FROM releases
                                         INNER JOIN crates ON crates.id = releases.crate_id
                                         WHERE crates.name = $1 AND releases.version = $2",
                                        &[&name, &version]));
            if rows.len() > 0 && rows.get(0).get(0) {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#4d76ae".to_owned(),
                }
            } else {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#e05d44".to_owned(),
                }
            }
        }
        MatchVersion::Semver(version) => {
            let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/badge.svg?version={}",
                                                req.url.scheme(),
                                                req.url.host(),
                                                req.url.port(),
                                                name,
                                                version)[..]));

            return Ok(super::redirect(url));
        }
        MatchVersion::None => {
            BadgeOptions {
                subject: "docs".to_owned(),
                status: "no builds".to_owned(),
                color: "#e05d44".to_owned(),
            }
        }
    };

    let mut resp = Response::with((status::Ok, ctry!(Badge::new(options)).to_svg()));
    resp.headers.set(ContentType("image/svg+xml".parse().unwrap()));
    resp.headers.set(Expires(HttpDate(time::now())));
    resp.headers.set(CacheControl(vec![CacheDirective::NoCache,
                                       CacheDirective::NoStore,
                                       CacheDirective::MustRevalidate]));
    Ok(resp)
}

/// Serves shared web resources used by rustdoc-generated documentation.
///
/// This includes common `css` and `js` files that only change when the compiler is updated, but are
/// otherwise the same for all crates documented with that compiler. Those have a custom handler to
/// deduplicate them and save space.
pub struct SharedResourceHandler;

impl Handler for SharedResourceHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let path = req.url.path();
        let filename = path.last().unwrap();  // unwrap is fine: vector is non-empty
        let suffix = filename.split('.').last().unwrap();  // unwrap is fine: split always works
        if ["js", "css", "woff", "svg"].contains(&suffix) {
            let conn = extension!(req, Pool);

            if let Some(file) = File::from_path(conn, filename) {
                return Ok(file.serve());
            }
        }

        // Just always return a 404 here - the main handler will then try the other handlers
        Err(IronError::new(Nope::ResourceNotFound, status::NotFound))
    }
}
<commit_msg>return files directly when serving JS files<commit_after>//! rustdoc handler

use super::pool::Pool;
use super::file::File;
use super::latest_version;
use super::crate_details::CrateDetails;
use iron::prelude::*;
use iron::{status, Url};
use iron::modifiers::Redirect;
use router::Router;
use super::{match_version, MatchVersion};
use super::error::Nope;
use super::page::Page;
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use iron::headers::{Expires, HttpDate, CacheControl, CacheDirective};
use time;
use iron::Handler;
use utils;

#[derive(Debug)]
struct RustdocPage {
    pub head: String,
    pub body: String,
    pub body_class: String,
    pub name: String,
    pub full: String,
    pub version: String,
    pub description: Option<String>,
    pub crate_details: Option<CrateDetails>,
}

impl Default for RustdocPage {
    fn default() -> RustdocPage {
        RustdocPage {
            head: String::new(),
            body: String::new(),
            body_class: String::new(),
            name: String::new(),
            full: String::new(),
            version: String::new(),
            description: None,
            crate_details: None,
        }
    }
}

impl ToJson for RustdocPage {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("rustdoc_head".to_string(), self.head.to_json());
        m.insert("rustdoc_body".to_string(), self.body.to_json());
        m.insert("rustdoc_body_class".to_string(), self.body_class.to_json());
        m.insert("rustdoc_full".to_string(), self.full.to_json());
        m.insert("rustdoc_status".to_string(), true.to_json());
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("crate_details".to_string(), self.crate_details.to_json());
        m.to_json()
    }
}

/// Handler called for `/:crate` and `/:crate/:version` URLs. Automatically redirects to the docs
/// or crate details page based on whether the given crate version was successfully built.
pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {

    fn redirect_to_doc(req: &Request,
                       name: &str,
                       vers: &str,
                       target_name: &str)
                       -> IronResult<Response> {
        let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}/",
                                            req.url.scheme(),
                                            req.url.host(),
                                            req.url.port(),
                                            name,
                                            vers,
                                            target_name)[..]));
        let mut resp = Response::with((status::Found, Redirect(url)));
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    fn redirect_to_crate(req: &Request,
                         name: &str,
                         vers: &str)
                         -> IronResult<Response> {
        let url = ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
                                            req.url.scheme(),
                                            req.url.host(),
                                            req.url.port(),
                                            name,
                                            vers)[..]));

        let mut resp = Response::with((status::Found, Redirect(url)));
        resp.headers.set(Expires(HttpDate(time::now())));

        Ok(resp)
    }

    // this unwrap is safe because iron urls are always able to use `path_segments`
    // i'm using this instead of `req.url.path()` to avoid allocating the Vec, and also to avoid
    // keeping the borrow alive into the return statement
    if req.url.as_ref().path_segments().unwrap().last().map_or(false, |s| s.ends_with(".js")) {
        // javascript files should be handled by the file server instead of erroneously
        // redirecting to the crate root page
        if req.url.as_ref().path_segments().unwrap().count() > 2 {
            // this URL is actually from a crate-internal path, serve it there instead
            return rustdoc_html_server_handler(req);
        } else {
            let path = req.url.path();
            let path = path.join("/");
            let conn = extension!(req, Pool);
            match File::from_path(&conn, &path) {
                Some(f) => return Ok(f.serve()),
                None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
            }
        }
    } else if req.url.as_ref().path_segments().unwrap().last().map_or(false, |s| s.ends_with(".ico")) {
        // route .ico files into their dedicated handler so that docs.rs's favicon is always
        // displayed
        return super::ico_handler(req);
    }

    let router = extension!(req, Router);
    // this handler should never called without crate pattern
    let crate_name = cexpect!(router.find("crate"));
    let req_version = router.find("version");

    let conn = extension!(req, Pool);

    // it doesn't matter if the version that was given was exact or not, since we're redirecting
    // anyway
    let version = match match_version(&conn, &crate_name, req_version).into_option() {
        Some(v) => v,
        None => return Err(IronError::new(Nope::CrateNotFound, status::NotFound)),
    };

    // get target name and whether it has docs
    // FIXME: This is a bit inefficient but allowing us to use less code in general
    let (target_name, has_docs): (String, bool) = {
        let rows = ctry!(conn.query("SELECT target_name, rustdoc_status
                                     FROM releases
                                     INNER JOIN crates ON crates.id = releases.crate_id
                                     WHERE crates.name = $1 AND releases.version = $2",
                                    &[&crate_name, &version]));

        (rows.get(0).get(0), rows.get(0).get(1))
    };

    if has_docs {
        redirect_to_doc(req, &crate_name, &version, &target_name)
    } else {
        redirect_to_crate(req, &crate_name, &version)
    }
}

/// Serves documentation generated by rustdoc.
///
/// This includes all HTML files for an individual crate, as well as the `search-index.js`, which is
/// also crate-specific.
pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {

    let router = extension!(req, Router);
    let name = router.find("crate").unwrap_or("").to_string();
    let url_version = router.find("version");
    let version; // pre-declaring it to enforce drop order relative to `req_path`
    let conn = extension!(req, Pool);

    let mut req_path = req.url.path();

    // remove name and version from path
    for _ in 0..2 {
        req_path.remove(0);
    }

    version = match match_version(&conn, &name, url_version) {
        MatchVersion::Exact(v) => v,
        MatchVersion::Semver(v) => {
            // to prevent cloudfront caching the wrong artifacts on URLs with loose semver
            // versions, redirect the browser to the returned version instead of loading it
            // immediately
            let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}",
                                                req.url.scheme(),
                                                req.url.host(),
                                                req.url.port(),
                                                name,
                                                v,
                                                req_path.join("/"))[..]));
            return Ok(super::redirect(url));
        }
        MatchVersion::None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    // docs have "rustdoc" prefix in database
    req_path.insert(0, "rustdoc");

    // add crate name and version
    req_path.insert(1, &name);
    req_path.insert(2, &version);

    let path = {
        let mut path = req_path.join("/");
        if path.ends_with("/") {
            path.push_str("index.html");
            req_path.push("index.html");
        }
        path
    };

    let file = match File::from_path(&conn, &path) {
        Some(f) => f,
        None => return Err(IronError::new(Nope::ResourceNotFound, status::NotFound)),
    };

    // serve file directly if it's not html
    if !path.ends_with(".html") {
        return Ok(file.serve());
    }

    let mut content = RustdocPage::default();

    let file_content = ctry!(String::from_utf8(file.content));

    let (head, body, mut body_class) = ctry!(utils::extract_head_and_body(&file_content));
    content.head = head;
    content.body = body;

    if body_class.is_empty() {
        body_class = "rustdoc container-rustdoc".to_string();
    } else {
        // rustdoc adds its own "rustdoc" class to the body
        body_class.push_str(" container-rustdoc");
    }
    content.body_class = body_class;

    content.full = file_content;
    let crate_details = cexpect!(CrateDetails::new(&conn, &name, &version));
    let latest_version = latest_version(&crate_details.versions, &version);

    content.crate_details = Some(crate_details);

    Page::new(content)
        .set_true("show_package_navigation")
        .set_true("package_navigation_documentation_tab")
        .set_true("package_navigation_show_platforms_tab")
        .set_bool("is_latest_version", latest_version.is_none())
        .set("latest_version", &latest_version.unwrap_or(String::new()))
        .to_resp("rustdoc")
}

pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
    use iron::headers::ContentType;
    use params::{Params, Value};
    use badge::{Badge, BadgeOptions};

    let version = {
        let params = ctry!(req.get_ref::<Params>());
        match params.find(&["version"]) {
            Some(&Value::String(ref version)) => version.clone(),
            _ => "*".to_owned(),
        }
    };

    let name = cexpect!(extension!(req, Router).find("crate"));
    let conn = extension!(req, Pool);

    let options = match match_version(&conn, &name, Some(&version)) {
        MatchVersion::Exact(version) => {
            let rows = ctry!(conn.query("SELECT rustdoc_status
                                         FROM releases
                                         INNER JOIN crates ON crates.id = releases.crate_id
                                         WHERE crates.name = $1 AND releases.version = $2",
                                        &[&name, &version]));
            if rows.len() > 0 && rows.get(0).get(0) {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#4d76ae".to_owned(),
                }
            } else {
                BadgeOptions {
                    subject: "docs".to_owned(),
                    status: version,
                    color: "#e05d44".to_owned(),
                }
            }
        }
        MatchVersion::Semver(version) => {
            let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/badge.svg?version={}",
                                                req.url.scheme(),
                                                req.url.host(),
                                                req.url.port(),
                                                name,
                                                version)[..]));

            return Ok(super::redirect(url));
        }
        MatchVersion::None => {
            BadgeOptions {
                subject: "docs".to_owned(),
                status: "no builds".to_owned(),
                color: "#e05d44".to_owned(),
            }
        }
    };

    let mut resp = Response::with((status::Ok, ctry!(Badge::new(options)).to_svg()));
    resp.headers.set(ContentType("image/svg+xml".parse().unwrap()));
    resp.headers.set(Expires(HttpDate(time::now())));
    resp.headers.set(CacheControl(vec![CacheDirective::NoCache,
                                       CacheDirective::NoStore,
                                       CacheDirective::MustRevalidate]));
    Ok(resp)
}

/// Serves shared web resources used by rustdoc-generated documentation.
///
/// This includes common `css` and `js` files that only change when the compiler is updated, but are
/// otherwise the same for all crates documented with that compiler. Those have a custom handler to
/// deduplicate them and save space.
pub struct SharedResourceHandler;

impl Handler for SharedResourceHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let path = req.url.path();
        let filename = path.last().unwrap();  // unwrap is fine: vector is non-empty
        let suffix = filename.split('.').last().unwrap();  // unwrap is fine: split always works
        if ["js", "css", "woff", "svg"].contains(&suffix) {
            let conn = extension!(req, Pool);

            if let Some(file) = File::from_path(conn, filename) {
                return Ok(file.serve());
            }
        }

        // Just always return a 404 here - the main handler will then try the other handlers
        Err(IronError::new(Nope::ResourceNotFound, status::NotFound))
    }
}
<|endoftext|>


<commit_before>use rustc_span::ExpnId;
use std::fmt;

rustc_index::newtype_index! {
    pub struct NodeId {
        DEBUG_FORMAT = "NodeId({})"
    }
}

rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);

/// `NodeId` used to represent the root of the crate.
pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);

/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, in the renumber pass, we renumber them to have
/// small, positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;

impl NodeId {
    pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self {
        NodeId::from_u32(expn_id.as_u32())
    }

    pub fn placeholder_to_expn_id(self) -> ExpnId {
        ExpnId::from_u32(self.as_u32())
    }
}

impl fmt::Display for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.as_u32(), f)
    }
}
<commit_msg>Rollup merge of #78795 - est31:node_id_assignment_doc_fix, r=oli-obk<commit_after>use rustc_span::ExpnId;
use std::fmt;

rustc_index::newtype_index! {
    pub struct NodeId {
        DEBUG_FORMAT = "NodeId({})"
    }
}

rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);

/// `NodeId` used to represent the root of the crate.
pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);

/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, during expansion, we renumber them to have small,
/// positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;

impl NodeId {
    pub fn placeholder_from_expn_id(expn_id: ExpnId) -> Self {
        NodeId::from_u32(expn_id.as_u32())
    }

    pub fn placeholder_to_expn_id(self) -> ExpnId {
        ExpnId::from_u32(self.as_u32())
    }
}

impl fmt::Display for NodeId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.as_u32(), f)
    }
}
<|endoftext|>


<commit_before>use definitions::PREAMBLE;
use definitions::Value;
use definitions::config::VMConfig;
use definitions::instruction::Instruction;
use definitions::interrupt::{ExternalInterrupt, InternalInterrupt};
use definitions::program::Program;
use definitions::target::Target;
use definitions::typedef::*;
use error::*;
use std::cmp::Ordering;
use std::collections::{BTreeMap, LinkedList};
use std::sync::mpsc::{Receiver, SyncSender, TryRecvError, TrySendError};
use std::thread::{self, JoinHandle};

pub fn start(program: Program, sender: SyncSender<Frame>, receiver: Receiver<ExternalInterrupt>)
    -> JoinHandle<()> {
    thread::spawn(
        move || {
            if let Err(ref e) = VM::default().exec(program, sender, receiver) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                // The backtrace is not always generated. Try to run this example
                // with `RUST_BACKTRACE=1`.
                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }
        },
    )
}

/// The whole state of the VM
#[derive(Default, Debug)]
struct VM {
    /// The instructions that are currently executed
    image_data: Vec<Instruction>,
    /// The current program counter
    pc: Address,
    stack: LinkedList<Value>,
    val_index: BTreeMap<Address, Value>,
    interrupt_register: BTreeMap<usize, Address>,
    framebuffer: Frame,
    framebuffer_invalid: bool,
    /// A register for holding infomation about a recent comparison
    cmp_register: Option<Ordering>,
    /// A stack to hold the return addresses of function calls
    call_stack: LinkedList<Address>,
    /// A boolean used for locking the program counter
    pc_locked: bool,
    /// The configuration of the VM
    config: VMConfig,
    halted: bool,
}

impl VM {
    // # Maintainance functions

    /// Executes the given program
    pub fn exec(
        &mut self, program: Program, sender: SyncSender<Frame>,
        receiver: Receiver<ExternalInterrupt>
    ) -> VMResult<()> {
        self.reset();
        self.load_program(program)?;
        self.build_framebuffer();

        while (self.pc < self.image_data.len()) && !self.halted {
            self.external_interrupt(&receiver)?;

            let current_instruction = self.image_data[self.pc].clone();

            match current_instruction {
                Instruction::Halt => break,
                Instruction::Int(interrupt) => self.int(&interrupt),

                Instruction::Add(dest, src) => self.add(&dest, &src)?,
                Instruction::Sub(dest, src) => self.sub(&dest, &src)?,
                Instruction::Div(dest, src) => self.div(&dest, &src)?,
                Instruction::Mul(dest, src) => self.mul(&dest, &src)?,
                Instruction::Rem(dest, src) => self.rem(&dest, &src)?,

                Instruction::Cmp(target_a, target_b) => self.cmp(&target_a, &target_b)?,
                Instruction::Jmp(addr) => self.jmp(&addr),
                Instruction::JmpLt(addr) => self.jmp_lt(&addr),
                Instruction::JmpGt(addr) => self.jmp_gt(&addr),
                Instruction::JmpEq(addr) => self.jmp_eq(&addr),
                Instruction::JmpLtEq(addr) => self.jmp_lt_eq(&addr),
                Instruction::JmpGtEq(addr) => self.jmp_gt_eq(&addr),

                Instruction::Push(dest, value) => self.push(&dest, value)?,
                Instruction::Mov(dest, src) => self.mov(&dest, &src)?,
                Instruction::Swp(target_a, target_b) => self.swp(&target_a, &target_b)?,

                Instruction::Call(addr) => self.call(&addr),
                Instruction::Ret => self.ret()?,
            }

            self.flush_framebuffer(&sender)?;

            self.advance_pc();

            thread::yield_now();
        }

        Ok(())
    }

    /// Loads the instructions of the given program to the VM's state
    fn load_program(&mut self, program: Program) -> VMResult<()> {
        if program.preamble != String::from(PREAMBLE) {
            bail!("invalid preamble");
        } else if program.version != String::from(env!("CARGO_PKG_VERSION")) {
            bail!("invalid version");
        } else {
            self.image_data = program.instructions;
            self.config = program.config;
            Ok(())
        }
    }

    /// Aborts the execution of the current image
    fn abort(&mut self) {
        self.halted = true;
    }

    /// Handles an internal interrupt
    fn int(&mut self, interrupt: &InternalInterrupt) {
        match interrupt {
            &InternalInterrupt::FlushFramebuffer => self.invalidate(),
        }
    }

    /// Handles incoming interrupts or moves along
    fn external_interrupt(&mut self, receiver: &Receiver<ExternalInterrupt>) -> VMResult<()> {
        match receiver.try_recv() {
            Ok(interrupt) => {
                if interrupt.signal_id == 0 {
                    self.abort();
                    return Ok(());
                }

                let call_addr = if let Some(call_addr) = self.interrupt_register
                       .get(&interrupt.signal_id) {
                    call_addr.clone()
                } else {
                    bail!(
                        "no registered interrupt found at signal_id {}",
                        &interrupt.signal_id
                    );
                };

                for value in interrupt.args {
                    self.push(&Target::Stack, value)?;
                }

                self.call(&call_addr);

                Ok(())
            }
            Err(TryRecvError::Disconnected) => bail!("interrupt receiver disconnected"),
            _ => Ok(()),
        }
    }

    /// Flushes the internal framebuffer using the given sender
    fn flush_framebuffer(&mut self, sender: &SyncSender<Frame>) -> VMResult<()> {
        if self.framebuffer_invalid {
            if let Err(TrySendError::Disconnected(_)) = sender.try_send(self.framebuffer.clone()) {
                bail!("output channel disconnected");
            }

            self.framebuffer_invalid = false;
        }

        Ok(())
    }

    /// Allocates all the needed space in the framebuffer
    fn build_framebuffer(&mut self) {
        let ref resolution = self.config.display.resolution;
        let allocation_space = resolution.width * resolution.height;
        self.framebuffer = vec![(0, 0, 0); allocation_space];
    }

    /// Resets the VM to a clean state
    fn reset(&mut self) {
        *self = VM::default();
    }

    /// Locks the program counter in place
    fn lock_pc(&mut self) {
        self.pc_locked = true;
    }

    /// Advances the program counter
    fn advance_pc(&mut self) {
        if self.pc_locked {
            self.pc_locked = false;
        } else {
            self.pc += 1;
        }
    }

    /// Invalidates the frambuffer causing it to be resent to the display
    /// receiver
    fn invalidate(&mut self) {
        self.framebuffer_invalid = true;
    }

    /// Return the value at the specified target. The value of the target will
    /// be consumed
    fn pop(&mut self, target: &Target) -> VMResult<Value> {
        match target {
            &Target::ValueIndex(index) => {
                if let Some(value) = self.val_index.remove(&index) {
                    Ok(value)
                } else {
                    bail!("no value found at index {}", index);
                }
            }
            &Target::Stack => {
                if let Some(value) = self.stack.pop_front() {
                    Ok(value)
                } else {
                    bail!("unable to pop value off an empty stack");
                }
            }
            &Target::Framebuffer(index) => {
                if let Some(&(r, g, b)) = self.framebuffer.get(index) {
                    Ok(Value::Color(r, g, b))
                } else {
                    bail!("no value found in framebuffer at index {}", index);
                }
            }
        }
    }

    // # Instruction functions

    /// Adds the value of the src target to the value of the dest target
    fn add(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value + src_value)?;

        Ok(())
    }

    /// Subtracts the value of the src target from the value of the dest target
    fn sub(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value - src_value)?;

        Ok(())
    }

    /// Divides the value of the dest target through the value of the src target
    fn div(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value / src_value)?;

        Ok(())
    }

    /// Multiplies the value of the dest target with the value of the src target
    fn mul(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Applies the modulo operator on the value of the dest target using the
    /// value of the src target
    fn rem(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Compares the top values of the two targets and saves the result to
    /// `self.cmp_register`
    fn cmp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let target_a_value = self.pop(target_a)?;
        let target_b_value = self.pop(target_b)?;

        if target_a_value < target_b_value {
            self.cmp_register = Some(Ordering::Less);
        } else if target_a_value > target_b_value {
            self.cmp_register = Some(Ordering::Greater);
        } else if target_a_value == target_b_value {
            self.cmp_register = Some(Ordering::Equal);
        }

        Ok(())
    }

    /// Jumps unconditionally to the specified address
    fn jmp(&mut self, addr: &Address) {
        self.pc = *addr;
        self.lock_pc();
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)`
    fn jmp_lt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Less) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)`
    fn jmp_gt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Greater) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Equal)`
    fn jmp_eq(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Equal) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)` or
    /// `Some(Ordering::Equal)`
    fn jmp_lt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Less)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)` or
    /// `Some(Ordering::Equal)`
    fn jmp_gt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Greater)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Pushes the given value to the given target
    fn push(&mut self, dest: &Target, value: Value) -> VMResult<()> {
        match dest {
            &Target::ValueIndex(index) => {
                self.val_index.entry(index).or_insert(value);
                Ok(())
            }
            &Target::Stack => {
                self.stack.push_front(value);
                Ok(())
            }
            &Target::Framebuffer(index) => {
                if let Value::Color(r, g, b) = value {
                    self.framebuffer[index] = (r, g, b);
                    Ok(())
                } else {
                    bail!("unable push a non-color value to the framebuffer");
                }
            }
        }
    }

    /// Moves the top value of the src target to the dest target
    fn mov(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let src_value = self.pop(src)?;
        self.push(dest, src_value)?;

        Ok(())
    }

    /// Swaps the top values of the targets
    fn swp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let a_value = self.pop(target_a)?;
        let b_value = self.pop(target_b)?;

        self.push(target_a, b_value)?;
        self.push(target_b, a_value)?;

        Ok(())
    }

    /// Calls the function at the specified address saving the return address
    /// to the call stack
    fn call(&mut self, addr: &Address) {
        self.call_stack.push_front(self.pc + 1);
        self.jmp(addr);
    }

    /// Returns from an ongoing function call
    fn ret(&mut self) -> VMResult<()> {
        if let Some(retur_addr) = self.call_stack.pop_front() {
            self.jmp(&retur_addr);
        } else {
            bail!("unable to return from an empty call stack");
        }

        Ok(())
    }
}
<commit_msg>Optimized pattern matching in framebuffer flush method<commit_after>use definitions::PREAMBLE;
use definitions::Value;
use definitions::config::VMConfig;
use definitions::instruction::Instruction;
use definitions::interrupt::{ExternalInterrupt, InternalInterrupt};
use definitions::program::Program;
use definitions::target::Target;
use definitions::typedef::*;
use error::*;
use std::cmp::Ordering;
use std::collections::{BTreeMap, LinkedList};
use std::sync::mpsc::{Receiver, SyncSender, TryRecvError, TrySendError};
use std::thread::{self, JoinHandle};

pub fn start(program: Program, sender: SyncSender<Frame>, receiver: Receiver<ExternalInterrupt>)
    -> JoinHandle<()> {
    thread::spawn(
        move || {
            if let Err(ref e) = VM::default().exec(program, sender, receiver) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                // The backtrace is not always generated. Try to run this example
                // with `RUST_BACKTRACE=1`.
                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }
        },
    )
}

/// The whole state of the VM
#[derive(Default, Debug)]
struct VM {
    /// The instructions that are currently executed
    image_data: Vec<Instruction>,
    /// The current program counter
    pc: Address,
    stack: LinkedList<Value>,
    val_index: BTreeMap<Address, Value>,
    interrupt_register: BTreeMap<usize, Address>,
    framebuffer: Frame,
    framebuffer_invalid: bool,
    /// A register for holding infomation about a recent comparison
    cmp_register: Option<Ordering>,
    /// A stack to hold the return addresses of function calls
    call_stack: LinkedList<Address>,
    /// A boolean used for locking the program counter
    pc_locked: bool,
    /// The configuration of the VM
    config: VMConfig,
    halted: bool,
}

impl VM {
    // # Maintainance functions

    /// Executes the given program
    pub fn exec(
        &mut self, program: Program, sender: SyncSender<Frame>,
        receiver: Receiver<ExternalInterrupt>
    ) -> VMResult<()> {
        self.reset();
        self.load_program(program)?;
        self.build_framebuffer();

        while (self.pc < self.image_data.len()) && !self.halted {
            self.external_interrupt(&receiver)?;

            let current_instruction = self.image_data[self.pc].clone();

            match current_instruction {
                Instruction::Halt => break,
                Instruction::Int(interrupt) => self.int(&interrupt),

                Instruction::Add(dest, src) => self.add(&dest, &src)?,
                Instruction::Sub(dest, src) => self.sub(&dest, &src)?,
                Instruction::Div(dest, src) => self.div(&dest, &src)?,
                Instruction::Mul(dest, src) => self.mul(&dest, &src)?,
                Instruction::Rem(dest, src) => self.rem(&dest, &src)?,

                Instruction::Cmp(target_a, target_b) => self.cmp(&target_a, &target_b)?,
                Instruction::Jmp(addr) => self.jmp(&addr),
                Instruction::JmpLt(addr) => self.jmp_lt(&addr),
                Instruction::JmpGt(addr) => self.jmp_gt(&addr),
                Instruction::JmpEq(addr) => self.jmp_eq(&addr),
                Instruction::JmpLtEq(addr) => self.jmp_lt_eq(&addr),
                Instruction::JmpGtEq(addr) => self.jmp_gt_eq(&addr),

                Instruction::Push(dest, value) => self.push(&dest, value)?,
                Instruction::Mov(dest, src) => self.mov(&dest, &src)?,
                Instruction::Swp(target_a, target_b) => self.swp(&target_a, &target_b)?,

                Instruction::Call(addr) => self.call(&addr),
                Instruction::Ret => self.ret()?,
            }

            self.flush_framebuffer(&sender)?;

            self.advance_pc();

            thread::yield_now();
        }

        Ok(())
    }

    /// Loads the instructions of the given program to the VM's state
    fn load_program(&mut self, program: Program) -> VMResult<()> {
        if program.preamble != String::from(PREAMBLE) {
            bail!("invalid preamble");
        } else if program.version != String::from(env!("CARGO_PKG_VERSION")) {
            bail!("invalid version");
        } else {
            self.image_data = program.instructions;
            self.config = program.config;
            Ok(())
        }
    }

    /// Aborts the execution of the current image
    fn abort(&mut self) {
        self.halted = true;
    }

    /// Handles an internal interrupt
    fn int(&mut self, interrupt: &InternalInterrupt) {
        match interrupt {
            &InternalInterrupt::FlushFramebuffer => self.invalidate(),
        }
    }

    /// Handles incoming interrupts or moves along
    fn external_interrupt(&mut self, receiver: &Receiver<ExternalInterrupt>) -> VMResult<()> {
        match receiver.try_recv() {
            Ok(interrupt) => {
                if interrupt.signal_id == 0 {
                    self.abort();
                    return Ok(());
                }

                let call_addr = if let Some(call_addr) = self.interrupt_register
                       .get(&interrupt.signal_id) {
                    call_addr.clone()
                } else {
                    bail!(
                        "no registered interrupt found at signal_id {}",
                        &interrupt.signal_id
                    );
                };

                for value in interrupt.args {
                    self.push(&Target::Stack, value)?;
                }

                self.call(&call_addr);

                Ok(())
            }
            Err(TryRecvError::Disconnected) => bail!("interrupt receiver disconnected"),
            _ => Ok(()),
        }
    }

    /// Flushes the internal framebuffer using the given sender
    fn flush_framebuffer(&mut self, sender: &SyncSender<Frame>) -> VMResult<()> {
        if self.framebuffer_invalid {
            if let Err(TrySendError::Disconnected(..)) = sender.try_send(self.framebuffer.clone()) {
                bail!("output channel disconnected");
            }

            self.framebuffer_invalid = false;
        }

        Ok(())
    }

    /// Allocates all the needed space in the framebuffer
    fn build_framebuffer(&mut self) {
        let ref resolution = self.config.display.resolution;
        let allocation_space = resolution.width * resolution.height;
        self.framebuffer = vec![(0, 0, 0); allocation_space];
    }

    /// Resets the VM to a clean state
    fn reset(&mut self) {
        *self = VM::default();
    }

    /// Locks the program counter in place
    fn lock_pc(&mut self) {
        self.pc_locked = true;
    }

    /// Advances the program counter
    fn advance_pc(&mut self) {
        if self.pc_locked {
            self.pc_locked = false;
        } else {
            self.pc += 1;
        }
    }

    /// Invalidates the frambuffer causing it to be resent to the display
    /// receiver
    fn invalidate(&mut self) {
        self.framebuffer_invalid = true;
    }

    /// Return the value at the specified target. The value of the target will
    /// be consumed
    fn pop(&mut self, target: &Target) -> VMResult<Value> {
        match target {
            &Target::ValueIndex(index) => {
                if let Some(value) = self.val_index.remove(&index) {
                    Ok(value)
                } else {
                    bail!("no value found at index {}", index);
                }
            }
            &Target::Stack => {
                if let Some(value) = self.stack.pop_front() {
                    Ok(value)
                } else {
                    bail!("unable to pop value off an empty stack");
                }
            }
            &Target::Framebuffer(index) => {
                if let Some(&(r, g, b)) = self.framebuffer.get(index) {
                    Ok(Value::Color(r, g, b))
                } else {
                    bail!("no value found in framebuffer at index {}", index);
                }
            }
        }
    }

    // # Instruction functions

    /// Adds the value of the src target to the value of the dest target
    fn add(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value + src_value)?;

        Ok(())
    }

    /// Subtracts the value of the src target from the value of the dest target
    fn sub(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value - src_value)?;

        Ok(())
    }

    /// Divides the value of the dest target through the value of the src target
    fn div(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value / src_value)?;

        Ok(())
    }

    /// Multiplies the value of the dest target with the value of the src target
    fn mul(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Applies the modulo operator on the value of the dest target using the
    /// value of the src target
    fn rem(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let dest_value = self.pop(dest)?;
        let src_value = self.pop(src)?;

        self.push(dest, dest_value * src_value)?;

        Ok(())
    }

    /// Compares the top values of the two targets and saves the result to
    /// `self.cmp_register`
    fn cmp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let target_a_value = self.pop(target_a)?;
        let target_b_value = self.pop(target_b)?;

        if target_a_value < target_b_value {
            self.cmp_register = Some(Ordering::Less);
        } else if target_a_value > target_b_value {
            self.cmp_register = Some(Ordering::Greater);
        } else if target_a_value == target_b_value {
            self.cmp_register = Some(Ordering::Equal);
        }

        Ok(())
    }

    /// Jumps unconditionally to the specified address
    fn jmp(&mut self, addr: &Address) {
        self.pc = *addr;
        self.lock_pc();
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)`
    fn jmp_lt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Less) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)`
    fn jmp_gt(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Greater) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Equal)`
    fn jmp_eq(&mut self, addr: &Address) {
        if self.cmp_register == Some(Ordering::Equal) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Less)` or
    /// `Some(Ordering::Equal)`
    fn jmp_lt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Less)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Jumps if the last compare got the result `Some(Ordering::Greater)` or
    /// `Some(Ordering::Equal)`
    fn jmp_gt_eq(&mut self, addr: &Address) {
        if (self.cmp_register == Some(Ordering::Greater)) ||
           (self.cmp_register == Some(Ordering::Equal)) {
            self.jmp(addr);
        }
    }

    /// Pushes the given value to the given target
    fn push(&mut self, dest: &Target, value: Value) -> VMResult<()> {
        match dest {
            &Target::ValueIndex(index) => {
                self.val_index.entry(index).or_insert(value);
                Ok(())
            }
            &Target::Stack => {
                self.stack.push_front(value);
                Ok(())
            }
            &Target::Framebuffer(index) => {
                if let Value::Color(r, g, b) = value {
                    self.framebuffer[index] = (r, g, b);
                    Ok(())
                } else {
                    bail!("unable push a non-color value to the framebuffer");
                }
            }
        }
    }

    /// Moves the top value of the src target to the dest target
    fn mov(&mut self, dest: &Target, src: &Target) -> VMResult<()> {
        let src_value = self.pop(src)?;
        self.push(dest, src_value)?;

        Ok(())
    }

    /// Swaps the top values of the targets
    fn swp(&mut self, target_a: &Target, target_b: &Target) -> VMResult<()> {
        let a_value = self.pop(target_a)?;
        let b_value = self.pop(target_b)?;

        self.push(target_a, b_value)?;
        self.push(target_b, a_value)?;

        Ok(())
    }

    /// Calls the function at the specified address saving the return address
    /// to the call stack
    fn call(&mut self, addr: &Address) {
        self.call_stack.push_front(self.pc + 1);
        self.jmp(addr);
    }

    /// Returns from an ongoing function call
    fn ret(&mut self) -> VMResult<()> {
        if let Some(retur_addr) = self.call_stack.pop_front() {
            self.jmp(&retur_addr);
        } else {
            bail!("unable to return from an empty call stack");
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use spin::Mutex;

use memory::Frame;
use paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
use paging::entry::EntryFlags;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200;

        {
            let page = Page::containing_address(VirtualAddress::new(mode_info_addr));
            let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get()));
            let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::NO_EXECUTE);
            result.flush(active_table);
        }

        {
            let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };

            width = mode_info.xresolution as usize;
            height = mode_info.yresolution as usize;
            physbaseptr = mode_info.physbaseptr as usize;
        }

        {
            let page = Page::containing_address(VirtualAddress::new(mode_info_addr));
            let result = active_table.unmap_return(page);
            result.flush(active_table);
        }
    }

    {
        let size = width * height;

        let onscreen = physbaseptr + ::KERNEL_OFFSET;
        {
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - ::KERNEL_OFFSET));
                let flags = EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE;
                let result = active_table.map_to(page, frame, flags);
                result.flush(active_table);
            }
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    if let Some(debug_display) = DEBUG_DISPLAY.lock().take() {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.width * display.height;
        {
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let result = active_table.unmap_return(page);
                result.flush(active_table);
            }
        }
    }

    println!("Finished graphical debug");
}
<commit_msg>Fix mapping code for physbaseptr<commit_after>use spin::Mutex;

use memory::Frame;
use paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress};
use paging::entry::EntryFlags;
use paging::mapper::MapperFlushAll;

pub use self::debug::DebugDisplay;
use self::display::Display;
use self::mode_info::VBEModeInfo;
use self::primitive::fast_set64;

pub mod debug;
pub mod display;
pub mod mode_info;
pub mod primitive;

pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font");

pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None);

pub fn init(active_table: &mut ActivePageTable) {
    println!("Starting graphical debug");

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info_addr = 0x5200;

        {
            let page = Page::containing_address(VirtualAddress::new(mode_info_addr));
            let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get()));
            let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::NO_EXECUTE);
            result.flush(active_table);
        }

        {
            let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) };

            width = mode_info.xresolution as usize;
            height = mode_info.yresolution as usize;
            physbaseptr = mode_info.physbaseptr as usize;
        }

        {
            let page = Page::containing_address(VirtualAddress::new(mode_info_addr));
            let (result, _frame) = active_table.unmap_return(page, false);
            result.flush(active_table);
        }
    }

    {
        let size = width * height;

        let onscreen = physbaseptr + ::KERNEL_OFFSET;
        {
            let mut flush_all = MapperFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get() - ::KERNEL_OFFSET));
                let flags = EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE;
                let result = active_table.map_to(page, frame, flags);
                flush_all.consume(result);
            }
            flush_all.flush(active_table);
        }

        unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

        let display = Display::new(width, height, onscreen);
        let debug_display = DebugDisplay::new(display);
        *DEBUG_DISPLAY.lock() = Some(debug_display);
    }
}

pub fn fini(active_table: &mut ActivePageTable) {
    if let Some(debug_display) = DEBUG_DISPLAY.lock().take() {
        let display = debug_display.into_display();
        let onscreen = display.onscreen.as_mut_ptr() as usize;
        let size = display.width * display.height;
        {
            let mut flush_all = MapperFlushAll::new();
            let start_page = Page::containing_address(VirtualAddress::new(onscreen));
            let end_page = Page::containing_address(VirtualAddress::new(onscreen + size * 4));
            for page in Page::range_inclusive(start_page, end_page) {
                let (result, _frame) = active_table.unmap_return(page, false);
                flush_all.consume(result);
            }
            flush_all.flush(active_table);
        }
    }

    println!("Finished graphical debug");
}
<|endoftext|>