<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(dead_code)]

use kvproto::raftpb::{Entry, Snapshot};

// unstable.entris[i] has raft log position i+unstable.offset.
// Note that unstable.offset may be less than the highest log
// position in storage; this means that the next write to storage
// might need to truncate the log before persisting unstable.entries.
#[derive(Debug, PartialEq, Default)]
pub struct Unstable {
    // the incoming unstable snapshot, if any.
    pub snapshot: Option<Snapshot>,
    // all entries that have not yet been written to storage.
    pub entries: Vec<Entry>,
    pub offset: u64,
}

impl Unstable {
    pub fn new(offset: u64) -> Unstable {
        Unstable {
            offset: offset,
            snapshot: None,
            entries: vec![],
        }
    }
    // maybe_first_index returns the index of the first possible entry in entries
    // if it has a snapshot.
    pub fn maybe_first_index(&self) -> Option<u64> {
        self.snapshot
            .as_ref()
            .map_or(None, |snap| Some(snap.get_metadata().get_index() + 1))
    }

    pub fn get_snapshot(&self) -> Snapshot {
        self.snapshot.as_ref().unwrap().clone()
    }

    // maybe_last_index returns the last index if it has at least one
    // unstable entry or snapshot.
    pub fn maybe_last_index(&self) -> Option<u64> {
        match self.entries.len() {
            0 => {
                self.snapshot
                    .as_ref()
                    .map_or(None, |snap| Some(snap.get_metadata().get_index()))
            }
            len => Some(self.offset + len as u64 - 1),
        }
    }

    // maybe_term returns the term of the entry at index idx, if there
    // is any.
    pub fn maybe_term(&self, idx: u64) -> Option<u64> {
        if idx < self.offset {
            if self.snapshot.is_none() {
                return None;
            }

            let meta = self.snapshot.as_ref().unwrap().get_metadata();
            if idx == meta.get_index() {
                return Some(meta.get_term());
            }
            return None;
        }
        match self.maybe_last_index() {
            None => None,
            Some(last) => {
                if idx > last {
                    return None;
                }
                Some(self.entries[(idx - self.offset) as usize].get_term())
            }
        }
    }

    pub fn stable_to(&mut self, idx: u64, term: u64) {
        let t = self.maybe_term(idx);
        if t.is_none() {
            return;
        }

        if t.unwrap() == term && idx >= self.offset {
            let start = idx + 1 - self.offset;
            self.entries.drain(..start as usize);
            self.offset = idx + 1;
        }
    }

    pub fn stable_snap_to(&mut self, idx: u64) {
        if self.snapshot.is_none() {
            return;
        }
        if idx == self.snapshot.as_ref().unwrap().get_metadata().get_index() {
            self.snapshot = None;
        }
    }

    pub fn restore(&mut self, snap: Snapshot) {
        self.entries.clear();
        self.offset = snap.get_metadata().get_index() + 1;
        self.snapshot = Some(snap);
    }

    // append entries to unstable, truncate local block first if overlapped.
    pub fn truncate_and_append(&mut self, ents: &[Entry]) {
        let after = ents[0].get_index() - 1;
        if after == self.offset + self.entries.len() as u64 - 1 {
            // after is the last index in the self.entries, append directly
            self.entries.extend_from_slice(ents);
        } else if after < self.offset {
            // The log is being truncated to before our current offset
            // portion, so set the offset and replace the entries
            self.offset = after + 1;
            self.entries.clear();
            self.entries.extend_from_slice(ents);
        } else {
            // truncate to after and copy to self.entries then append
            let off = self.offset;
            self.entries = {
                let cut_ents = self.slice(off, after + 1);
                let mut entries = Vec::with_capacity(cut_ents.len() + ents.len());
                entries.extend_from_slice(cut_ents);
                entries.extend_from_slice(ents);
                entries
            };
        }
    }

    pub fn slice(&self, lo: u64, hi: u64) -> &[Entry] {
        self.must_check_outofbounds(lo, hi);
        let l = lo as usize;
        let h = hi as usize;
        let off = self.offset as usize;
        &self.entries[l - off..h - off]
    }

    pub fn must_check_outofbounds(&self, lo: u64, hi: u64) {
        if lo > hi {
            panic!("invalid unstable.slice {} > {}", lo, hi)
        }
        let upper = self.offset + self.entries.len() as u64;
        if lo < self.offset || hi > upper {
            panic!("unstable.slice[{}, {}] out of bound[{}, {}]",
                   lo,
                   hi,
                   self.offset,
                   upper)
        }
    }
}

#[cfg(test)]
mod test {
    use kvproto::raftpb::{Entry, Snapshot};
    use raft::log_unstable::Unstable;

    fn new_entry(index: u64, term: u64) -> Entry {
        let mut e = Entry::new();
        e.set_term(term);
        e.set_index(index);
        e
    }

    fn new_snapshot(index: u64, term: u64) -> Snapshot {
        use kvproto::raftpb::{SnapshotMetadata, Snapshot};
        let mut snap = Snapshot::new();
        let mut meta = SnapshotMetadata::new();
        meta.set_index(index);
        meta.set_term(term);
        snap.set_metadata(meta);
        snap
    }

