<commit_before>use std::ops;
use std::mem;

use self::Node::*;

/// A `Node` in the `Rope`'s tree.
///
/// A `Node` is either a `Leaf` holding a `String`, or a
/// a `Branch` concatenating together two `Node`s.
#[derive(Debug, Clone)]
pub enum Node {
    /// A leaf node
    Leaf(String)
  , /// A branch concatenating together `l`eft and `r`ight nodes.
    Branch(BranchNode)
}

#[derive(Debug, Clone)]
pub struct BranchNode {
    /// The length of this node
    len: usize
  , /// The weight of a node is the summed weight of its left subtree
    weight: usize
  , /// The left branch node
    pub left: Box<Node>
  , /// The right branch node
    pub right: Box<Node>
}

impl Default for Node {
    fn default() -> Self { Node::empty() }
}

/// Returns the _n_th fibonacci number.
// TODO: replace with an iterative implementation and/or lookup table?
fn fibonacci(n: usize) -> usize {
    match n { 0 => 1
            , 1 => 1
            , _ => fibonacci(n - 1) + fibonacci(n - 2)
            }
}

impl BranchNode {

    #[inline]
    fn new(left: Node, right: Node) -> Self {
        BranchNode { len: left.len() + right.len()
                   , weight: left.subtree_weight()
                   , left: box left
                   , right: box right
                   }
    }

    fn split(self, index: usize) -> (Node, Node) {
        let weight = (&self).weight;
        if index < weight {
            // split the left node
            let (left, left_right) = self.left.split(index);
            let right = if (&left_right).len() == 0 {
                *self.right
            } else {
                Node::new_branch(left_right, *self.right)
            };
            (left, right)
        } else {
            // split the right node;
            let (right_left, right) = self.right.split(index - weight);
            let left = if (&right_left).len() == 0 {
                *self.left
            } else {
                Node::new_branch(*self.left, right_left)
            };
            (left, right)
        }
    }

}

impl Node {

    pub fn split(self, index: usize) -> (Node, Node) {
        match self {
            Leaf(s) => if s.len() == 0 {
                (Node::empty(), Node::empty())
            } else {
                // split the string into left and right parts...
                let left = Leaf(s[..index].to_string());
                let right = Leaf(s[index..].to_string());
                (left, right)
            }
          , Branch(node) => node.split(index)
        }
    }

    #[inline]
    pub fn empty() -> Self {
        Leaf(String::new())
    }

    /// Concatenate two `Node`s to return a new `Branch` node.
    #[inline]
    pub fn new_branch(left: Self, right: Self) -> Self {
        Branch(BranchNode::new(left, right))
    }

    #[inline]
    pub fn concat(&mut self, right: Self) {
        *self = Node::new_branch(mem::replace(self, Node::empty()), right)
    }

    #[inline]
    pub const fn new_leaf(string: String) -> Self {
        Leaf(string)
    }

    /// Returns true if this node is balanced
    ///
    /// "We define the depth of a leaf to be 0, and the depth of a
    /// concatenation to be one plus the maximum depth of its children. Let
    /// _Fn_ be the _n_th Fibonacci number. A rope of depth _n_ is balanced if
    /// its length is at least _Fn_+2, e.g. a balanced rope of depth 1 must
    /// have length at least 2. Note that balanced ropes may contain unbalanced
    /// subropes."
    /// – from "Ropes: An Alternative to Strings"
    #[inline]
    pub fn is_balanced(&self) -> bool {
        self.len() >= fibonacci(self.depth() + 2)
    }

    /// Returns the depth in the tree of a node
    #[inline]
    fn depth(&self) -> usize {
        use std::cmp::max;

        match self {
            &Node::Leaf(_) => 0
          , &Node::Branch(BranchNode { ref left, ref right, .. }) =>
                max(left.depth(), right.depth()) + 1
            }
    }

    /// Returns the length of a node
    //  TODO: do we want to cache this?
    pub fn len(&self) -> usize {
        match self { &Leaf(ref s) => s.len()
                   , &Branch(BranchNode { ref left, ref right, .. }) =>
                        left.len() + right.len()
                    }
    }

    /// Calculates the weight of a node
    #[inline]
    fn subtree_weight (&self) -> usize {
        match self { &Leaf(ref s) => s.len()
                   , &Branch(BranchNode { ref left, .. }) => left.len()
                    }
    }

    /// Rebalance the subrope starting at this `Node`, returning a new `Node`
    ///
    /// From "Ropes: An Alternative to Strings":
    /// > "The rebalancing operation maintains an ordered sequence of (empty
    /// > or) balanced ropes, one for each length interval [_Fn_, _Fn_+1), for
    /// > _n_ >= 2. We traverse the rope from left to right, inserting each
    /// > leaf into the appropriate sequence position, depending on its length.
    ///
    /// > The concatenation of the sequence of ropes in order of decreasing
    /// > length is equivalent to the prefix of the rope we have traversed so
    /// > far. Each new leaf _x_ is with_insert_ropeed into the appropriate entry of the
    /// > sequence. Assume that _x_’s length is in the interval [_Fn_, _Fn_+1),
    /// > and thus it should be put in slot _n_ (which also corresponds to
    /// > maximum depth _n_ − 2). If all lower and equal numbered levels are
    /// > empty, then this can be done directly. If not, then we concatenate
    /// > ropes in slots 2, ... ,(_n_ − 1) (concatenating onto the left), and
    /// > concatenate _x_ to the right of the result. We then continue to
    /// > concatenate ropes from the sequence in increasing order to the left
    /// > of this result, until the result fits into an empty slot in the
    /// > sequence."
    pub fn rebalance(self) -> Self {
        // TODO: this is a huge mess, I based it on the IBM Java implementation
        //       please refactor until it stops being ugly!
        //        - eliza, 12/17/2016

        if self.is_balanced() {
            // the subrope is already balanced, do nothing
            self
        } else {
            let mut leaves: Vec<Option<Node>> =
                self.into_leaves().map(Option::Some).collect();
            let len = leaves.len();
            fn _rebalance(l: &mut Vec<Option<Node>>, start: usize, end: usize)
                          -> Node {
                match end - start {
                    1 => l[start].take().unwrap()
                  , 2 => l[start].take().unwrap() + l[start + 1].take().unwrap()
                  , n => {
                        let mid = start + (n / 2);
                        _rebalance(l, start, mid) + _rebalance(l, mid, end)

                    }
                }
            };
            _rebalance(&mut leaves, 0, len)
        }
    }

    /// Returns an iterator over all leaf nodes in this `Node`'s subrope
    fn leaves<'a>(&'a self) -> Leaves<'a> {
        Leaves(vec![self])
    }

    /// Returns a move iterator over all leaf nodes in this `Node`'s subrope
    fn into_leaves(self) -> IntoLeaves {
        IntoLeaves(vec![self])
    }

    /// Returns an iterator over all the strings in this `Node`s subrope'
    #[inline]
    pub fn strings<'a>(&'a self) -> impl Iterator<Item=&'a str> {
        box self.leaves().map(|n| match n {
            &Leaf(ref s) => s.as_ref()
          , _ => unreachable!("Node.leaves() iterator contained something \
                               that wasn't a leaf. Barring _force majeure_, \
                               this should be impossible. Something's broken.")
        })
    }

    /// Returns a move iterator over all the strings in this `Node`s subrope'
    ///
    /// Consumes `self`.
    #[inline]
    pub fn into_strings(self) -> impl Iterator<Item=String> {
        box self.into_leaves().map(|n| match n {
            Leaf(s) => s
            , _ => unreachable!("Node.into_leaves() iterator contained \
                                 something  that wasn't a leaf. Barring _force \
                                 majeure_, this should be impossible. \
                                 Something's broken.")
        })
    }

    str_iters! {
        #[doc="Returns an iterator over all the bytes in this `Node`'s \
               subrope \n\

               \nAs a Rope consists of a sequence of bytes, we can iterate \
               through a rope by byte. This method returns such an iterator."]
        #[inline]
        impl bytes<u8> for Node {}
        #[doc="Returns an iterator over all the characters in this `Node`'s \
               subrope \n\

               \nAs a `Rope` consists of valid UTF-8, we can iterate through a \
               `Rope` by `char`. This method returns such an iterator. \n\

               \nIt's important to remember that `char` represents a Unicode \
               Scalar Value, and may not match your idea of what a \
               'character' is. Iteration over grapheme clusters may be what \
               you actually want."]
        #[inline]
        impl chars<char> for Node {}
        // TODO: this is actually Wrong, the indices will wrap around once we
        //       iterate into the next leaf node. we'll need to write our own
        //       char_indices iterator that tracks the character's index in the
        //       global Rope. shouldn't be too hard, just a fold...
        //          - eliza, 12/18/2016
        // #[inline]
        // impl char_indices<(usize, char)> for Node {}
        #[inline]
        impl split_whitespace<&'a str> for Node {}
        #[inline]
        impl lines<&'a str> for Node {}
    }

    // /// Returns n iterator over the bytes of this `Node`'s subrope
    // ///
    // ///
    // #[inline]
    // pub fn bytes<'a>(&'a self) -> impl Iterator<Item=u8> + 'a {
    //     self.strings().flat_map(str::bytes)
    // }

    /// Returns an iterator over the grapheme clusters of this `Node`'s subrope'
    ///
    /// This is the iterator returned by `Node::into_iter`.
    #[inline]
    pub fn graphemes<'a>(&'a self) -> impl Iterator<Item=&'a str> {
        // the compiler won't let me mark this as unimplemented using the
        // unimplemented!() macro, due to Reasons (i suspect relating to
        // returning `impl Trait`)
        //  - eliza, 12/18/2016
        panic!("Unimplemented!");
        self.strings()
    }

}