    #[test]
    fn test_maybe_first_index() {
        // entry, offset, snap, wok, windex,
        let tests = vec![
            // no snapshot
            (Some(new_entry(5,1)), 5, None, false, 0),
            (None, 0, None, false, 0),
            // has snapshot
            (Some(new_entry(5, 1)), 5, Some(new_snapshot(4,1)), true, 5),
            (None, 5, Some(new_snapshot(4,1)), true, 5),
        ];

        for (entries, offset, snapshot, wok, windex) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let index = u.maybe_first_index();
            match index {
                None => assert!(!wok),
                Some(index) => assert_eq!(index, windex),
            }
        }
    }

    #[test]
    fn test_maybe_last_index() {
        // entry, offset, snap, wok, windex,
        let tests = vec![
            (Some(new_entry(5,1)), 5, None, true, 5),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), true, 5),
            // last in snapshot
            (None, 5, Some(new_snapshot(4,1)), true, 4),
            // empty unstable
            (None, 0, None, false, 0),
       ];

        for (entries, offset, snapshot, wok, windex) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let index = u.maybe_last_index();
            match index {
                None => assert!(!wok),
                Some(index) => assert_eq!(index, windex),
            }
        }
    }

    #[test]
    fn test_maybe_term() {
        // entry, offset, snap, index, wok, wterm
        let tests = vec![
            // term from entries
            (Some(new_entry(5,1)), 5, None, 5, true, 1),
            (Some(new_entry(5,1)), 5, None, 6, false, 0),
            (Some(new_entry(5,1)), 5, None, 4, false, 0),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 5, true, 1),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 6, false, 0),
            // term from snapshot
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 4, true, 1),
            (None, 5, Some(new_snapshot(4,1)), 5, false, 0),
            (None, 5, Some(new_snapshot(4,1)), 4, true, 1),
            (None, 0, None, 5, false, 0),
       ];

        for (entries, offset, snapshot, index, wok, wterm) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let term = u.maybe_term(index);
            match term {
                None => assert!(!wok),
                Some(term) => assert_eq!(term, wterm),
            }
        }
    }

    #[test]
    fn test_restore() {
        let mut u = Unstable {
            entries: vec![new_entry(5, 1)],
            offset: 5,
            snapshot: Some(new_snapshot(4, 1)),
        };

        let s = new_snapshot(6, 2);
        u.restore(s.clone());

        assert_eq!(u.offset, s.get_metadata().get_index() + 1);
        assert!(u.entries.is_empty());
        assert_eq!(u.snapshot.unwrap(), s);
    }

    #[test]
    fn test_stable_to() {
        // entries, offset, snap, index, term, woffset, wlen
        let tests = vec![
            (vec![], 0, None, 5, 1, 0, 0),
            // stable to the first entry
            (vec![new_entry(5,1)], 5, None, 5, 1, 6, 0),
            (vec![new_entry(5,1), new_entry(6, 0)], 5, None, 5, 1, 6, 1),
            // stable to the first entry and term mismatch
            (vec![new_entry(6,2)], 5, None, 6, 1, 5, 1),
            // stable to old entry
            (vec![new_entry(5,1)], 5, None, 4, 1, 5, 1),
            (vec![new_entry(5,1)], 5, None, 4, 2, 5, 1),
            // with snapshot
            // stable to the first entry
            (vec![new_entry(5,1)], 5, Some(new_snapshot(4,1)), 5, 1, 6, 0),
            // stable to the first entry
            (vec![new_entry(5,1), new_entry(6,1)], 5, Some(new_snapshot(4,1)), 5, 1, 6, 1),
            // stable to the first entry and term mismatch
            (vec![new_entry(6,2)], 5, Some(new_snapshot(5,1)), 6, 1, 5, 1),
            // stable to snapshot
            (vec![new_entry(5,1)], 5, Some(new_snapshot(4,1)), 4, 1, 5, 1),
            // stable to old entry
            (vec![new_entry(5,2)], 5, Some(new_snapshot(4,2)), 4, 1, 5, 1),
       ];

        for (entries, offset, snapshot, index, term, woffset, wlen) in tests {
            let mut u = Unstable {
                entries: entries,
                offset: offset,
                snapshot: snapshot,
            };
            u.stable_to(index, term);
            assert_eq!(u.offset, woffset);
            assert_eq!(u.entries.len(), wlen);
        }
    }

    #[test]
    fn test_truncate_and_append() {
        // entries, offset, snap, to_append, woffset, wentries
        let tests = vec![
            // replace to the end
            (vec![new_entry(5, 1)], 5, None,
                vec![new_entry(6, 1), new_entry(7, 1)],
                    5, vec![new_entry(5,1), new_entry(6,1), new_entry(7,1)]),

            // replace to unstable entries
           (vec![new_entry(5, 1)], 5, None,
               vec![new_entry(5, 2), new_entry(6, 2)],
                   5, vec![new_entry(5, 2), new_entry(6, 2)]),

            (vec![new_entry(5, 1)], 5, None,
                vec![new_entry(4, 2), new_entry(5, 2), new_entry(6, 2)],
                    4, vec![new_entry(4,2), new_entry(5, 2), new_entry(6, 2)]),

            // truncate existing entries and append
           (vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 1)], 5, None,
               vec![new_entry(6, 2)],
                   5, vec![new_entry(5, 1), new_entry(6, 2)]),

            (vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 1)], 5, None,
                vec![new_entry(7, 2), new_entry(8, 2)],
                    5, vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 2), new_entry(8, 2)]),
      ];

        for (entries, offset, snapshot, to_append, woffset, wentries) in tests {
            let mut u = Unstable {
                entries: entries,
                offset: offset,
                snapshot: snapshot,
            };
            u.truncate_and_append(&to_append);
            assert_eq!(u.offset, woffset);
            assert_eq!(u.entries, wentries);
        }
    }
}
<commit_msg>raft: port from etcd. (#745)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(dead_code)]

use kvproto::raftpb::{Entry, Snapshot};

// unstable.entris[i] has raft log position i+unstable.offset.
// Note that unstable.offset may be less than the highest log
// position in storage; this means that the next write to storage
// might need to truncate the log before persisting unstable.entries.
#[derive(Debug, PartialEq, Default)]
pub struct Unstable {
    // the incoming unstable snapshot, if any.
    pub snapshot: Option<Snapshot>,
    // all entries that have not yet been written to storage.
    pub entries: Vec<Entry>,
    pub offset: u64,
}

impl Unstable {
    pub fn new(offset: u64) -> Unstable {
        Unstable {
            offset: offset,
            snapshot: None,
            entries: vec![],
        }
    }
    // maybe_first_index returns the index of the first possible entry in entries
    // if it has a snapshot.
    pub fn maybe_first_index(&self) -> Option<u64> {
        self.snapshot
            .as_ref()
            .map_or(None, |snap| Some(snap.get_metadata().get_index() + 1))
    }

    pub fn get_snapshot(&self) -> Snapshot {
        self.snapshot.as_ref().unwrap().clone()
    }

    // maybe_last_index returns the last index if it has at least one
    // unstable entry or snapshot.
    pub fn maybe_last_index(&self) -> Option<u64> {
        match self.entries.len() {
            0 => {
                self.snapshot
                    .as_ref()
                    .map_or(None, |snap| Some(snap.get_metadata().get_index()))
            }
            len => Some(self.offset + len as u64 - 1),
        }
    }

    // maybe_term returns the term of the entry at index idx, if there
    // is any.
    pub fn maybe_term(&self, idx: u64) -> Option<u64> {
        if idx < self.offset {
            if self.snapshot.is_none() {
                return None;
            }

            let meta = self.snapshot.as_ref().unwrap().get_metadata();
            if idx == meta.get_index() {
                return Some(meta.get_term());
            }
            return None;
        }
        match self.maybe_last_index() {
            None => None,
            Some(last) => {
                if idx > last {
                    return None;
                }
                Some(self.entries[(idx - self.offset) as usize].get_term())
            }
        }
    }

    pub fn stable_to(&mut self, idx: u64, term: u64) {
        let t = self.maybe_term(idx);
        if t.is_none() {
            return;
        }

        if t.unwrap() == term && idx >= self.offset {
            let start = idx + 1 - self.offset;
            self.entries.drain(..start as usize);
            self.offset = idx + 1;
        }
    }

    pub fn stable_snap_to(&mut self, idx: u64) {
        if self.snapshot.is_none() {
            return;
        }
        if idx == self.snapshot.as_ref().unwrap().get_metadata().get_index() {
            self.snapshot = None;
        }
    }

    pub fn restore(&mut self, snap: Snapshot) {
        self.entries.clear();
        self.offset = snap.get_metadata().get_index() + 1;
        self.snapshot = Some(snap);
    }

    // append entries to unstable, truncate local block first if overlapped.
    pub fn truncate_and_append(&mut self, ents: &[Entry]) {
        let after = ents[0].get_index();
        if after == self.offset + self.entries.len() as u64 {
            // after is the next index in the self.entries, append directly
            self.entries.extend_from_slice(ents);
        } else if after <= self.offset {
            // The log is being truncated to before our current offset
            // portion, so set the offset and replace the entries
            self.offset = after;
            self.entries.clear();
            self.entries.extend_from_slice(ents);
        } else {
            // truncate to after and copy to self.entries then append
            let off = self.offset;
            self.entries = {
                let cut_ents = self.slice(off, after);
                let mut entries = Vec::with_capacity(cut_ents.len() + ents.len());
                entries.extend_from_slice(cut_ents);
                entries.extend_from_slice(ents);
                entries
            };
        }
    }

    pub fn slice(&self, lo: u64, hi: u64) -> &[Entry] {
        self.must_check_outofbounds(lo, hi);
        let l = lo as usize;
        let h = hi as usize;
        let off = self.offset as usize;
        &self.entries[l - off..h - off]
    }

    pub fn must_check_outofbounds(&self, lo: u64, hi: u64) {
        if lo > hi {
            panic!("invalid unstable.slice {} > {}", lo, hi)
        }
        let upper = self.offset + self.entries.len() as u64;
        if lo < self.offset || hi > upper {
            panic!("unstable.slice[{}, {}] out of bound[{}, {}]",
                   lo,
                   hi,
                   self.offset,
                   upper)
        }
    }
}

#[cfg(test)]
mod test {
    use kvproto::raftpb::{Entry, Snapshot};
    use raft::log_unstable::Unstable;

    fn new_entry(index: u64, term: u64) -> Entry {
        let mut e = Entry::new();
        e.set_term(term);
        e.set_index(index);
        e
    }

    fn new_snapshot(index: u64, term: u64) -> Snapshot {
        use kvproto::raftpb::{SnapshotMetadata, Snapshot};
        let mut snap = Snapshot::new();
        let mut meta = SnapshotMetadata::new();
        meta.set_index(index);
        meta.set_term(term);
        snap.set_metadata(meta);
        snap
    }

    #[test]
    fn test_maybe_first_index() {
        // entry, offset, snap, wok, windex,
        let tests = vec![
            // no snapshot
            (Some(new_entry(5,1)), 5, None, false, 0),
            (None, 0, None, false, 0),
            // has snapshot
            (Some(new_entry(5, 1)), 5, Some(new_snapshot(4,1)), true, 5),
            (None, 5, Some(new_snapshot(4,1)), true, 5),
        ];

        for (entries, offset, snapshot, wok, windex) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let index = u.maybe_first_index();
            match index {
                None => assert!(!wok),
                Some(index) => assert_eq!(index, windex),
            }
        }
    }

    #[test]
    fn test_maybe_last_index() {
        // entry, offset, snap, wok, windex,
        let tests = vec![
            (Some(new_entry(5,1)), 5, None, true, 5),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), true, 5),
            // last in snapshot
            (None, 5, Some(new_snapshot(4,1)), true, 4),
            // empty unstable
            (None, 0, None, false, 0),
       ];

        for (entries, offset, snapshot, wok, windex) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let index = u.maybe_last_index();
            match index {
                None => assert!(!wok),
                Some(index) => assert_eq!(index, windex),
            }
        }
    }

    #[test]
    fn test_maybe_term() {
        // entry, offset, snap, index, wok, wterm
        let tests = vec![
            // term from entries
            (Some(new_entry(5,1)), 5, None, 5, true, 1),
            (Some(new_entry(5,1)), 5, None, 6, false, 0),
            (Some(new_entry(5,1)), 5, None, 4, false, 0),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 5, true, 1),
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 6, false, 0),
            // term from snapshot
            (Some(new_entry(5,1)), 5, Some(new_snapshot(4,1)), 4, true, 1),
            (None, 5, Some(new_snapshot(4,1)), 5, false, 0),
            (None, 5, Some(new_snapshot(4,1)), 4, true, 1),
            (None, 0, None, 5, false, 0),
       ];

        for (entries, offset, snapshot, index, wok, wterm) in tests {
            let u = Unstable {
                entries: entries.map_or(vec![], |entry| vec![entry]),
                offset: offset,
                snapshot: snapshot,
            };
            let term = u.maybe_term(index);
            match term {
                None => assert!(!wok),
                Some(term) => assert_eq!(term, wterm),
            }
        }
    }

    #[test]
    fn test_restore() {
        let mut u = Unstable {
            entries: vec![new_entry(5, 1)],
            offset: 5,
            snapshot: Some(new_snapshot(4, 1)),
        };

        let s = new_snapshot(6, 2);
        u.restore(s.clone());

        assert_eq!(u.offset, s.get_metadata().get_index() + 1);
        assert!(u.entries.is_empty());
        assert_eq!(u.snapshot.unwrap(), s);
    }

    #[test]
    fn test_stable_to() {
        // entries, offset, snap, index, term, woffset, wlen
        let tests = vec![
            (vec![], 0, None, 5, 1, 0, 0),
            // stable to the first entry
            (vec![new_entry(5,1)], 5, None, 5, 1, 6, 0),
            (vec![new_entry(5,1), new_entry(6, 0)], 5, None, 5, 1, 6, 1),
            // stable to the first entry and term mismatch
            (vec![new_entry(6,2)], 5, None, 6, 1, 5, 1),
            // stable to old entry
            (vec![new_entry(5,1)], 5, None, 4, 1, 5, 1),
            (vec![new_entry(5,1)], 5, None, 4, 2, 5, 1),
            // with snapshot
            // stable to the first entry
            (vec![new_entry(5,1)], 5, Some(new_snapshot(4,1)), 5, 1, 6, 0),
            // stable to the first entry
            (vec![new_entry(5,1), new_entry(6,1)], 5, Some(new_snapshot(4,1)), 5, 1, 6, 1),
            // stable to the first entry and term mismatch
            (vec![new_entry(6,2)], 5, Some(new_snapshot(5,1)), 6, 1, 5, 1),
            // stable to snapshot
            (vec![new_entry(5,1)], 5, Some(new_snapshot(4,1)), 4, 1, 5, 1),
            // stable to old entry
            (vec![new_entry(5,2)], 5, Some(new_snapshot(4,2)), 4, 1, 5, 1),
       ];

        for (entries, offset, snapshot, index, term, woffset, wlen) in tests {
            let mut u = Unstable {
                entries: entries,
                offset: offset,
                snapshot: snapshot,
            };
            u.stable_to(index, term);
            assert_eq!(u.offset, woffset);
            assert_eq!(u.entries.len(), wlen);
        }
    }

    #[test]
    fn test_truncate_and_append() {
        // entries, offset, snap, to_append, woffset, wentries
        let tests = vec![
            // replace to the end
            (vec![new_entry(5, 1)], 5, None,
                vec![new_entry(6, 1), new_entry(7, 1)],
                    5, vec![new_entry(5,1), new_entry(6,1), new_entry(7,1)]),

            // replace to unstable entries
           (vec![new_entry(5, 1)], 5, None,
               vec![new_entry(5, 2), new_entry(6, 2)],
                   5, vec![new_entry(5, 2), new_entry(6, 2)]),

            (vec![new_entry(5, 1)], 5, None,
                vec![new_entry(4, 2), new_entry(5, 2), new_entry(6, 2)],
                    4, vec![new_entry(4,2), new_entry(5, 2), new_entry(6, 2)]),

            // truncate existing entries and append
           (vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 1)], 5, None,
               vec![new_entry(6, 2)],
                   5, vec![new_entry(5, 1), new_entry(6, 2)]),

            (vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 1)], 5, None,
                vec![new_entry(7, 2), new_entry(8, 2)],
                    5, vec![new_entry(5, 1), new_entry(6, 1), new_entry(7, 2), new_entry(8, 2)]),
      ];

        for (entries, offset, snapshot, to_append, woffset, wentries) in tests {
            let mut u = Unstable {
                entries: entries,
                offset: offset,
                snapshot: snapshot,
            };
            u.truncate_and_append(&to_append);
            assert_eq!(u.offset, woffset);
            assert_eq!(u.entries, wentries);
        }
    }
}
<|endoftext|>