/// An iterator over a series of leaf `Node`s
struct Leaves<'a>(Vec<&'a Node>);

impl<'a> Iterator for Leaves<'a> {
    type Item = &'a Node;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0.pop() {
            None => None
          , Some(leaf @ &Leaf(_)) => Some(leaf)
          , Some(&Branch(BranchNode { ref left, ref right, .. })) => {
                self.0.push(right);
                self.0.push(left);
                self.next()
            }
        }
    }
}

/// A move iterator over a series of leaf `Node`s
struct IntoLeaves(Vec<Node>);

impl Iterator for IntoLeaves {
    type Item = Node;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0.pop() {
            None => None
          , Some(leaf @ Leaf(_)) => Some(leaf)
          , Some(Branch(BranchNode { box left, box right, .. })) => {
                self.0.push(right);
                self.0.push(left);
                self.next()
            }
        }
    }
}

impl ops::Add for Node {
    type Output = Self;
    /// Concatenate two `Node`s, returning a `Branch` node.
    fn add(self, right: Self) -> Self { Node::new_branch(self, right) }
}

impl ops::Index<usize> for Node {
    type Output = str;

    fn index(&self, i: usize) -> &str {
        let len = self.len();
        assert!( i < len
               , "Node::index: index {} out of bounds (length {})", i, len);
        match *self {
            Leaf(ref vec) => { &vec[i..i+1] }
          , Branch(BranchNode { box ref right, .. }) if len < i =>
                &right[i - len]
          , Branch(BranchNode { box ref left, .. }) => &left[i]
        }
    }
}
<commit_msg>rope: add better documentation to Node::split()<commit_after>use std::ops;
use std::mem;

use self::Node::*;

/// A `Node` in the `Rope`'s tree.
///
/// A `Node` is either a `Leaf` holding a `String`, or a
/// a `Branch` concatenating together two `Node`s.
#[derive(Debug, Clone)]
pub enum Node {
    /// A leaf node
    Leaf(String)
  , /// A branch concatenating together `l`eft and `r`ight nodes.
    Branch(BranchNode)
}

#[derive(Debug, Clone)]
pub struct BranchNode {
    /// The length of this node
    len: usize
  , /// The weight of a node is the summed weight of its left subtree
    weight: usize
  , /// The left branch node
    pub left: Box<Node>
  , /// The right branch node
    pub right: Box<Node>
}

impl Default for Node {
    fn default() -> Self { Node::empty() }
}

/// Returns the _n_th fibonacci number.
// TODO: replace with an iterative implementation and/or lookup table?
fn fibonacci(n: usize) -> usize {
    match n { 0 => 1
            , 1 => 1
            , _ => fibonacci(n - 1) + fibonacci(n - 2)
            }
}

impl BranchNode {

    #[inline]
    fn new(left: Node, right: Node) -> Self {
        BranchNode { len: left.len() + right.len()
                   , weight: left.subtree_weight()
                   , left: box left
                   , right: box right
                   }
    }

    fn split(self, index: usize) -> (Node, Node) {
        let weight = (&self).weight;
        if index < weight {
            // if the index to split on is less than this node's weight,
            // we split this node's left child.
            let (left, left_right) = self.left.split(index);
            // the left side of the split left child will become the left side
            // of the split pair.

            let right = if (&left_right).len() == 0 {
                // if the right side of the split is empty, then the right
                // side of the returned pair is just this node's right child
                *self.right
            } else {
                // otherwise, the right side of the returned pair is a branch
                // containing the right side of the split node on the left,
                // and this node's right child on the right
                Node::new_branch(left_right, *self.right)
            };
            (left, right)
        } else {
            // if the index is not less than this node's weight, we split the
            // right child
            let (right_left, right) = self.right.split(index - weight);
            // the right side of the split right child will become the right
            // side of the split

            let left = if (&right_left).len() == 0 {
                // if the left side of the split right child is empty, then the
                // left side of the returned pair is just this node's left child
                *self.left
            } else {
                // otherwise, the left side of the returned pair is a branch
                // containing the left side of the split node on the right,
                // and this node's left child on the left
                Node::new_branch(*self.left, right_left)
            };
            (left, right)
        }
    }

}

impl Node {

    pub fn split(self, index: usize) -> (Node, Node) {
        match self {
            Leaf(s) => if s.len() == 0 {
                (Node::empty(), Node::empty())
            } else {
                // split the string into left and right parts...
                let left = Leaf(s[..index].to_string());
                let right = Leaf(s[index..].to_string());
                (left, right)
            }
          , Branch(node) => node.split(index)
        }
    }

    #[inline]
    pub fn empty() -> Self {
        Leaf(String::new())
    }

    /// Concatenate two `Node`s to return a new `Branch` node.
    #[inline]
    pub fn new_branch(left: Self, right: Self) -> Self {
        Branch(BranchNode::new(left, right))
    }

    #[inline]
    pub fn concat(&mut self, right: Self) {
        *self = Node::new_branch(mem::replace(self, Node::empty()), right)
    }

    #[inline]
    pub const fn new_leaf(string: String) -> Self {
        Leaf(string)
    }

    /// Returns true if this node is balanced
    ///
    /// "We define the depth of a leaf to be 0, and the depth of a
    /// concatenation to be one plus the maximum depth of its children. Let
    /// _Fn_ be the _n_th Fibonacci number. A rope of depth _n_ is balanced if
    /// its length is at least _Fn_+2, e.g. a balanced rope of depth 1 must
    /// have length at least 2. Note that balanced ropes may contain unbalanced
    /// subropes."
    /// – from "Ropes: An Alternative to Strings"
    #[inline]
    pub fn is_balanced(&self) -> bool {
        self.len() >= fibonacci(self.depth() + 2)
    }

    /// Returns the depth in the tree of a node
    #[inline]
    fn depth(&self) -> usize {
        use std::cmp::max;

        match self {
            &Node::Leaf(_) => 0
          , &Node::Branch(BranchNode { ref left, ref right, .. }) =>
                max(left.depth(), right.depth()) + 1
            }
    }

    /// Returns the length of a node
    //  TODO: do we want to cache this?
    pub fn len(&self) -> usize {
        match self { &Leaf(ref s) => s.len()
                   , &Branch(BranchNode { ref left, ref right, .. }) =>
                        left.len() + right.len()
                    }
    }

    /// Calculates the weight of a node
    #[inline]
    fn subtree_weight (&self) -> usize {
        match self { &Leaf(ref s) => s.len()
                   , &Branch(BranchNode { ref left, .. }) => left.len()
                    }
    }

    /// Rebalance the subrope starting at this `Node`, returning a new `Node`
    ///
    /// From "Ropes: An Alternative to Strings":
    /// > "The rebalancing operation maintains an ordered sequence of (empty
    /// > or) balanced ropes, one for each length interval [_Fn_, _Fn_+1), for
    /// > _n_ >= 2. We traverse the rope from left to right, inserting each
    /// > leaf into the appropriate sequence position, depending on its length.
    ///
    /// > The concatenation of the sequence of ropes in order of decreasing
    /// > length is equivalent to the prefix of the rope we have traversed so
    /// > far. Each new leaf _x_ is with_insert_ropeed into the appropriate entry of the
    /// > sequence. Assume that _x_’s length is in the interval [_Fn_, _Fn_+1),
    /// > and thus it should be put in slot _n_ (which also corresponds to
    /// > maximum depth _n_ − 2). If all lower and equal numbered levels are
    /// > empty, then this can be done directly. If not, then we concatenate
    /// > ropes in slots 2, ... ,(_n_ − 1) (concatenating onto the left), and
    /// > concatenate _x_ to the right of the result. We then continue to
    /// > concatenate ropes from the sequence in increasing order to the left
    /// > of this result, until the result fits into an empty slot in the
    /// > sequence."
    pub fn rebalance(self) -> Self {
        // TODO: this is a huge mess, I based it on the IBM Java implementation
        //       please refactor until it stops being ugly!
        //        - eliza, 12/17/2016

        if self.is_balanced() {
            // the subrope is already balanced, do nothing
            self
        } else {
            let mut leaves: Vec<Option<Node>> =
                self.into_leaves().map(Option::Some).collect();
            let len = leaves.len();
            fn _rebalance(l: &mut Vec<Option<Node>>, start: usize, end: usize)
                          -> Node {
                match end - start {
                    1 => l[start].take().unwrap()
                  , 2 => l[start].take().unwrap() + l[start + 1].take().unwrap()
                  , n => {
                        let mid = start + (n / 2);
                        _rebalance(l, start, mid) + _rebalance(l, mid, end)

                    }
                }
            };
            _rebalance(&mut leaves, 0, len)
        }
    }