<commit_before>//! Hyphenating iterators.

use std::borrow::Cow;

use language::{Corpus};
use utilia::{Interspersable, Intersperse};

pub trait Hyphenation<Hyphenator> where Hyphenator : Iterator {
    /// Returns the byte indices of valid hyphenation points within a word.
    fn opportunities(self, corp: &Corpus) -> Vec<usize>;

    /// Returns an iterator over orthographic syllables separated by valid
    /// hyphenation points.
    ///
    /// Note that, in some orthographies, the syllables of a hyphenated word
    /// are not necessarily substrings of the original word.
    fn hyphenate(self, corp: &Corpus) -> Hyphenator;
}

/// The `Standard` hyphenator iterates over a word, returning slices
/// delimited by word boundaries and valid hyphenation points.
#[derive(Clone, Debug)]
pub struct Standard<'a> {
    word: &'a str,
    opportunities: Vec<usize>,
    prior: usize,
    i: usize
}

impl<'a> Standard<'a> {
    // Inserts a soft hyphen at hyphenation points.
    pub fn punctuate(self) -> Intersperse<Self> {
        self.intersperse("\u{ad}")
    }
}

impl<'a> Iterator for Standard<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        let start = self.prior;
        let i = self.i;

        match self.opportunities.get(i) {
            Some(&end) => {
                self.prior = end;
                self.i = i + 1;
                Some(&self.word[start .. end])
            },
            None => {
                if i <= self.opportunities.len() {
                    self.i = i + 1;
                    Some(&self.word[start ..])
                } else {
                    None
                }
            }
        }
    }
}