    /// Returns an iterator over all leaf nodes in this `Node`'s subrope
    fn leaves<'a>(&'a self) -> Leaves<'a> {
        Leaves(vec![self])
    }

    /// Returns a move iterator over all leaf nodes in this `Node`'s subrope
    fn into_leaves(self) -> IntoLeaves {
        IntoLeaves(vec![self])
    }

    /// Returns an iterator over all the strings in this `Node`s subrope'
    #[inline]
    pub fn strings<'a>(&'a self) -> impl Iterator<Item=&'a str> {
        box self.leaves().map(|n| match n {
            &Leaf(ref s) => s.as_ref()
          , _ => unreachable!("Node.leaves() iterator contained something \
                               that wasn't a leaf. Barring _force majeure_, \
                               this should be impossible. Something's broken.")
        })
    }

    /// Returns a move iterator over all the strings in this `Node`s subrope'
    ///
    /// Consumes `self`.
    #[inline]
    pub fn into_strings(self) -> impl Iterator<Item=String> {
        box self.into_leaves().map(|n| match n {
            Leaf(s) => s
            , _ => unreachable!("Node.into_leaves() iterator contained \
                                 something  that wasn't a leaf. Barring _force \
                                 majeure_, this should be impossible. \
                                 Something's broken.")
        })
    }

    str_iters! {
        #[doc="Returns an iterator over all the bytes in this `Node`'s \
               subrope \n\

               \nAs a Rope consists of a sequence of bytes, we can iterate \
               through a rope by byte. This method returns such an iterator."]
        #[inline]
        impl bytes<u8> for Node {}
        #[doc="Returns an iterator over all the characters in this `Node`'s \
               subrope \n\

               \nAs a `Rope` consists of valid UTF-8, we can iterate through a \
               `Rope` by `char`. This method returns such an iterator. \n\

               \nIt's important to remember that `char` represents a Unicode \
               Scalar Value, and may not match your idea of what a \
               'character' is. Iteration over grapheme clusters may be what \
               you actually want."]
        #[inline]
        impl chars<char> for Node {}
        // TODO: this is actually Wrong, the indices will wrap around once we
        //       iterate into the next leaf node. we'll need to write our own
        //       char_indices iterator that tracks the character's index in the
        //       global Rope. shouldn't be too hard, just a fold...
        //          - eliza, 12/18/2016
        // #[inline]
        // impl char_indices<(usize, char)> for Node {}
        #[inline]
        impl split_whitespace<&'a str> for Node {}
        #[inline]
        impl lines<&'a str> for Node {}
    }

    // /// Returns n iterator over the bytes of this `Node`'s subrope
    // ///
    // ///
    // #[inline]
    // pub fn bytes<'a>(&'a self) -> impl Iterator<Item=u8> + 'a {
    //     self.strings().flat_map(str::bytes)
    // }

    /// Returns an iterator over the grapheme clusters of this `Node`'s subrope'
    ///
    /// This is the iterator returned by `Node::into_iter`.
    #[inline]
    pub fn graphemes<'a>(&'a self) -> impl Iterator<Item=&'a str> {
        // the compiler won't let me mark this as unimplemented using the
        // unimplemented!() macro, due to Reasons (i suspect relating to
        // returning `impl Trait`)
        //  - eliza, 12/18/2016
        panic!("Unimplemented!");
        self.strings()
    }

}

/// An iterator over a series of leaf `Node`s
struct Leaves<'a>(Vec<&'a Node>);

impl<'a> Iterator for Leaves<'a> {
    type Item = &'a Node;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0.pop() {
            None => None
          , Some(leaf @ &Leaf(_)) => Some(leaf)
          , Some(&Branch(BranchNode { ref left, ref right, .. })) => {
                self.0.push(right);
                self.0.push(left);
                self.next()
            }
        }
    }
}

/// A move iterator over a series of leaf `Node`s
struct IntoLeaves(Vec<Node>);

impl Iterator for IntoLeaves {
    type Item = Node;
    fn next(&mut self) -> Option<Self::Item> {
        match self.0.pop() {
            None => None
          , Some(leaf @ Leaf(_)) => Some(leaf)
          , Some(Branch(BranchNode { box left, box right, .. })) => {
                self.0.push(right);
                self.0.push(left);
                self.next()
            }
        }
    }
}

impl ops::Add for Node {
    type Output = Self;
    /// Concatenate two `Node`s, returning a `Branch` node.
    fn add(self, right: Self) -> Self { Node::new_branch(self, right) }
}

impl ops::Index<usize> for Node {
    type Output = str;

    fn index(&self, i: usize) -> &str {
        let len = self.len();
        assert!( i < len
               , "Node::index: index {} out of bounds (length {})", i, len);
        match *self {
            Leaf(ref vec) => { &vec[i..i+1] }
          , Branch(BranchNode { box ref right, .. }) if len < i =>
                &right[i - len]
          , Branch(BranchNode { box ref left, .. }) => &left[i]
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Ilkka Rauta
//
// 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,non_camel_case_types,non_snake_case)]
mod ffi;

use std::path::Path;
use libc::{c_uint,c_ulong};

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod enums {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/demux-enums.rs"));
}

pub use self::enums::{Input,Output,PesType};

pub struct Demux {
    device: DeviceFileDescriptor
}

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult};

impl Demux {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Demux> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Demux { device: device })
    }

    pub fn read(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.read(buffer)
    }

    pub fn start(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_START as c_ulong)
    }

    pub fn stop(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_STOP as c_ulong)
    }

    pub fn set_filter(&self, filter_params: &SectionFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_sct_filter_params {
            pid: filter_params.pid,
            filter: ffi::Struct_dmx_filter {
                filter: filter_params.filter.filter,
                mask: filter_params.filter.mask,
                mode: filter_params.filter.mode,
            },
            timeout: filter_params.timeout,
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_pes_filter(&self, filter_params: &PesFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_pes_filter_params {
            pid: filter_params.pid,
            input: filter_params.input.into(),
            output: filter_params.output.into(),
            pes_type: filter_params.pes_type.into(),
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_PES_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }

    pub fn get_system_time_counter(&self, num: u32) -> DeviceResult<SystemTimeCounter> {
        let mut ffi_stc = ffi::Struct_dmx_stc { num: num as c_uint, base: 0, stc: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_STC as c_ulong, &mut ffi_stc));
        Ok(SystemTimeCounter { base: ffi_stc.base, counter: ffi_stc.stc })
    }

    pub fn get_pes_pids(&self) -> DeviceResult<[u16; 5]> {
        let mut ffi_pids = [0u16; 5];
        try!(self.device.ioctl_pointer(ffi::DMX_GET_PES_PIDS as c_ulong, ffi_pids.as_mut_ptr()));
        Ok(ffi_pids)
    }

    /// This function is undocumented in the official documentation, and doesn't seem to be
    /// actually in use. It is still nevertheless included here, if it is used by out-of-tree DVB
    /// drivers.
    pub fn get_caps(&self) -> DeviceResult<DemuxCaps> {
        let mut ffi_caps = ffi::Struct_dmx_caps { caps: 0, num_decoders: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_CAPS as c_ulong, &mut ffi_caps));
        Ok(DemuxCaps { caps: ffi_caps.caps, num_decoders: ffi_caps.num_decoders as i32 })
    }

    /// Like get_caps(), this doesn't seem to be actively in use in Linux, but is still implemented.
    pub fn set_source(&self, source: enums::DemuxSource) -> DeviceResult<()> {
        let mut ffi_source: ffi::dmx_source_t =  source.into();
        self.device.ioctl_pointer(ffi::DMX_SET_SOURCE as c_ulong, &mut ffi_source)
    }

    pub fn add_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_ADD_PID as c_ulong, &mut ffi_pid)
    }

    pub fn remove_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_REMOVE_PID as c_ulong, &mut ffi_pid)
    }
}

pub struct DemuxCaps {
    pub caps: u32,
    pub num_decoders: i32
}

pub struct SystemTimeCounter {
    pub base: u32,
    pub counter: u64
}

pub struct DemuxFilter {
    pub filter: [u8; ffi::DMX_FILTER_SIZE as usize],
    pub mask: [u8; ffi::DMX_FILTER_SIZE as usize],
    pub mode: [u8; ffi::DMX_FILTER_SIZE as usize],
}

pub struct SectionFilterParams {
    pub pid: u16,
    pub filter: DemuxFilter,
    pub timeout: u32,
    pub flags: flags::SectionFilterFlags
}

pub struct PesFilterParams {
    pub pid: u16,
    pub input: Input,
    pub output: Output,
    pub pes_type: PesType,
    pub flags: flags::PesFilterFlags
}

#[allow(dead_code,non_upper_case_globals)]
pub mod flags {
    use super::ffi as ffi;
    bitflags!{
        flags SectionFilterFlags: u32 {
            const SecCheckCrC = ffi::DMX_CHECK_CRC,
            const SecOneShot = ffi::DMX_ONESHOT,
            const SecImmediateStart = ffi::DMX_IMMEDIATE_START
        }
    }

    bitflags!{
        flags PesFilterFlags: u32 {
            const PesImmediateStart = ffi::DMX_IMMEDIATE_START
        }
    }
}

pub struct Dvr {
    device: DeviceFileDescriptor
}

impl Dvr {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Dvr> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Dvr { device: device })
    }

    pub fn write(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.write(buffer)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }
}

fn set_buffer_size(device: &DeviceFileDescriptor, buffer_size: u32) -> DeviceResult<()> {
    let mut ffi_size = buffer_size as c_ulong;
    device.ioctl_pointer(ffi::DMX_SET_BUFFER_SIZE as c_ulong, &mut ffi_size)
}
<commit_msg>Make DemuxFilter bit more convenient to use.<commit_after>// Copyright 2015 Ilkka Rauta
//
// 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,non_camel_case_types,non_snake_case)]
mod ffi;