impl<'a> Hyphenation<Standard<'a>> for &'a str {
    fn opportunities(self, corp: &Corpus) -> Vec<usize> {
        if self.chars().count() < corp.left_min + corp.right_min {
            return vec![];
        }

        let pts = match corp.exceptions.iter()
                            .filter_map(|exs| exs.score(self))
                            .next() {
                Some(vec) => Cow::Borrowed(vec),
                None => Cow::Owned(corp.patterns.score(self))
        };

        let (l, r) = (corp.left_min, pts.len() - corp.right_min + 1);

        self.char_indices().skip(l)
            .zip(&pts[l .. r])
            .filter(|&(_, p)| p % 2 != 0)
            .map(|((i, _), _)| i)
            .collect()
    }

    fn hyphenate(self, corp: &Corpus) -> Standard<'a> {
        let os = self.opportunities(corp);

        Standard {
            word: self,
            opportunities: os,
            prior: 0,
            i: 0
        }
    }
}
<commit_msg>Rename  field<commit_after>//! Hyphenating iterators.

use std::borrow::Cow;

use language::{Corpus};
use utilia::{Interspersable, Intersperse};

pub trait Hyphenation<Hyphenator> where Hyphenator : Iterator {
    /// Returns the byte indices of valid hyphenation points within a word.
    fn opportunities(self, corp: &Corpus) -> Vec<usize>;

    /// Returns an iterator over orthographic syllables separated by valid
    /// hyphenation points.
    ///
    /// Note that, in some orthographies, the syllables of a hyphenated word
    /// are not necessarily substrings of the original word.
    fn hyphenate(self, corp: &Corpus) -> Hyphenator;
}

/// The `Standard` hyphenator iterates over a word, returning slices
/// delimited by string boundaries and valid hyphenation points.
#[derive(Clone, Debug)]
pub struct Standard<'a> {
    text: &'a str,
    opportunities: Vec<usize>,
    prior: usize,
    i: usize
}

impl<'a> Standard<'a> {
    // Inserts a soft hyphen at hyphenation points.
    pub fn punctuate(self) -> Intersperse<Self> {
        self.intersperse("\u{ad}")
    }
}

impl<'a> Iterator for Standard<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        let start = self.prior;
        let i = self.i;

        match self.opportunities.get(i) {
            Some(&end) => {
                self.prior = end;
                self.i = i + 1;
                Some(&self.text[start .. end])
            },
            None => {
                if i <= self.opportunities.len() {
                    self.i = i + 1;
                    Some(&self.text[start ..])
                } else {
                    None
                }
            }
        }
    }
}