use std::path::Path;
use libc::{c_uint,c_ulong};

pub const DEMUX_FILTER_SIZE: usize = ffi::DMX_FILTER_SIZE as usize;

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod enums {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/demux-enums.rs"));
}

pub use self::enums::{Input,Output,PesType};

pub struct Demux {
    device: DeviceFileDescriptor
}

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult};

impl Demux {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Demux> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Demux { device: device })
    }

    pub fn read(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.read(buffer)
    }

    pub fn start(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_START as c_ulong)
    }

    pub fn stop(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_STOP as c_ulong)
    }

    pub fn set_filter(&self, filter_params: &SectionFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_sct_filter_params {
            pid: filter_params.pid,
            filter: ffi::Struct_dmx_filter {
                filter: filter_params.filter.filter,
                mask: filter_params.filter.mask,
                mode: filter_params.filter.mode,
            },
            timeout: filter_params.timeout,
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_pes_filter(&self, filter_params: &PesFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_pes_filter_params {
            pid: filter_params.pid,
            input: filter_params.input.into(),
            output: filter_params.output.into(),
            pes_type: filter_params.pes_type.into(),
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_PES_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }

    pub fn get_system_time_counter(&self, num: u32) -> DeviceResult<SystemTimeCounter> {
        let mut ffi_stc = ffi::Struct_dmx_stc { num: num as c_uint, base: 0, stc: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_STC as c_ulong, &mut ffi_stc));
        Ok(SystemTimeCounter { base: ffi_stc.base, counter: ffi_stc.stc })
    }

    pub fn get_pes_pids(&self) -> DeviceResult<[u16; 5]> {
        let mut ffi_pids = [0u16; 5];
        try!(self.device.ioctl_pointer(ffi::DMX_GET_PES_PIDS as c_ulong, ffi_pids.as_mut_ptr()));
        Ok(ffi_pids)
    }

    /// This function is undocumented in the official documentation, and doesn't seem to be
    /// actually in use. It is still nevertheless included here, if it is used by out-of-tree DVB
    /// drivers.
    pub fn get_caps(&self) -> DeviceResult<DemuxCaps> {
        let mut ffi_caps = ffi::Struct_dmx_caps { caps: 0, num_decoders: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_CAPS as c_ulong, &mut ffi_caps));
        Ok(DemuxCaps { caps: ffi_caps.caps, num_decoders: ffi_caps.num_decoders as i32 })
    }

    /// Like get_caps(), this doesn't seem to be actively in use in Linux, but is still implemented.
    pub fn set_source(&self, source: enums::DemuxSource) -> DeviceResult<()> {
        let mut ffi_source: ffi::dmx_source_t =  source.into();
        self.device.ioctl_pointer(ffi::DMX_SET_SOURCE as c_ulong, &mut ffi_source)
    }

    pub fn add_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_ADD_PID as c_ulong, &mut ffi_pid)
    }

    pub fn remove_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_REMOVE_PID as c_ulong, &mut ffi_pid)
    }
}

pub struct DemuxCaps {
    pub caps: u32,
    pub num_decoders: i32
}

pub struct SystemTimeCounter {
    pub base: u32,
    pub counter: u64
}

pub struct DemuxFilter {
    pub filter: [u8; DEMUX_FILTER_SIZE],
    pub mask: [u8; DEMUX_FILTER_SIZE],
    pub mode: [u8; DEMUX_FILTER_SIZE],
}

impl Default for DemuxFilter {
    fn default() -> DemuxFilter {
        DemuxFilter {
            filter: [0u8; DEMUX_FILTER_SIZE],
            mask: [0u8; DEMUX_FILTER_SIZE],
            mode: [0u8; DEMUX_FILTER_SIZE],
        }
    }
}

pub struct SectionFilterParams {
    pub pid: u16,
    pub filter: DemuxFilter,
    pub timeout: u32,
    pub flags: flags::SectionFilterFlags
}

pub struct PesFilterParams {
    pub pid: u16,
    pub input: Input,
    pub output: Output,
    pub pes_type: PesType,
    pub flags: flags::PesFilterFlags
}

#[allow(dead_code,non_upper_case_globals)]
pub mod flags {
    use super::ffi as ffi;
    bitflags!{
        flags SectionFilterFlags: u32 {
            const SecCheckCrC = ffi::DMX_CHECK_CRC,
            const SecOneShot = ffi::DMX_ONESHOT,
            const SecImmediateStart = ffi::DMX_IMMEDIATE_START
        }
    }

    bitflags!{
        flags PesFilterFlags: u32 {
            const PesImmediateStart = ffi::DMX_IMMEDIATE_START
        }
    }
}

pub struct Dvr {
    device: DeviceFileDescriptor
}

impl Dvr {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Dvr> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Dvr { device: device })
    }

    pub fn write(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.write(buffer)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }
}

fn set_buffer_size(device: &DeviceFileDescriptor, buffer_size: u32) -> DeviceResult<()> {
    let mut ffi_size = buffer_size as c_ulong;
    device.ioctl_pointer(ffi::DMX_SET_BUFFER_SIZE as c_ulong, &mut ffi_size)
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Error Reporting for Anonymous Region Lifetime Errors.
use hir;
use infer::InferCtxt;
use ty::{self, Region};
use infer::region_inference::RegionResolutionError::*;
use infer::region_inference::RegionResolutionError;
use hir::map as hir_map;
use hir::def_id::DefId;

impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
    // This method walks the Type of the function body arguments using
    // `fold_regions()` function and returns the
    // &hir::Arg of the function argument corresponding to the anonymous
    // region and the Ty corresponding to the named region.
    // Currently only the case where the function declaration consists of
    // one named region and one anonymous region is handled.
    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
    // Here, we would return the hir::Arg for y, we return the type &'a
    // i32, which is the type of y but with the anonymous region replaced
    // with 'a, the corresponding bound region and is_first which is true if
    // the hir::Arg is the first argument in the function declaration.
    fn find_arg_with_anonymous_region
        (&self,
         anon_region: Region<'tcx>,
         named_region: Region<'tcx>)
         -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {

        match *anon_region {
            ty::ReFree(ref free_region) => {

                let id = free_region.scope;
                let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
                let body_id = self.tcx.hir.maybe_body_owned_by(node_id).unwrap();
                let mut is_first = false;
                let body = self.tcx.hir.body(body_id);
                body.arguments
                    .iter()
                    .filter_map(|arg| if let Some(tables) = self.in_progress_tables {
                                    let ty = tables.borrow().node_id_to_type(arg.id);
                                    let mut found_anon_region = false;
                                    let new_arg_ty = self.tcx
                                        .fold_regions(&ty,
                                                      &mut false,
                                                      |r, _| if *r == *anon_region {
                                                          found_anon_region = true;
                                                          named_region
                                                      } else {
                                                          r
                                                      });
                                    if found_anon_region {
                                        if body.arguments.iter().nth(0) == Some(&arg) {
                                            is_first = true;
                                        }
                                        return Some((arg,
                                                     new_arg_ty,
                                                     free_region.bound_region,
                                                     is_first));
                                    } else {
                                        None
                                    }
                                } else {
                                    None
                                })
                    .next()
            }
            _ => None,
        }

    }

    // This method generates the error message for the case when
    // the function arguments consist of a named region and an anonymous
    // region and corresponds to `ConcreteFailure(..)`
    pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {

        let (span, sub, sup) = match *error {
            ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
            _ => return false, // inapplicable
        };

        // Determine whether the sub and sup consist of one named region ('a)
        // and one anonymous (elided) region. If so, find the parameter arg
        // where the anonymous region appears (there must always be one; we
        // only introduced anonymous regions in parameters) as well as a
        // version new_ty of its type where the anonymous region is replaced
        // with the named one.
        let (named, (arg, new_ty, br, is_first), scope_def_id) = if
            self.is_named_region(sub) && self.is_suitable_anonymous_region(sup).is_some() {
            (sub,
             self.find_arg_with_anonymous_region(sup, sub).unwrap(),
             self.is_suitable_anonymous_region(sup).unwrap())
        } else if
            self.is_named_region(sup) && self.is_suitable_anonymous_region(sub).is_some() {
            (sup,
             self.find_arg_with_anonymous_region(sub, sup).unwrap(),
             self.is_suitable_anonymous_region(sub).unwrap())
        } else {
            return false; // inapplicable
        };

        // Here, we check for the case where the anonymous region
        // is in the return type.
        // FIXME(#42703) - Need to handle certain cases here.
        let ret_ty = self.tcx.type_of(scope_def_id);
        match ret_ty.sty {
            ty::TyFnDef(_, _, sig) => {
                let late_bound_regions = self.tcx
                    .collect_referenced_late_bound_regions(&sig.output());
                if late_bound_regions.iter().any(|r| *r == br) {
                    return false;
                } else {
                }
            }
            _ => {}
        }

        // Here we check for the case where anonymous region
        // corresponds to self and if yes, we display E0312.
        // FIXME(#42700) - Need to format self properly to
        // enable E0611 for it.
        if is_first &&
           self.tcx
               .opt_associated_item(scope_def_id)
               .map(|i| i.method_has_self_argument)
               .unwrap_or(false) {
            return false;
        }

        if let Some(simple_name) = arg.pat.simple_name() {
            struct_span_err!(self.tcx.sess,
                             span,
                             E0611,
                             "explicit lifetime required in the type of `{}`",
                             simple_name)
                    .span_label(arg.pat.span,
                                format!("consider changing the type of `{}` to `{}`",
                                        simple_name,
                                        new_ty))
                    .span_label(span, format!("lifetime `{}` required", named))
                    .emit();

        } else {
            struct_span_err!(self.tcx.sess,
                             span,
                             E0611,
                             "explicit lifetime required in parameter type")
                    .span_label(arg.pat.span,
                                format!("consider changing type to `{}`", new_ty))
                    .span_label(span, format!("lifetime `{}` required", named))
                    .emit();
        }
        return true;

    }

    // This method returns whether the given Region is Anonymous
    // and returns the DefId corresponding to the region.
    pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<DefId> {

        match *region {
            ty::ReFree(ref free_region) => {
                match free_region.bound_region {
                    ty::BrAnon(..) => {
                        let anonymous_region_binding_scope = free_region.scope;
                        let node_id = self.tcx
                            .hir
                            .as_local_node_id(anonymous_region_binding_scope)
                            .unwrap();
                        match self.tcx.hir.find(node_id) {
                            Some(hir_map::NodeItem(..)) |
                            Some(hir_map::NodeTraitItem(..)) => {
                                // proceed ahead //
                            }
                            Some(hir_map::NodeImplItem(..)) => {
                                if self.tcx.impl_trait_ref(self.tcx.
associated_item(anonymous_region_binding_scope).container.id()).is_some() {
                                    // For now, we do not try to target impls of traits. This is
                                    // because this message is going to suggest that the user
                                    // change the fn signature, but they may not be free to do so,
                                    // since the signature must match the trait.
                                    //
                                    // FIXME(#42706) -- in some cases, we could do better here.
                                    return None;
                                }
                              else{  }

                            }
                            _ => return None, // inapplicable
                            // we target only top-level functions
                        }
                        return Some(anonymous_region_binding_scope);
                    }
                    _ => None,
                }
            }
            _ => None,
        }
    }
}
<commit_msg>code review fixes<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Error Reporting for Anonymous Region Lifetime Errors.
use hir;
use infer::InferCtxt;
use ty::{self, Region};
use infer::region_inference::RegionResolutionError::*;
use infer::region_inference::RegionResolutionError;
use hir::map as hir_map;
use hir::def_id::DefId;

impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
    // This method walks the Type of the function body arguments using
    // `fold_regions()` function and returns the
    // &hir::Arg of the function argument corresponding to the anonymous
    // region and the Ty corresponding to the named region.
    // Currently only the case where the function declaration consists of
    // one named region and one anonymous region is handled.
    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
    // Here, we would return the hir::Arg for y, we return the type &'a
    // i32, which is the type of y but with the anonymous region replaced
    // with 'a, the corresponding bound region and is_first which is true if
    // the hir::Arg is the first argument in the function declaration.
    fn find_arg_with_anonymous_region
        (&self,
         anon_region: Region<'tcx>,
         named_region: Region<'tcx>)
         -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {

        match *anon_region {
            ty::ReFree(ref free_region) => {

                let id = free_region.scope;
                let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
                let body_id = self.tcx.hir.maybe_body_owned_by(node_id).unwrap();
                let mut is_first = false;
                let body = self.tcx.hir.body(body_id);
                if let Some(tables) = self.in_progress_tables {
                    body.arguments
                        .iter()
                        .filter_map(|arg| {
                            let ty = tables.borrow().node_id_to_type(arg.id);
                            let mut found_anon_region = false;
                            let new_arg_ty = self.tcx
                                .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
                                    found_anon_region = true;
                                    named_region
                                } else {
                                    r
                                });
                            if found_anon_region {
                                if body.arguments.iter().nth(0) == Some(&arg) {
                                    is_first = true;
                                }
                                Some((arg, new_arg_ty, free_region.bound_region, is_first))
                            } else {
                                None
                            }
                        })
                        .next()
                } else {
                    None
                }
            }
            _ => None,

        }
    }

    // This method generates the error message for the case when
    // the function arguments consist of a named region and an anonymous
    // region and corresponds to `ConcreteFailure(..)`
    pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {

        let (span, sub, sup) = match *error {
            ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
            _ => return false, // inapplicable
        };

        // Determine whether the sub and sup consist of one named region ('a)
        // and one anonymous (elided) region. If so, find the parameter arg
        // where the anonymous region appears (there must always be one; we
        // only introduced anonymous regions in parameters) as well as a
        // version new_ty of its type where the anonymous region is replaced
        // with the named one.
        let (named, (arg, new_ty, br, is_first), scope_def_id) = if
            self.is_named_region(sub) && self.is_suitable_anonymous_region(sup).is_some() {
            (sub,
             self.find_arg_with_anonymous_region(sup, sub).unwrap(),
             self.is_suitable_anonymous_region(sup).unwrap())
        } else if
            self.is_named_region(sup) && self.is_suitable_anonymous_region(sub).is_some() {
            (sup,
             self.find_arg_with_anonymous_region(sub, sup).unwrap(),
             self.is_suitable_anonymous_region(sub).unwrap())
        } else {
            return false; // inapplicable
        };

        // Here, we check for the case where the anonymous region
        // is in the return type.
        // FIXME(#42703) - Need to handle certain cases here.
        let ret_ty = self.tcx.type_of(scope_def_id);
        match ret_ty.sty {
            ty::TyFnDef(_, _, sig) => {
                let late_bound_regions = self.tcx
                    .collect_referenced_late_bound_regions(&sig.output());
                if late_bound_regions.iter().any(|r| *r == br) {
                    return false;
                } else {
                }
            }
            _ => {}
        }

        // Here we check for the case where anonymous region
        // corresponds to self and if yes, we display E0312.
        // FIXME(#42700) - Need to format self properly to
        // enable E0611 for it.
        if is_first &&
           self.tcx
               .opt_associated_item(scope_def_id)
               .map(|i| i.method_has_self_argument)
               .unwrap_or(false) {
            return false;
        }

        if let Some(simple_name) = arg.pat.simple_name() {
            struct_span_err!(self.tcx.sess,
                             span,
                             E0611,
                             "explicit lifetime required in the type of `{}`",
                             simple_name)
                    .span_label(arg.pat.span,
                                format!("consider changing the type of `{}` to `{}`",
                                        simple_name,
                                        new_ty))
                    .span_label(span, format!("lifetime `{}` required", named))
                    .emit();

        } else {
            struct_span_err!(self.tcx.sess,
                             span,
                             E0611,
                             "explicit lifetime required in parameter type")
                    .span_label(arg.pat.span,
                                format!("consider changing type to `{}`", new_ty))
                    .span_label(span, format!("lifetime `{}` required", named))
                    .emit();
        }
        return true;

    }

    // This method returns whether the given Region is Anonymous
    // and returns the DefId corresponding to the region.
    pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<DefId> {

        match *region {
            ty::ReFree(ref free_region) => {
                match free_region.bound_region {
                    ty::BrAnon(..) => {
                        let anonymous_region_binding_scope = free_region.scope;
                        let node_id = self.tcx
                            .hir
                            .as_local_node_id(anonymous_region_binding_scope)
                            .unwrap();
                        match self.tcx.hir.find(node_id) {
                            Some(hir_map::NodeItem(..)) |
                            Some(hir_map::NodeTraitItem(..)) => {
                                // proceed ahead //
                            }
                            Some(hir_map::NodeImplItem(..)) => {
                                if self.tcx.impl_trait_ref(self.tcx.
associated_item(anonymous_region_binding_scope).container.id()).is_some() {
                                    // For now, we do not try to target impls of traits. This is
                                    // because this message is going to suggest that the user
                                    // change the fn signature, but they may not be free to do so,
                                    // since the signature must match the trait.
                                    //
                                    // FIXME(#42706) -- in some cases, we could do better here.
                                    return None;
                                }
                              else{  }

                            }
                            _ => return None, // inapplicable
                            // we target only top-level functions
                        }
                        return Some(anonymous_region_binding_scope);
                    }
                    _ => None,
                }
            }
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use chrono::{DateTime, TimeZone, Utc};
use plist;
use plist::PlistEvent::*;
use std::fs::File;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use {Error, Result};

/// Represents a file with a provisioning profile info.
#[derive(Debug, Clone)]
pub struct Profile {
    pub path: PathBuf,
    pub info: ProfileInfo,
}

impl Profile {
    /// Returns instance of the `Profile` parsed from a file.
    pub fn from_file(path: &Path) -> Result<Self> {
        let mut buf = Vec::new();
        File::open(path)?.read_to_end(&mut buf)?;
        let info = ProfileInfo::from_xml_data(&buf)
            .ok_or_else(|| Error::Own("Couldn't parse file.".into()))?;
        Ok(Profile {
            path: path.to_owned(),
            info,
        })
    }
}

/// Represents provisioning profile info.
#[derive(Debug, Clone)]
pub struct ProfileInfo {
    pub uuid: String,
    pub name: String,
    pub app_identifier: String,
    pub creation_date: DateTime<Utc>,
    pub expiration_date: DateTime<Utc>,
}

impl ProfileInfo {
    /// Returns instance of the `Profile` parsed from a `data`.
    pub fn from_xml_data(data: &[u8]) -> Option<Self> {
        if let Some(data) = ::plist_extractor::find(data) {
            let mut profile = ProfileInfo::empty();
            let mut iter = plist::xml::EventReader::new(io::Cursor::new(data)).into_iter();
            while let Some(item) = iter.next() {
                if let Ok(StringValue(key)) = item {
                    if key == "UUID" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.uuid = value;
                        }
                    }
                    if key == "Name" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.name = value;
                        }
                    }
                    if key == "application-identifier" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.app_identifier = value;
                        }
                    }
                    if key == "CreationDate" {
                        if let Some(Ok(DateValue(value))) = iter.next() {
                            profile.creation_date = value.into();
                        }
                    }
                    if key == "ExpirationDate" {
                        if let Some(Ok(DateValue(value))) = iter.next() {
                            profile.expiration_date = value.into();
                        }
                    }
                }
            }
            Some(profile)
        } else {
            None
        }
    }

    pub fn empty() -> Self {
        ProfileInfo {
            uuid: "".into(),
            name: "".into(),
            app_identifier: "".into(),
            creation_date: Utc.timestamp(0, 0),
            expiration_date: Utc.timestamp(0, 0),
        }
    }

    /// Returns `true` if one or more fields of the profile contain `string`.
    pub fn contains(&self, string: &str) -> bool {
        let s = string.to_lowercase();
        let items = &[&self.name, &self.app_identifier, &self.uuid];
        for item in items {
            if item.to_lowercase().contains(&s) {
                return true;
            }
        }
        false
    }

    /// Returns profile in a text form.
    pub fn description(&self) -> String {
        let mut desc = String::new();
        desc.push_str(&self.uuid);
        desc.push_str("\n");
        desc.push_str(&self.app_identifier);
        desc.push_str("\n");
        desc.push_str(&self.name);
        desc.push_str("\n");
        desc.push_str(&format!(
            "{} - {}",
            self.creation_date, self.expiration_date
        ));
        desc
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::{TimeZone, Utc};
    use expectest::prelude::*;
    use std::path::PathBuf;

    #[test]
    fn contains() {
        let profile = ProfileInfo {
            uuid: "123".into(),
            name: "name".into(),
            app_identifier: "id".into(),
            creation_date: Utc.timestamp(0, 0),
            expiration_date: Utc.timestamp(0, 0),
        };
        expect!(profile.contains("12")).to(be_true());
        expect!(profile.contains("me")).to(be_true());
        expect!(profile.contains("id")).to(be_true());
    }
}
<commit_msg>Implement bundle_id method for a profile<commit_after>use chrono::{DateTime, TimeZone, Utc};
use plist;
use plist::PlistEvent::*;
use std::fs::File;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use {Error, Result};