impl<'a> Hyphenation<Standard<'a>> for &'a str {
    fn opportunities(self, corp: &Corpus) -> Vec<usize> {
        if self.chars().count() < corp.left_min + corp.right_min {
            return vec![];
        }

        let pts = match corp.exceptions.iter()
                            .filter_map(|exs| exs.score(self))
                            .next() {
                Some(vec) => Cow::Borrowed(vec),
                None => Cow::Owned(corp.patterns.score(self))
        };

        let (l, r) = (corp.left_min, pts.len() - corp.right_min + 1);

        self.char_indices().skip(l)
            .zip(&pts[l .. r])
            .filter(|&(_, p)| p % 2 != 0)
            .map(|((i, _), _)| i)
            .collect()
    }

    fn hyphenate(self, corp: &Corpus) -> Standard<'a> {
        let os = self.opportunities(corp);

        Standard {
            text: self,
            opportunities: os,
            prior: 0,
            i: 0
        }
    }
}
<|endoftext|>


<commit_before>//! Contains various type and trait definition related to connexion management

mod uni;
mod multi;
<commit_msg>net/conn: Reexport UniConn and MultiConn from net::conn<commit_after>//! Contains various type and trait definition related to connexion management

mod uni;
mod multi;

pub use self::uni::UniConn;
pub use self::multi::MultiConn;
<|endoftext|>


<commit_before>extern crate num;
extern crate byteorder;

use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Cursor;
use std::mem;
use std::iter::FromIterator;
use byteorder::{ByteOrder, LittleEndian, BigEndian, NativeEndian};
use std::time::Duration;

type c32 = num::Complex<f32>;

fn kernel1(ai: &mut Vec<f32>, ef: &Vec<c32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[i].re.powi(2) + ef[i].im.powi(2);
    }
}

fn kernel2(ai: &mut Vec<f32>, ef_re: &Vec<f32>, ef_im: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef_re[i]*ef_re[i] + ef_im[i]*ef_im[i];
    }
}