/// Represents a file with a provisioning profile info.
#[derive(Debug, Clone)]
pub struct Profile {
    pub path: PathBuf,
    pub info: ProfileInfo,
}

impl Profile {
    /// Returns instance of the `Profile` parsed from a file.
    pub fn from_file(path: &Path) -> Result<Self> {
        let mut buf = Vec::new();
        File::open(path)?.read_to_end(&mut buf)?;
        let info = ProfileInfo::from_xml_data(&buf)
            .ok_or_else(|| Error::Own("Couldn't parse file.".into()))?;
        Ok(Profile {
            path: path.to_owned(),
            info,
        })
    }
}

/// Represents provisioning profile info.
#[derive(Debug, Clone)]
pub struct ProfileInfo {
    pub uuid: String,
    pub name: String,
    pub app_identifier: String,
    pub creation_date: DateTime<Utc>,
    pub expiration_date: DateTime<Utc>,
}

impl ProfileInfo {
    /// Returns instance of the `Profile` parsed from a `data`.
    pub fn from_xml_data(data: &[u8]) -> Option<Self> {
        if let Some(data) = ::plist_extractor::find(data) {
            let mut profile = ProfileInfo::empty();
            let mut iter = plist::xml::EventReader::new(io::Cursor::new(data)).into_iter();
            while let Some(item) = iter.next() {
                if let Ok(StringValue(key)) = item {
                    if key == "UUID" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.uuid = value;
                        }
                    }
                    if key == "Name" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.name = value;
                        }
                    }
                    if key == "application-identifier" {
                        if let Some(Ok(StringValue(value))) = iter.next() {
                            profile.app_identifier = value;
                        }
                    }
                    if key == "CreationDate" {
                        if let Some(Ok(DateValue(value))) = iter.next() {
                            profile.creation_date = value.into();
                        }
                    }
                    if key == "ExpirationDate" {
                        if let Some(Ok(DateValue(value))) = iter.next() {
                            profile.expiration_date = value.into();
                        }
                    }
                }
            }
            Some(profile)
        } else {
            None
        }
    }

    /// Returns an empty profile info.
    pub fn empty() -> Self {
        ProfileInfo {
            uuid: "".into(),
            name: "".into(),
            app_identifier: "".into(),
            creation_date: Utc.timestamp(0, 0),
            expiration_date: Utc.timestamp(0, 0),
        }
    }

    /// Returns `true` if one or more fields of the profile contain `string`.
    pub fn contains(&self, string: &str) -> bool {
        let s = string.to_lowercase();
        let items = &[&self.name, &self.app_identifier, &self.uuid];
        for item in items {
            if item.to_lowercase().contains(&s) {
                return true;
            }
        }
        false
    }

    /// Returns a bundle id of a profile.
    pub fn bundle_id(&self) -> Option<&str> {
        self.app_identifier
            .find(|ch| ch == '.')
            .map(|i| &self.app_identifier[(i + 1)..])
    }

    /// Returns profile in a text form.
    pub fn description(&self) -> String {
        let mut desc = String::new();
        desc.push_str(&self.uuid);
        desc.push_str("\n");
        desc.push_str(&self.app_identifier);
        desc.push_str("\n");
        desc.push_str(&self.name);
        desc.push_str("\n");
        desc.push_str(&format!(
            "{} - {}",
            self.creation_date, self.expiration_date
        ));
        desc
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::{TimeZone, Utc};
    use expectest::prelude::*;

    #[test]
    fn contains() {
        let profile = ProfileInfo {
            uuid: "123".into(),
            name: "name".into(),
            app_identifier: "id".into(),
            creation_date: Utc.timestamp(0, 0),
            expiration_date: Utc.timestamp(0, 0),
        };
        expect!(profile.contains("12")).to(be_true());
        expect!(profile.contains("me")).to(be_true());
        expect!(profile.contains("id")).to(be_true());
    }

    #[test]
    fn correct_bundle_id() {
        let mut profile = ProfileInfo::empty();
        profile.app_identifier = "12345ABCDE.com.exmaple.app".to_owned();
        expect!(profile.bundle_id()).to(be_some().value("com.exmaple.app"));
    }

    #[test]
    fn incorrect_bundle_id() {
        let mut profile = ProfileInfo::empty();
        profile.app_identifier = "12345ABCDE".to_owned();
        expect!(profile.bundle_id()).to(be_none());
    }

    #[test]
    fn wildcard_bundle_id() {
        let mut profile = ProfileInfo::empty();
        profile.app_identifier = "12345ABCDE.*".to_owned();
        expect!(profile.bundle_id()).to(be_some().value("*"));
    }
}
<|endoftext|>


<commit_before>use std::ops::Range;

use crate::email;
use crate::scanner::Scanner;

const QUOTES: &[char] = &['\'', '\"'];

/// Scan for URLs starting from the trigger character ":", requires "://".
///
/// Based on RFC 3986.
pub struct UrlScanner;

impl Scanner for UrlScanner {
    /// Scan for an URL at the given separator index in the string.
    ///
    /// The kind of separator that was used (`://` vs `.`) has effect on whether URLs with no
    /// schemes are found.
    ///
    /// Returns `None` if none was found, or if an invalid separator index was given.
    fn scan(&self, s: &str, separator: usize) -> Option<Range<usize>> {
        // There must be something before separator for scheme or host
        if separator == 0 {
            return None;
        }

        // Detect used separator, being `://` or `.`
        let (is_slash_slash, separator_len) = if s[separator..].starts_with("://") {
            (true, "://".len())
        } else if s[separator..].starts_with('.') {
            (false, ".".len())
        } else {
            return None;
        };
        let after_separator = separator + separator_len;

        // When scanning a dot, for URLs without a scheme, do some additional checks
        if !is_slash_slash {
            // The TLD must be at least 2 characters long
            let end = &s[after_separator..];
            let after_last_dot = end
                .split('/')
                .next()
                .unwrap()
                .rfind('.')
                .map(|pos| pos + 1)
                .unwrap_or(0);
            let tld_too_short = end[after_last_dot..]
                .chars()
                .take_while(|c| c.is_ascii_alphabetic())
                .take(2)
                .count()
                < 2;
            if tld_too_short {
                return None;
            }

            // If this is an email address, don't scan it as URL
            if email::is_mail(&s[after_separator..]) {
                return None;
            }
        }

        // Need at least one character for scheme, and one after '//'
        if after_separator < s.len() {
            if let (Some(start), quote) = self.find_start(&s[0..separator], is_slash_slash) {
                if let Some(end) = self.find_end(&s[after_separator..], quote) {
                    let range = Range {
                        start,
                        end: after_separator + end,
                    };
                    return Some(range);
                }
            }
        }
        None
    }
}

impl UrlScanner {
    // For URL searching starting before the `://` separator, the `has_scheme` parameter should be
    // true because the URL will have a scheme for sure. If searching before the `.` separator, it
    // should be `false` as we might search over the scheme definition for the scheme being optional.
    // See "scheme" in RFC 3986
    fn find_start(&self, s: &str, mut has_scheme: bool) -> (Option<usize>, Option<char>) {
        let mut first = None;
        let mut special = None;
        let mut quote = None;
        for (i, c) in s.char_indices().rev() {
            match c {
                'a'..='z' | 'A'..='Z' => first = Some(i),
                '0'..='9' => special = Some(i),
                '/' if !has_scheme => special = Some(i),
                ':' if !has_scheme => {
                    has_scheme = true;
                    special = Some(i)
                }
                '+' | '-' | '.' => {}
                '@' => return (None, None),
                c if QUOTES.contains(&c) => {
                    // Check if there's a quote before the scheme,
                    // and stop once we encounter one of those quotes.
                    // https://github.com/robinst/linkify/issues/20
                    quote = Some(c);
                }
                c if !has_scheme && !matches!(c, '(' | ')' | '[' | ']' | '{' | '}' | ' ') => {
                    // Detect the start for links using unicode when having links without a scheme,
                    // then looking for ASCII alpha characters is not enough
                    first = Some(i);
                }
                _ => break,
            }
        }

        // We don't want to extract "abc://foo" out of "1abc://foo".
        // ".abc://foo" and others are ok though, as they feel more like separators.
        if let Some(first) = first {
            if let Some(special) = special {
                // Comparing the byte indices with `- 1` is ok as scheme must be ASCII
                if first > 0 && first - 1 == special {
                    return (None, quote);
                }
            }
        }
        (first, quote)
    }