fn main() {
    let arg = env::args_os().nth(1).expect("Please, provide a file as argument");
    let mut file = File::open(&arg).ok().expect(&format!("The file {:?} does not exist", arg));
    let file_info = file.metadata().ok().expect("Cannot get file metadata");
    if !file_info.is_file() {
        panic!("{:?} is not a file", arg);
    }
    println!("the file {:?} has length {} bytes", arg, file_info.len());
    let mut buf = [0u8; 4];
    file.read(&mut buf[..]);
    let L = LittleEndian::read_u32(&buf[..]);
    println!("L : {}", L);
    let size = L as usize;
    let mut grid_buf = Vec::new();
    let sz = file.read_to_end(&mut grid_buf).unwrap();
    let mut ai = Vec::from_iter((0..size).map(|i| {
                let start = i * mem::size_of::<f32>();
                let val = LittleEndian::read_f32(&grid_buf[start .. ]);
                //println!("val : {}",val);
                val
            }));
    let ef = Vec::from_iter((0..size).map(|i| {
                let offset = size * mem::size_of::<f32>();
                let start = offset + i * mem::size_of::<c32>();
                let val_real = LittleEndian::read_f32(&grid_buf[start                         .. ]);
                let val_imag = LittleEndian::read_f32(&grid_buf[start + mem::size_of::<f32>() .. ]);
                //println!("val_real : {}  val_imag : {}",val_real,val_imag);
                c32::new(val_real, val_imag)
            }));

    let ef_re = Vec::from_iter((0..size).map(|i| ef[i].re));
    let ef_im = Vec::from_iter((0..size).map(|i| ef[i].im));

    let rep_count = 10000;
    let mut timer = std::time::Instant::now();

    for i in 0..rep_count {
      kernel1(&mut ai, &ef);
    }

    let d = timer.elapsed();
    println!("elapsed time for kernel1: {:.7}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);

    timer = std::time::Instant::now();

    for i in 0..rep_count {
      kernel2(&mut ai, &ef_re, &ef_im);
    }

    let d = timer.elapsed();
    println!("elapsed time for kernel2: {:.7}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);

    println!("fini");
}
<commit_msg>use `powi` for kernel2.<commit_after>extern crate num;
extern crate byteorder;

use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::io::Cursor;
use std::mem;
use std::iter::FromIterator;
use byteorder::{ByteOrder, LittleEndian, BigEndian, NativeEndian};
use std::time::Duration;

type c32 = num::Complex<f32>;

fn kernel1(ai: &mut Vec<f32>, ef: &Vec<c32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef[i].re.powi(2) + ef[i].im.powi(2);
    }
}

fn kernel2(ai: &mut Vec<f32>, ef_re: &Vec<f32>, ef_im: &Vec<f32>) {
    let size = ai.len();
    for i in 0..size {
        ai[i] += ef_re[i].powi(2) + ef_im[i].powi(2);
    }
}

fn main() {
    let arg = env::args_os().nth(1).expect("Please, provide a file as argument");
    let mut file = File::open(&arg).ok().expect(&format!("The file {:?} does not exist", arg));
    let file_info = file.metadata().ok().expect("Cannot get file metadata");
    if !file_info.is_file() {
        panic!("{:?} is not a file", arg);
    }
    println!("the file {:?} has length {} bytes", arg, file_info.len());
    let mut buf = [0u8; 4];
    file.read(&mut buf[..]);
    let L = LittleEndian::read_u32(&buf[..]);
    println!("L : {}", L);
    let size = L as usize;
    let mut grid_buf = Vec::new();
    let sz = file.read_to_end(&mut grid_buf).unwrap();
    let mut ai = Vec::from_iter((0..size).map(|i| {
                let start = i * mem::size_of::<f32>();
                let val = LittleEndian::read_f32(&grid_buf[start .. ]);
                //println!("val : {}",val);
                val
            }));
    let ef = Vec::from_iter((0..size).map(|i| {
                let offset = size * mem::size_of::<f32>();
                let start = offset + i * mem::size_of::<c32>();
                let val_real = LittleEndian::read_f32(&grid_buf[start                         .. ]);
                let val_imag = LittleEndian::read_f32(&grid_buf[start + mem::size_of::<f32>() .. ]);
                //println!("val_real : {}  val_imag : {}",val_real,val_imag);
                c32::new(val_real, val_imag)
            }));

    let ef_re = Vec::from_iter((0..size).map(|i| ef[i].re));
    let ef_im = Vec::from_iter((0..size).map(|i| ef[i].im));

    let rep_count = 10000;
    let mut timer = std::time::Instant::now();

    for i in 0..rep_count {
      kernel1(&mut ai, &ef);
    }

    let d = timer.elapsed();
    println!("elapsed time for kernel1: {:.7}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);

    timer = std::time::Instant::now();

    for i in 0..rep_count {
      kernel2(&mut ai, &ef_re, &ef_im);
    }

    let d = timer.elapsed();
    println!("elapsed time for kernel2: {:.7}s.", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);

    println!("fini");
}
<|endoftext|>


<commit_before>// A Map struct that combines Tiles and Units for convenience
// This struct contains all the stuff that is saved/loaded

use super::units::{UnitSide, UnitType, Units};
use super::tiles::{Visibility, Tiles};
use items::{Item, BANDAGE_HEAL};
use weapons::{Weapon, WeaponType};

use std::fs::{File, create_dir_all};
use std::path::{Path, PathBuf};

use bincode;

const SIZE_LIMIT: bincode::Infinite = bincode::Infinite;
const EXTENSION: &str = ".sav";
const SAVES: &str = "savegames/skirmishes";
const AUTOSAVE: &str = "autosave.sav";

// The Map struct
#[derive(Serialize, Deserialize)]
pub struct Map {
    pub units: Units,
    pub tiles: Tiles,
    pub turn: u8
}

impl Map {
    // Create a new map
    pub fn new() -> Map {
        Map {
            units: Units::new(),
            tiles: Tiles::new(),
            turn: 1
        }
    }

    // Work out if a tile is taken or not
    pub fn taken(&self, x: usize, y: usize) -> bool {
        !self.tiles.at(x, y).walkable() || self.units.at(x, y).is_some()
    }

    // Work out how many units of a particular side are visible to the other side
    pub fn visible(&self, side: UnitSide) -> usize {
        self.units.iter()
            .filter(|unit| unit.side == side && match side {
                UnitSide::Player => self.tiles.at(unit.x, unit.y).ai_visibility,
                UnitSide::AI => self.tiles.at(unit.x, unit.y).player_visibility
            } == Visibility::Visible)
            .count()
    }

    // Get a unit to drop an item
    pub fn drop_item(&mut self, unit: u8, index: usize) {
        if let Some(unit) = self.units.get_mut(unit) {
            if let Some(item) = unit.inventory.get(index).cloned() {
                self.tiles.drop(unit.x, unit.y, item);
                unit.inventory.remove(index);
            }
        }
    }

    // Get a unit to pick up am item
    pub fn pick_up_item(&mut self, unit: u8, index: usize) {
        if let Some(unit) = self.units.get_mut(unit) {
            let tile = self.tiles.at_mut(unit.x, unit.y);

            if let Some(item) = tile.items.get(index).cloned() {
                let new_weight = unit.inventory.iter().fold(
                    item.weight() + unit.weapon.tag.weight(),
                    |total, item| total + item.weight()
                );

                if new_weight <= unit.tag.capacity() {
                    unit.inventory.push(item);
                    tile.items.remove(index);
                }
            }
        }
    }

    // Get a unit to use an item in its inventory
    pub fn use_item(&mut self, unit: u8, index: usize) -> bool {
        let mut item_consumed = false;
        let mut new_item = None;

        if let Some(unit) = self.units.get_mut(unit) {
            if let Some(item) = unit.inventory.get(index) {
                match (*item, unit.weapon.tag) {
                    // Reload the corresponding weapon
                    (Item::RifleClip(ammo), WeaponType::Rifle) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    (Item::MachineGunClip(ammo), WeaponType::MachineGun) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    (Item::PlasmaClip(ammo), WeaponType::PlasmaRifle) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    // Switch weapons
                    (Item::Rifle(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::Rifle, ammo);
                        item_consumed = true;
                    },
                    (Item::MachineGun(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::MachineGun, ammo);
                        item_consumed = true;
                    },
                    (Item::PlasmaRifle(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::PlasmaRifle, ammo);
                        item_consumed = true;
                    },
                    // Use other items
                    (Item::Bandages, _) => if let UnitType::Squaddie = unit.tag {
                        if unit.tag.health() - unit.health >= BANDAGE_HEAL {
                            unit.health += BANDAGE_HEAL;
                            item_consumed = true;
                        }
                    }
                    _ => {}
                }
            }

            // If the item was consumed, remove it from the inventory
            if item_consumed {
                unit.inventory.remove(index);
            }

            // If a new item was created, add it to the inventory
            if let Some(item) = new_item {
                unit.inventory.push(item);
            }
        }

        // return true if an item was consumed and no item took its place
        item_consumed && new_item.is_none()
    }

    // Load a skirmish if possible
    pub fn load(filename: &str) -> Option<Map> {
        let path = Path::new(SAVES).join(filename);

        File::open(path).ok()
            .and_then(|mut file| bincode::deserialize_from(&mut file, SIZE_LIMIT).ok())
    }

    // Save the skirmish
    pub fn save(&self, filename: Option<String>) -> Option<PathBuf> {
        // Push the extension onto the filename if it is given or use the default filename
        let filename = filename.map(|mut filename| {
            filename.push_str(EXTENSION);
            filename
        }).unwrap_or_else(|| AUTOSAVE.into());

        // Don't save invisible files
        if filename.starts_with('.') {
            return None;
        }

        // Create the directory

        let directory = Path::new(SAVES);

        if !directory.exists() && create_dir_all(&directory).is_err() {
            return None;
        }

        // Save the game and return the path

        let save = directory.join(filename);

        File::create(&save).ok()
            .and_then(|mut file| bincode::serialize_into(&mut file, self, SIZE_LIMIT).ok())
            .map(|_| save)
    }
}<commit_msg>Fixed comma error<commit_after>// A Map struct that combines Tiles and Units for convenience
// This struct contains all the stuff that is saved/loaded

use super::units::{UnitSide, UnitType, Units};
use super::tiles::{Visibility, Tiles};
use items::{Item, BANDAGE_HEAL};
use weapons::{Weapon, WeaponType};

use std::fs::{File, create_dir_all};
use std::path::{Path, PathBuf};

use bincode;

const SIZE_LIMIT: bincode::Infinite = bincode::Infinite;
const EXTENSION: &str = ".sav";
const SAVES: &str = "savegames/skirmishes";
const AUTOSAVE: &str = "autosave.sav";

// The Map struct
#[derive(Serialize, Deserialize)]
pub struct Map {
    pub units: Units,
    pub tiles: Tiles,
    pub turn: u8
}

impl Map {
    // Create a new map
    pub fn new() -> Map {
        Map {
            units: Units::new(),
            tiles: Tiles::new(),
            turn: 1
        }
    }

    // Work out if a tile is taken or not
    pub fn taken(&self, x: usize, y: usize) -> bool {
        !self.tiles.at(x, y).walkable() || self.units.at(x, y).is_some()
    }

    // Work out how many units of a particular side are visible to the other side
    pub fn visible(&self, side: UnitSide) -> usize {
        self.units.iter()
            .filter(|unit| unit.side == side && match side {
                UnitSide::Player => self.tiles.at(unit.x, unit.y).ai_visibility,
                UnitSide::AI => self.tiles.at(unit.x, unit.y).player_visibility
            } == Visibility::Visible)
            .count()
    }

    // Get a unit to drop an item
    pub fn drop_item(&mut self, unit: u8, index: usize) {
        if let Some(unit) = self.units.get_mut(unit) {
            if let Some(item) = unit.inventory.get(index).cloned() {
                self.tiles.drop(unit.x, unit.y, item);
                unit.inventory.remove(index);
            }
        }
    }

    // Get a unit to pick up am item
    pub fn pick_up_item(&mut self, unit: u8, index: usize) {
        if let Some(unit) = self.units.get_mut(unit) {
            let tile = self.tiles.at_mut(unit.x, unit.y);

            if let Some(item) = tile.items.get(index).cloned() {
                let new_weight = unit.inventory.iter().fold(
                    item.weight() + unit.weapon.tag.weight(),
                    |total, item| total + item.weight()
                );

                if new_weight <= unit.tag.capacity() {
                    unit.inventory.push(item);
                    tile.items.remove(index);
                }
            }
        }
    }

    // Get a unit to use an item in its inventory
    pub fn use_item(&mut self, unit: u8, index: usize) -> bool {
        let mut item_consumed = false;
        let mut new_item = None;

        if let Some(unit) = self.units.get_mut(unit) {
            if let Some(item) = unit.inventory.get(index) {
                match (*item, unit.weapon.tag) {
                    // Reload the corresponding weapon
                    (Item::RifleClip(ammo), WeaponType::Rifle) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    (Item::MachineGunClip(ammo), WeaponType::MachineGun) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    (Item::PlasmaClip(ammo), WeaponType::PlasmaRifle) => if unit.weapon.can_reload(ammo) {
                        unit.weapon.ammo += ammo;
                        item_consumed = true;
                    },
                    // Switch weapons
                    (Item::Rifle(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::Rifle, ammo);
                        item_consumed = true;
                    },
                    (Item::MachineGun(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::MachineGun, ammo);
                        item_consumed = true;
                    },
                    (Item::PlasmaRifle(ammo), _) => {
                        new_item = Some(unit.weapon.to_item());
                        unit.weapon = Weapon::new(WeaponType::PlasmaRifle, ammo);
                        item_consumed = true;
                    },
                    // Use other items
                    (Item::Bandages, _) => if let UnitType::Squaddie = unit.tag {
                        if unit.tag.health() - unit.health >= BANDAGE_HEAL {
                            unit.health += BANDAGE_HEAL;
                            item_consumed = true;
                        }
                    },
                    _ => {}
                }
            }

            // If the item was consumed, remove it from the inventory
            if item_consumed {
                unit.inventory.remove(index);
            }

            // If a new item was created, add it to the inventory
            if let Some(item) = new_item {
                unit.inventory.push(item);
            }
        }

        // return true if an item was consumed and no item took its place
        item_consumed && new_item.is_none()
    }

    // Load a skirmish if possible
    pub fn load(filename: &str) -> Option<Map> {
        let path = Path::new(SAVES).join(filename);

        File::open(path).ok()
            .and_then(|mut file| bincode::deserialize_from(&mut file, SIZE_LIMIT).ok())
    }

    // Save the skirmish
    pub fn save(&self, filename: Option<String>) -> Option<PathBuf> {
        // Push the extension onto the filename if it is given or use the default filename
        let filename = filename.map(|mut filename| {
            filename.push_str(EXTENSION);
            filename
        }).unwrap_or_else(|| AUTOSAVE.into());

        // Don't save invisible files
        if filename.starts_with('.') {
            return None;
        }

        // Create the directory

        let directory = Path::new(SAVES);

        if !directory.exists() && create_dir_all(&directory).is_err() {
            return None;
        }

        // Save the game and return the path

        let save = directory.join(filename);

        File::create(&save).ok()
            .and_then(|mut file| bincode::serialize_into(&mut file, self, SIZE_LIMIT).ok())
            .map(|_| save)
    }
}<|endoftext|>


<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::{def_id::DefId, Span, DUMMY_SP};

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, span: Span, msg: impl AsRef<str>) {
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
        );
    }

    fn check_bb(&self, span: Span, bb: BasicBlock) {
        if self.body.basic_blocks().get(bb).is_none() {
            self.fail(span, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
                self.fail(
                    DUMMY_SP,
                    format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location),
                );
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            DUMMY_SP,
                            format!(
                                "encountered `Assign` statement with overlapping memory at {:?}",
                                location
                            ),
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(terminator.source_info.span, *target);
            }
            TerminatorKind::SwitchInt { targets, .. } => {
                if targets.is_empty() {
                    self.fail(
                        terminator.source_info.span,
                        "encountered `SwitchInt` terminator with no target to jump to",
                    );
                }
                for target in targets {
                    self.check_bb(terminator.source_info.span, *target);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(terminator.source_info.span, *target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(terminator.source_info.span, *target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        terminator.source_info.span,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(terminator.source_info.span, *target);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(terminator.source_info.span, *cleanup);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        terminator.source_info.span,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(terminator.source_info.span, *target);
                if let Some(cleanup) = cleanup {
                    self.check_bb(terminator.source_info.span, *cleanup);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(terminator.source_info.span, *resume);
                if let Some(drop) = drop {
                    self.check_bb(terminator.source_info.span, *drop);
                }
            }
            TerminatorKind::FalseEdges { real_target, imaginary_target } => {
                self.check_bb(terminator.source_info.span, *real_target);
                self.check_bb(terminator.source_info.span, *imaginary_target);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(terminator.source_info.span, *real_target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(terminator.source_info.span, *destination);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>replace DUMMY_SP by proper span<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::{def_id::DefId, Span, DUMMY_SP};

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, span: Span, msg: impl AsRef<str>) {
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
        );
    }

    fn check_bb(&self, span: Span, bb: BasicBlock) {
        if self.body.basic_blocks().get(bb).is_none() {
            self.fail(span, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(span, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            DUMMY_SP,
                            format!(
                                "encountered `Assign` statement with overlapping memory at {:?}",
                                location
                            ),
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(terminator.source_info.span, *target);
            }
            TerminatorKind::SwitchInt { targets, .. } => {
                if targets.is_empty() {
                    self.fail(
                        terminator.source_info.span,
                        "encountered `SwitchInt` terminator with no target to jump to",
                    );
                }
                for target in targets {
                    self.check_bb(terminator.source_info.span, *target);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(terminator.source_info.span, *target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(terminator.source_info.span, *target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        terminator.source_info.span,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(terminator.source_info.span, *target);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(terminator.source_info.span, *cleanup);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        terminator.source_info.span,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(terminator.source_info.span, *target);
                if let Some(cleanup) = cleanup {
                    self.check_bb(terminator.source_info.span, *cleanup);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(terminator.source_info.span, *resume);
                if let Some(drop) = drop {
                    self.check_bb(terminator.source_info.span, *drop);
                }
            }
            TerminatorKind::FalseEdges { real_target, imaginary_target } => {
                self.check_bb(terminator.source_info.span, *real_target);
                self.check_bb(terminator.source_info.span, *imaginary_target);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(terminator.source_info.span, *real_target);
                if let Some(unwind) = unwind {
                    self.check_bb(terminator.source_info.span, *unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(terminator.source_info.span, *destination);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { *kill_tx = k_tx.clone(); }

    // Writer thread's channel
    let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    unsafe { *writer_tx = w_tx.clone(); }

    debug!("Attempting connect to: {}", host_address);

    let result = TcpStream::connect(host_address);
    if result.is_err() {
        error!("Error connecting to {} - {}", host_address, result.unwrap_err());
        return -1 as c_int;
    }
    debug!("Connected");
    on_connect_handler();

    let stream = result.unwrap();
    let client = Bstream::new(stream);
    let r_client = client.clone();
    let w_client = client.clone();

    // Start the reader thread
    thread::Builder::new()
        .name("ReaderThread".to_string())
        .spawn(move||{
            reader_thread(r_client, data_handler)
        }).unwrap();

    // Start the writer thread
    thread::Builder::new()
        .name("WriterThread".to_string())
        .spawn(move||{
            writer_thread(writer_rx, w_client)
        }).unwrap();

    // Wait for the kill signal
    match kill_rx.recv() {
        Ok(_) => { }
        Err(e) => {
            error!("Error on kill channel: {}", e);
            on_disconnect_handler();
            return -1 as c_int;
        }
    };
    on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    // super::init();
    //
    // trace!("Rust - start()");
    //
    // let mut r_address;
    // unsafe {
    //     r_address = CStr::from_ptr(address);
    // }
    // let s_address = r_address.to_bytes();
    // let host_address = match str::from_utf8(s_address) {
    //     Ok(safe_str) => safe_str,
    //     Err(_) => {
    //         error!("Invalid host address");
    //         return -1 as c_int;
    //     }
    // };
    //
    // // Create and register a way to kill this client
    // let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    // unsafe { *kill_tx = k_tx.clone(); }
    //
    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Fabian Schuiki

//! This crate contains the fundamental utilities used to by the rest of the
//! moore compiler.

extern crate rustc_serialize;
extern crate memmap;
#[macro_use]
extern crate bitflags;

pub mod errors;
pub mod lexer;
pub mod name;
pub mod source;
pub mod grind;
pub mod id;
pub mod score;
pub mod util;

pub use self::id::NodeId;
use errors::{DiagBuilder2, DiagEmitter, Severity};
use std::cell::Cell;

pub struct Session {
	pub opts: SessionOptions,
	/// Whether any error diagnostics were produced.
	pub failed: Cell<bool>,
}

impl Session {
	/// Create a new session.
	pub fn new() -> Session {
		Session {
			opts: Default::default(),
			failed: Cell::new(false),
		}
	}
}

impl DiagEmitter for Session {
	fn emit(&self, diag: DiagBuilder2) {
		if diag.severity >= Severity::Error {
			self.failed.set(true);
		}
		eprintln!("{}", diag);
	}
}

/// A set of options for a session.
///
/// The arguments passed on the command line are intended to modify these values
/// in order to configure the execution of the program.
#[derive(Debug, Default)]
pub struct SessionOptions {
	pub ignore_duplicate_defs: bool,
	/// Print a trace of scoreboard invocations for debugging purposes.
	pub trace_scoreboard: bool,
	/// The verbosity options.
	pub verbosity: Verbosity,
}

bitflags! {
	/// A set of verbosity options for a session.
	///
	/// These flags control how much information the compiler emits.
	#[derive(Default)]
	pub struct Verbosity: u8 {
		const TYPES         = 0b00001;
		const EXPR_TYPES    = 0b00010;
		const TYPE_CONTEXTS = 0b00100;
		const TYPECK        = 0b01000;
		const NAMES         = 0b10000;
	}
}
<commit_msg>common: add session context<commit_after>// Copyright (c) 2017 Fabian Schuiki

//! This crate contains the fundamental utilities used to by the rest of the
//! moore compiler.

#[macro_use]
extern crate bitflags;
extern crate memmap;
extern crate rustc_serialize;

pub mod errors;
pub mod lexer;
pub mod name;
pub mod source;
pub mod grind;
pub mod id;
pub mod score;
pub mod util;

pub use self::id::NodeId;
use errors::{DiagBuilder2, DiagEmitter, Severity};
use std::cell::Cell;

pub struct Session {
    pub opts: SessionOptions,
    /// Whether any error diagnostics were produced.
    pub failed: Cell<bool>,
}

impl Session {
    /// Create a new session.
    pub fn new() -> Session {
        Session {
            opts: Default::default(),
            failed: Cell::new(false),
        }
    }
}

impl DiagEmitter for Session {
    fn emit(&self, diag: DiagBuilder2) {
        if diag.severity >= Severity::Error {
            self.failed.set(true);
        }
        eprintln!("{}", diag);
    }
}

impl SessionContext for Session {
    fn has_verbosity(&self, verb: Verbosity) -> bool {
        self.opts.verbosity.contains(verb)
    }
}

/// Access session options and emit diagnostics.
pub trait SessionContext: DiagEmitter {
    /// Check if a verbosity option is set.
    fn has_verbosity(&self, verb: Verbosity) -> bool;
}

/// A set of options for a session.
///
/// The arguments passed on the command line are intended to modify these values
/// in order to configure the execution of the program.
#[derive(Debug, Default)]
pub struct SessionOptions {
    pub ignore_duplicate_defs: bool,
    /// Print a trace of scoreboard invocations for debugging purposes.
    pub trace_scoreboard: bool,
    /// The verbosity options.
    pub verbosity: Verbosity,
}

bitflags! {
    /// A set of verbosity options for a session.
    ///
    /// These flags control how much information the compiler emits.
    #[derive(Default)]
    pub struct Verbosity: u8 {
        const TYPES         = 0b00001;
        const EXPR_TYPES    = 0b00010;
        const TYPE_CONTEXTS = 0b00100;
        const TYPECK        = 0b01000;
        const NAMES         = 0b10000;
    }
}
<|endoftext|>