    fn find_end(&self, s: &str, quote: Option<char>) -> Option<usize> {
        let mut round = 0;
        let mut square = 0;
        let mut curly = 0;
        let mut single_quote = false;

        let mut previous_can_be_last = true;
        let mut end = None;

        for (i, c) in s.char_indices() {
            let can_be_last = match c {
                '\u{00}'..='\u{1F}' | ' ' | '|' | '\"' | '<' | '>' | '`' | '\u{7F}'..='\u{9F}' => {
                    // These can never be part of an URL, so stop now. See RFC 3986 and RFC 3987.
                    // Some characters are not in the above list, even they are not in "unreserved"
                    // or "reserved":
                    //   '\\', '^', '{', '}'
                    // The reason for this is that other link detectors also allow them. Also see
                    // below, we require the braces to be balanced.
                    break;
                }
                '?' | '!' | '.' | ',' | ':' | ';' | '*' => {
                    // These may be part of an URL but not at the end. It's not that the spec
                    // doesn't allow them, but they are frequently used in plain text as delimiters
                    // where they're not meant to be part of the URL.
                    false
                }
                '/' => {
                    // This may be part of an URL and at the end, but not if the previous character
                    // can't be the end of an URL
                    previous_can_be_last
                }
                '(' => {
                    round += 1;
                    false
                }
                ')' => {
                    round -= 1;
                    if round < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                '[' => {
                    // Allowed in IPv6 address host
                    square += 1;
                    false
                }
                ']' => {
                    // Allowed in IPv6 address host
                    square -= 1;
                    if square < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                '{' => {
                    curly += 1;
                    false
                }
                '}' => {
                    curly -= 1;
                    if curly < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                _ if Some(c) == quote => {
                    // Found matching quote from beginning of URL, stop now
                    break;
                }
                '\'' => {
                    single_quote = !single_quote;
                    // A single quote can only be the end of an URL if there's an even number
                    !single_quote
                }
                _ => true,
            };
            if can_be_last {
                end = Some(i + c.len_utf8());
            }
            previous_can_be_last = can_be_last;
        }

        end
    }
}
<commit_msg>Skip parsing very short strings<commit_after>use std::ops::Range;

use crate::email;
use crate::scanner::Scanner;

/// Minimum valid URL length
///
/// The shortest valid URL (without a scheme) might be g.cn (Google China),
/// which consists of four characters.
/// We set this as a lower threshold for parsing URLs from plaintext
/// to avoid false-positives and as a slight performance optimization.
/// This threshold might be adjusted in the future.
const MIN_URL_LENGTH: usize = 4;

const QUOTES: &[char] = &['\'', '\"'];

/// Scan for URLs starting from the trigger character ":", requires "://".
///
/// Based on RFC 3986.
pub struct UrlScanner;

impl Scanner for UrlScanner {
    /// Scan for an URL at the given separator index in the string.
    ///
    /// The kind of separator that was used (`://` vs `.`) has effect on whether URLs with no
    /// schemes are found.
    ///
    /// Returns `None` if none was found, or if an invalid separator index was given.
    fn scan(&self, s: &str, separator: usize) -> Option<Range<usize>> {
        // There must be something before separator for scheme or host
        if separator == 0 {
            return None;
        }

        if s.len() < MIN_URL_LENGTH {
            // URL shorter than threshold; skip parsing
            return None;
        }

        // Detect used separator, being `://` or `.`
        let (is_slash_slash, separator_len) = if s[separator..].starts_with("://") {
            (true, "://".len())
        } else if s[separator..].starts_with('.') {
            (false, ".".len())
        } else {
            return None;
        };
        let after_separator = separator + separator_len;

        // When scanning a dot, for URLs without a scheme, do some additional checks
        if !is_slash_slash {
            // The TLD must be at least 2 characters long
            let end = &s[after_separator..];
            let after_last_dot = end
                .split('/')
                .next()
                .unwrap()
                .rfind('.')
                .map(|pos| pos + 1)
                .unwrap_or(0);
            let tld_too_short = end[after_last_dot..]
                .chars()
                .take_while(|c| c.is_ascii_alphabetic())
                .take(2)
                .count()
                < 2;
            if tld_too_short {
                return None;
            }

            // If this is an email address, don't scan it as URL
            if email::is_mail(&s[after_separator..]) {
                return None;
            }
        }

        // Need at least one character for scheme, and one after '//'
        if after_separator < s.len() {
            if let (Some(start), quote) = self.find_start(&s[0..separator], is_slash_slash) {
                if let Some(end) = self.find_end(&s[after_separator..], quote) {
                    let range = Range {
                        start,
                        end: after_separator + end,
                    };
                    return Some(range);
                }
            }
        }
        None
    }
}

impl UrlScanner {
    // For URL searching starting before the `://` separator, the `has_scheme` parameter should be
    // true because the URL will have a scheme for sure. If searching before the `.` separator, it
    // should be `false` as we might search over the scheme definition for the scheme being optional.
    // See "scheme" in RFC 3986
    fn find_start(&self, s: &str, mut has_scheme: bool) -> (Option<usize>, Option<char>) {
        let mut first = None;
        let mut special = None;
        let mut quote = None;
        for (i, c) in s.char_indices().rev() {
            match c {
                'a'..='z' | 'A'..='Z' => first = Some(i),
                '0'..='9' => special = Some(i),
                '/' if !has_scheme => special = Some(i),
                ':' if !has_scheme => {
                    has_scheme = true;
                    special = Some(i)
                }
                '+' | '-' | '.' => {}
                '@' => return (None, None),
                c if QUOTES.contains(&c) => {
                    // Check if there's a quote before the scheme,
                    // and stop once we encounter one of those quotes.
                    // https://github.com/robinst/linkify/issues/20
                    quote = Some(c);
                }
                c if !has_scheme && !matches!(c, '(' | ')' | '[' | ']' | '{' | '}' | ' ') => {
                    // Detect the start for links using unicode when having links without a scheme,
                    // then looking for ASCII alpha characters is not enough
                    first = Some(i);
                }
                _ => break,
            }
        }

        // We don't want to extract "abc://foo" out of "1abc://foo".
        // ".abc://foo" and others are ok though, as they feel more like separators.
        if let Some(first) = first {
            if let Some(special) = special {
                // Comparing the byte indices with `- 1` is ok as scheme must be ASCII
                if first > 0 && first - 1 == special {
                    return (None, quote);
                }
            }
        }
        (first, quote)
    }

    fn find_end(&self, s: &str, quote: Option<char>) -> Option<usize> {
        let mut round = 0;
        let mut square = 0;
        let mut curly = 0;
        let mut single_quote = false;

        let mut previous_can_be_last = true;
        let mut end = None;

        for (i, c) in s.char_indices() {
            let can_be_last = match c {
                '\u{00}'..='\u{1F}' | ' ' | '|' | '\"' | '<' | '>' | '`' | '\u{7F}'..='\u{9F}' => {
                    // These can never be part of an URL, so stop now. See RFC 3986 and RFC 3987.
                    // Some characters are not in the above list, even they are not in "unreserved"
                    // or "reserved":
                    //   '\\', '^', '{', '}'
                    // The reason for this is that other link detectors also allow them. Also see
                    // below, we require the braces to be balanced.
                    break;
                }
                '?' | '!' | '.' | ',' | ':' | ';' | '*' => {
                    // These may be part of an URL but not at the end. It's not that the spec
                    // doesn't allow them, but they are frequently used in plain text as delimiters
                    // where they're not meant to be part of the URL.
                    false
                }
                '/' => {
                    // This may be part of an URL and at the end, but not if the previous character
                    // can't be the end of an URL
                    previous_can_be_last
                }
                '(' => {
                    round += 1;
                    false
                }
                ')' => {
                    round -= 1;
                    if round < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                '[' => {
                    // Allowed in IPv6 address host
                    square += 1;
                    false
                }
                ']' => {
                    // Allowed in IPv6 address host
                    square -= 1;
                    if square < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                '{' => {
                    curly += 1;
                    false
                }
                '}' => {
                    curly -= 1;
                    if curly < 0 {
                        // More closing than opening brackets, stop now
                        break;
                    }
                    true
                }
                _ if Some(c) == quote => {
                    // Found matching quote from beginning of URL, stop now
                    break;
                }
                '\'' => {
                    single_quote = !single_quote;
                    // A single quote can only be the end of an URL if there's an even number
                    !single_quote
                }
                _ => true,
            };
            if can_be_last {
                end = Some(i + c.len_utf8());
            }
            previous_can_be_last = can_be_last;
        }

        end
    }
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{bail, Context, Result};

use std::cell::{Cell, RefCell, RefMut};
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::rc::Rc;
use std::time::Duration;

use crate::io::emu::EmuState;
use crate::io::uart::{Uart, UartError};
use crate::transport::ti50emulator::emu::EMULATOR_INVALID_ID;
use crate::transport::ti50emulator::Inner;
use crate::transport::ti50emulator::Ti50Emulator;

const TI50_UART_BAUDRATE: u32 = 115200;

pub struct Ti50Uart {
    inner: Rc<RefCell<Inner>>,
    // This socket is valid as long as SubProcess is running.
    socket: RefCell<Option<UnixStream>>,
    // full path to socket file
    path: PathBuf,
    // last SubProcess ID
    last_id: Cell<u64>,
}

impl Ti50Uart {
    pub fn open(ti50: &Ti50Emulator, path: &str) -> Result<Self> {
        let soc_path = ti50.inner.borrow().process.get_runtime_dir().join(path);
        Ok(Self {
            inner: Rc::clone(&ti50.inner),
            socket: RefCell::default(),
            path: soc_path,
            last_id: Cell::new(EMULATOR_INVALID_ID),
        })
    }

    pub fn reconnect(&self) -> Result<()> {
        let mut socket = self.socket.borrow_mut();
        let id = self.inner.borrow_mut().process.get_id();
        if self.last_id.get() != id {
            *socket = Some(UnixStream::connect(&self.path)?);
            self.last_id.set(id);
        }
        Ok(())
    }

    pub fn check_state(&self) -> Result<bool> {
        let process = &mut self.inner.borrow_mut().process;
        process.update_status()?;
        let valid = match process.get_state() {
            EmuState::On | EmuState::Error => true,
            _ => false,
        };
        return Ok(valid);
    }

    pub fn get_socket(&self) -> Result<RefMut<UnixStream>> {
        if self.check_state()? {
            self.reconnect()?;
            // Socket should be valid as long as the subprocess is running.
            return Ok(RefMut::map(self.socket.borrow_mut(), |socket| {
                socket.as_mut().unwrap()
            }));
        }
        bail!(UartError::GenericError("Invalid socket".to_string()));
    }
}

/// A trait which represents a UART.
impl Uart for Ti50Uart {
    /// Returns the UART baudrate.  May return zero for virtual UARTs.
    fn get_baudrate(&self) -> Result<u32> {
        // As a virtual uart, the value is set only for compatibility with common hardware
        // Real speed of uart is much higher and it is mostly limited by IPC speed.
        Ok(TI50_UART_BAUDRATE)
    }

    /// Sets the UART baudrate.  May do nothing for virtual UARTs.
    fn set_baudrate(&self, _baudrate: u32) -> Result<()> {
        // As a virtual uart, setting the baudrate is a no-op.
        Ok(())
    }

    /// Reads UART receive data into `buf`, returning the number of bytes read.
    /// This function _may_ block.
    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        let mut socket = self.get_socket()?;
        socket.set_read_timeout(None)?;
        return Ok(socket.read(buf)?);
    }

    /// Reads UART receive data into `buf`, returning the number of bytes read.
    /// The `timeout` may be used to specify a duration to wait for data.
    /// If timeout expires without any data arriving `Ok(0)` will be returned, never `Err(_)`.
    fn read_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
        let mut socket = self.get_socket()?;
        socket.set_read_timeout(Some(timeout))?;
        return Ok(socket.read(buf).context("UART read error")?);
    }

    /// Writes data from `buf` to the UART.
    fn write(&self, buf: &[u8]) -> Result<()> {
        self.get_socket()?.write(buf).context("UART read error")?;
        return Ok(());
    }

    /// Clears the UART RX buffer.
    fn clear_rx_buffer(&self) -> Result<()> {
        // Iterators are lazy, consume using `count()`.
        Read::by_ref(&mut *self.get_socket()?)
            .bytes()
            .take_while(Result::is_ok)
            .count();
        return Ok(());
    }
}
<commit_msg>[opentitanlib] Fix Ti50Emulator Uart Sub-process state handling.<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{bail, Context, Result};

use std::cell::{Cell, RefCell, RefMut};
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::rc::Rc;
use std::time::Duration;

use crate::io::emu::EmuState;
use crate::io::uart::{Uart, UartError};
use crate::transport::ti50emulator::emu::EMULATOR_INVALID_ID;
use crate::transport::ti50emulator::Inner;
use crate::transport::ti50emulator::Ti50Emulator;

const TI50_UART_BAUDRATE: u32 = 115200;

pub struct Ti50Uart {
    inner: Rc<RefCell<Inner>>,
    // This socket is valid as long as SubProcess is running.
    socket: RefCell<Option<UnixStream>>,
    // full path to socket file
    path: PathBuf,
    // last SubProcess ID
    last_id: Cell<u64>,
}

impl Ti50Uart {
    pub fn open(ti50: &Ti50Emulator, path: &str) -> Result<Self> {
        let soc_path = ti50.inner.borrow().process.get_runtime_dir().join(path);
        Ok(Self {
            inner: Rc::clone(&ti50.inner),
            socket: RefCell::default(),
            path: soc_path,
            last_id: Cell::new(EMULATOR_INVALID_ID),
        })
    }

    pub fn reconnect(&self) -> Result<()> {
        let mut socket = self.socket.borrow_mut();
        let id = self.inner.borrow_mut().process.get_id();
        if self.last_id.get() != id {
            *socket = Some(UnixStream::connect(&self.path)?);
            self.last_id.set(id);
        }
        Ok(())
    }

    pub fn get_state(&self) -> Result<EmuState> {
        let process = &mut self.inner.borrow_mut().process;
        process.update_status()?;
        Ok(process.get_state())
    }

    pub fn get_socket(&self) -> Result<RefMut<UnixStream>> {
        self.reconnect()?;
        return Ok(RefMut::map(self.socket.borrow_mut(), |socket| {
            socket.as_mut().unwrap()
        }));
    }
}

/// A trait which represents a UART.
impl Uart for Ti50Uart {
    /// Returns the UART baudrate.  May return zero for virtual UARTs.
    fn get_baudrate(&self) -> Result<u32> {
        // As a virtual uart, the value is set only for compatibility with common hardware
        // Real speed of uart is much higher and it is mostly limited by IPC speed.
        Ok(TI50_UART_BAUDRATE)
    }

    /// Sets the UART baudrate.  May do nothing for virtual UARTs.
    fn set_baudrate(&self, _baudrate: u32) -> Result<()> {
        // As a virtual uart, setting the baudrate is a no-op.
        Ok(())
    }

    /// Reads UART receive data into `buf`, returning the number of bytes read.
    /// This function _may_ block.
    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        match self.get_state()? {
            EmuState::On => {
                let mut socket = self.get_socket()?;
                socket.set_read_timeout(None)?;
                Ok(socket.read(buf)?)
            }
            EmuState::Off => Ok(0),
            state => Err(UartError::GenericError(format!(
                "Operation not supported in Emulator state: {}",
                state
            ))
            .into()),
        }
    }

    /// Reads UART receive data into `buf`, returning the number of bytes read.
    /// The `timeout` may be used to specify a duration to wait for data.
    /// If timeout expires without any data arriving `Ok(0)` will be returned, never `Err(_)`.
    fn read_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
        match self.get_state()? {
            EmuState::On => {
                let mut socket = self.get_socket()?;
                socket.set_read_timeout(Some(timeout))?;
                return Ok(socket.read(buf).context("UART read error")?);
            }
            EmuState::Off => Ok(0),
            state => Err(UartError::GenericError(format!(
                "Operation not supported in Emulator state: {}",
                state
            ))
            .into()),
        }
    }

    /// Writes data from `buf` to the UART.
    fn write(&self, buf: &[u8]) -> Result<()> {
        match self.get_state()? {
            EmuState::On => {
                self.get_socket()?.write(buf).context("UART read error")?;
                Ok(())
            }
            state => Err(UartError::GenericError(format!(
                "Operation not supported in Emulator state: {}",
                state
            ))
            .into()),
        }
    }

    /// Clears the UART RX buffer.
    fn clear_rx_buffer(&self) -> Result<()> {
        // Iterators are lazy, consume using `count()`.
        Read::by_ref(&mut *self.get_socket()?)
            .bytes()
            .take_while(Result::is_ok)
            .count();
        return Ok(());
    }
}
<|endoftext|>