<commit_before>
use assembler::parser::Literal;
use assembler::u12::*;

pub trait Emittable {

  /**
   @return The size of the emittable element in bytes.
   */
  fn size(&self) -> usize;

  /**
   Consuming conversion from an emittable element into an byte Vec.
   @return A Vec of bytes to be written into the output stream.
   */
  fn into_slice(self) -> Vec<u8>;

}

pub enum Directive {
  /// Directive to set the emit origin to the new address.
  Org(U12),
  /// Directive to emit the specified bytes.
  Db(Vec<u8>)
}

pub mod names {
  /// The name of the 'origin' directive (.org)
  pub const ORG: &'static str = "org";
  /// The name of the 'define-bytes' directive (.db)
  pub const DB : &'static str = "db" ;
}

impl<'a> Directive {
  /**
   Validate the semantics of a directive and yield the Emittable representation.
   @param identifier The identifier for a directive.
   @param arguments The literal arguments specified for the directive.
   @return Directive in the event the arguments are valid for the directive, or a failure reason.
   */
  fn from_identifier_and_parameters(identifier: &'a str, arguments: &Vec<Literal>) -> Result<Directive,String> {
    /*
     The 'org' directive is used to set the current output origin address. The directive requires
     a single numeric literal in the range $000-$FFF.
     */
    if identifier == names::ORG {
      if arguments.len() != 1 {
        return Err(format!("Incorrect number of parameters ({}) for directive .org, expecting 1.", arguments.len()));
      }

      return match arguments[0] {
        Literal::Numeric(a) if a <= 4095 => {
          Ok(Directive::Org(a.as_u12().unwrap()))
        }
        _ => {
          Err(format!("Directive .org requires 1 numeric literal in the range $000-$FFF."))
        }
      }
    }

    /*
     The 'db' directive is used to emit a series of bytes at the current location.
     A list of 1 or more literals is required, either numeric or string.
     A string literal is emitted without a null-terminator.
     */
    if identifier == names::DB {
      if arguments.len() == 0 {
        return Err(format!("Incorrect number of parameters (0) for directive .db, expecting 1 or more."));
      }

      let mut bytes = Vec::<u8>::new();

      // Convert all valid parameters into their byte literal values.
      for argument in arguments {
        match argument {
          &Literal::Numeric(value) => {
            if value > 0xFF {
              return Err(format!("All numeric parameters to .db must be 1-byte literals (${:X} > $FF)", value));
            } else {
              bytes.push((value & 0x00FF) as u8);
            }
          }
          &Literal::String(string) => {
            bytes.extend_from_slice(string.as_bytes());
          }
        }
      }

      return Ok(Directive::Db(bytes));
    }

    Err(format!("Unrecognized directive .{}", identifier))
  }
}

impl Emittable for Directive {

  /**
   @return The size of the directive in the output stream in bytes.
   */
  fn size(&self) -> usize {
    match *self {
      Directive::Org(_)       => 0,
      Directive::Db(ref data) => data.len()
    }
  }

  /**
   Consuming conversion from a directive element into the byte representation in the output stream.
   @return A Vec of bytes to be written into the output stream for the directive.
   */
  fn into_slice(self) -> Vec<u8> {
    match self {
      Directive::Org(_)       => vec![],
      Directive::Db(data)     => data
    }
  }

}
<commit_msg>Break the Directive semantic analysis and emit code into its own module and Enum.<commit_after>
use assembler::assembler::Emittable;
use assembler::parser::Literal;
use assembler::u12::*;

#[derive(PartialEq, Eq, Debug)]
pub enum Directive {
  /// Directive to set the emit origin to the new address.
  Org(U12),
  /// Directive to emit the specified bytes.
  Db(Vec<u8>)
}

mod names {
  /// The name of the 'origin' directive (.org)
  pub const ORG: &'static str = "org";
  /// The name of the 'define-bytes' directive (.db)
  pub const DB : &'static str = "db" ;
}

impl<'a> Directive {
  /**
   Validate the semantics of a directive and yield the Emittable representation.
   @param identifier The identifier for a directive.
   @param arguments The literal arguments specified for the directive.
   @return Directive in the event the arguments are valid for the directive, or a failure reason.
   */
  pub fn from_identifier_and_parameters(identifier: &'a str, arguments: &Vec<Literal>) -> Result<Directive,String> {
    /*
     The 'org' directive is used to set the current output origin address. The directive requires
     a single numeric literal in the range $000-$FFF.
     */
    if identifier == names::ORG {
      if arguments.len() != 1 {
        return Err(format!("Incorrect number of parameters ({}) for directive .org, expecting 1.", arguments.len()));
      }

      return match arguments[0] {
        Literal::Numeric(a) if a <= 4095 => {
          Ok(Directive::Org(a.as_u12().unwrap()))
        }
        _ => {
          Err(format!("Directive .org requires 1 numeric literal in the range $000-$FFF."))
        }
      }
    }

    /*
     The 'db' directive is used to emit a series of bytes at the current location.
     A list of 1 or more literals is required, either numeric or string.
     A string literal is emitted without a null-terminator.
     */
    if identifier == names::DB {
      if arguments.len() == 0 {
        return Err(format!("Incorrect number of parameters (0) for directive .db, expecting 1 or more."));
      }

      let mut bytes = Vec::new();

      // Convert all valid parameters into their byte literal values.
      for argument in arguments {
        match argument {
          &Literal::Numeric(value) => {
            if value > 0xFF {
              return Err(format!("All numeric parameters to .db must be 1-byte literals (${:X} > $FF)", value));
            } else {
              bytes.push((value & 0x00FF) as u8);
            }
          }
          &Literal::String(string) => {
            bytes.extend_from_slice(string.as_bytes());
          }
        }
      }

      return Ok(Directive::Db(bytes));
    }

    Err(format!("Unrecognized directive .{}", identifier))
  }
}

impl Emittable for Directive {

  /**
   @return The size of the directive in the output stream in bytes.
   */
  fn size(&self) -> usize {
    match *self {
      Directive::Org(_)       => 0,
      Directive::Db(ref data) => data.len()
    }
  }

  /**
   Consuming conversion from a directive element into the byte representation in the output stream.
   @return A Vec of bytes to be written into the output stream for the directive.
   */
  fn into_slice(self) -> Vec<u8> {
    match self {
      Directive::Org(_)       => vec![],
      Directive::Db(data)     => data
    }
  }

}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use assembler::assembler::Emittable;
  use assembler::parser::Literal;
  use assembler::u12;
  use assembler::u12::*;

  use super::*;

  #[test]
  fn test_validate_directive_semantics_for_org() {
    let mut params = vec![];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Err("Incorrect number of parameters (0) for directive .org, expecting 1.".to_string()));
    params = vec![Literal::Numeric(1), Literal::Numeric(3)];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Err("Incorrect number of parameters (2) for directive .org, expecting 1.".to_string()));
    params = vec![Literal::Numeric(0x1000)];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Err("Directive .org requires 1 numeric literal in the range $000-$FFF.".to_string()));
    params = vec![Literal::String("TEST_STRING")];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Err("Directive .org requires 1 numeric literal in the range $000-$FFF.".to_string()));
    params = vec![Literal::Numeric(0xFFF)];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Ok(Directive::Org(u12::MAX)));
    params = vec![Literal::Numeric(0x000)];
    assert_eq!(Directive::from_identifier_and_parameters("org", &params), Ok(Directive::Org(u12::MIN)));
  }

  #[test]
  fn test_validate_directive_semantics_for_db() {
    let mut params = vec![];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Err("Incorrect number of parameters (0) for directive .db, expecting 1 or more.".to_string()));
    params = vec![Literal::Numeric(0x100)];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Err("All numeric parameters to .db must be 1-byte literals ($100 > $FF)".to_string()));
    params = vec![Literal::String("TEST_STRING")];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Ok(Directive::Db("TEST_STRING".as_bytes().to_vec())));
    params = vec![Literal::Numeric(0xFF)];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Ok(Directive::Db(vec![0xFF])));
    params = vec![Literal::Numeric(0x00)];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Ok(Directive::Db(vec![0x00])));
    params = vec![Literal::String("TEST_STRING"), Literal::Numeric(0x00)];
    assert_eq!(Directive::from_identifier_and_parameters("db", &params), Ok(Directive::Db("TEST_STRING\0".as_bytes().to_vec())));
  }

  #[test]
  fn test_validate_directive_semantics_for_invalid_directive() {
    let mut params = vec![];
    assert_eq!(Directive::from_identifier_and_parameters("dw", &params), Err("Unrecognized directive .dw".to_string()));
    params = vec![Literal::Numeric(0x100)];
    assert_eq!(Directive::from_identifier_and_parameters("dw", &params), Err("Unrecognized directive .dw".to_string()));
  }

  #[test]
  fn test_size_of_directive_for_org() {
    let org_directive = Directive::from_identifier_and_parameters("org", &vec![Literal::Numeric(0xFFF)]).ok().unwrap();
    assert_eq!(org_directive.size(), 0);
  }

  #[test]
  fn test_size_of_directive_for_db() {
    let expected_sizes = [1, 11, 12];
    let directives = [
      Directive::from_identifier_and_parameters("db", &vec![Literal::Numeric(0xFF)]).ok().unwrap(),
      Directive::from_identifier_and_parameters("db", &vec![Literal::String("TEST_STRING")]).ok().unwrap(),
      Directive::from_identifier_and_parameters("db", &vec![Literal::String("TEST_STRING"), Literal::Numeric(0x00)]).ok().unwrap()
    ];

    // Validate the expected size is obtained for each directive.
    for (i, directive) in directives.iter().enumerate() {
      let size = directive.size();
      let expected_size = expected_sizes[i];
      assert_eq!(size, expected_size);
    }
  }

}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// 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.

use std::collections::HashSet;
use std::ffi;
use std::fmt;
use std::mem;
use std::str;
use super::gl;

use gfx::device::Capabilities;
use gfx::device::shade;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive

#[automatically_derived]
impl ::std::cmp::Ord for Version {
    #[inline]
    fn cmp(&self, other: &Version) -> ::std::cmp::Ordering {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}
#[automatically_derived]
impl ::std::cmp::PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> ::std::option::Option<::std::cmp::Ordering> {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .partial_cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[(i + 1)..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

const EMPTY_STRING: &'static str = "";

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        error!("Invalid GLenum passed to `get_string`: {:x}", name);
        EMPTY_STRING
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 0, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.version >= Version::new(major, minor, None, "") || self.is_extension_supported(ext)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    use gfx::device::shade::ShaderModel;
    match v {
        v if *v < Version::new(1, 20, None, "") => ShaderModel::Unsupported,
        v if *v < Version::new(1, 50, None, "") => ShaderModel::Version30,
        v if *v < Version::new(3,  0, None, "") => ShaderModel::Version40,
        v if *v < Version::new(4, 30, None, "") => ShaderModel::Version41,
        _                                       => ShaderModel::Version50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model:                   to_shader_model(&info.shading_language),

        max_vertex_count:               get_usize(gl, gl::MAX_ELEMENTS_VERTICES),
        max_index_count:                get_usize(gl, gl::MAX_ELEMENTS_INDICES),
        max_draw_buffers:               get_usize(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size:               get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes:          get_usize(gl, gl::MAX_VERTEX_ATTRIBS),

        buffer_role_change_allowed:     true, //TODO

        array_buffer_supported:         info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        fragment_output_supported:      info.is_version_or_extension_supported(3, 0, "GL_EXT_gpu_shader4"),
        immutable_storage_supported:    info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        instance_base_supported:        info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:        info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:        info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        render_targets_supported:       info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object"),
        srgb_color_supported:           info.is_version_or_extension_supported(3, 2, "GL_ARB_framebuffer_sRGB"),
        sampler_objects_supported:      info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        uniform_block_supported:        info.is_version_or_extension_supported(3, 0, "GL_ARB_uniform_buffer_object"),
        vertex_base_supported:          info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        use gfx::device::shade::ShaderModel;
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), ShaderModel::Unsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), ShaderModel::Version30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), ShaderModel::Version40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), ShaderModel::Version41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), ShaderModel::Version50);
    }
}
<commit_msg>Update info.rs<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// 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.

use std::collections::HashSet;
use std::ffi;
use std::fmt;
use std::mem;
use std::str;
use super::gl;

use gfx::device::Capabilities;
use gfx::device::shade;

/// A version number for a specific component of an OpenGL implementation
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Version {
    pub major: u32,
    pub minor: u32,
    pub revision: Option<u32>,
    pub vendor_info: &'static str,
}

// FIXME https://github.com/rust-lang/rust/issues/18738
// derive

#[automatically_derived]
impl ::std::cmp::Ord for Version {
    #[inline]
    fn cmp(&self, other: &Version) -> ::std::cmp::Ordering {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}
#[automatically_derived]
impl ::std::cmp::PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> ::std::option::Option<::std::cmp::Ordering> {
        (&self.major, &self.minor, &self.revision, self.vendor_info)
            .partial_cmp(&(&other.major, &other.minor, &other.revision, other.vendor_info))
    }
}

impl Version {
    /// Create a new OpenGL version number
    pub fn new(major: u32, minor: u32, revision: Option<u32>,
               vendor_info: &'static str) -> Version {
        Version {
            major: major,
            minor: minor,
            revision: revision,
            vendor_info: vendor_info,
        }
    }

    /// According to the OpenGL specification, the version information is
    /// expected to follow the following syntax:
    ///
    /// ~~~bnf
    /// <major>       ::= <number>
    /// <minor>       ::= <number>
    /// <revision>    ::= <number>
    /// <vendor-info> ::= <string>
    /// <release>     ::= <major> "." <minor> ["." <release>]
    /// <version>     ::= <release> [" " <vendor-info>]
    /// ~~~
    ///
    /// Note that this function is intentionally lenient in regards to parsing,
    /// and will try to recover at least the first two version numbers without
    /// resulting in an `Err`.
    pub fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = match src.find(' ') {
            Some(i) => (&src[..i], &src[(i + 1)..]),
            None => (src, ""),
        };

        // TODO: make this even more lenient so that we can also accept
        // `<major> "." <minor> [<???>]`
        let mut it = version.split('.');
        let major = it.next().and_then(|s| s.parse().ok());
        let minor = it.next().and_then(|s| s.parse().ok());
        let revision = it.next().and_then(|s| s.parse().ok());

        match (major, minor, revision) {
            (Some(major), Some(minor), revision) => Ok(Version {
                major: major,
                minor: minor,
                revision: revision,
                vendor_info: vendor_info,
            }),
            (_, _, _) => Err(src),
        }
    }
}

impl fmt::Debug for Version {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match (self.major, self.minor, self.revision, self.vendor_info) {
            (major, minor, Some(revision), "") =>
                write!(f, "{}.{}.{}", major, minor, revision),
            (major, minor, None, "") =>
                write!(f, "{}.{}", major, minor),
            (major, minor, Some(revision), vendor_info) =>
                write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info),
            (major, minor, None, vendor_info) =>
                write!(f, "{}.{}, {}", major, minor, vendor_info),
        }
    }
}

const EMPTY_STRING: &'static str = "";

/// Get a statically allocated string from the implementation using
/// `glGetString`. Fails if it `GLenum` cannot be handled by the
/// implementation's `gl.GetString` function.
fn get_string(gl: &gl::Gl, name: gl::types::GLenum) -> &'static str {
    let ptr = unsafe { gl.GetString(name) } as *const i8;
    if !ptr.is_null() {
        // This should be safe to mark as statically allocated because
        // GlGetString only returns static strings.
        unsafe { c_str_as_static_str(ptr) }
    } else {
        error!("Invalid GLenum passed to `get_string`: {:x}", name);
        EMPTY_STRING
    }
}

fn get_usize(gl: &gl::Gl, name: gl::types::GLenum) -> usize {
    let mut value = 0 as gl::types::GLint;
    unsafe { gl.GetIntegerv(name, &mut value) };
    value as usize
}

unsafe fn c_str_as_static_str(c_str: *const i8) -> &'static str {
    mem::transmute(str::from_utf8(ffi::CStr::from_ptr(c_str as *const _).to_bytes()).unwrap())
}

/// A unique platform identifier that does not change between releases
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PlatformName {
    /// The company responsible for the OpenGL implementation
    pub vendor: &'static str,
    /// The name of the renderer
    pub renderer: &'static str,
}

impl PlatformName {
    fn get(gl: &gl::Gl) -> PlatformName {
        PlatformName {
            vendor: get_string(gl, gl::VENDOR),
            renderer: get_string(gl, gl::RENDERER),
        }
    }
}

/// OpenGL implementation information
#[derive(Debug)]
pub struct Info {
    /// The platform identifier
    pub platform_name: PlatformName,
    /// The OpenGL API vesion number
    pub version: Version,
    /// The GLSL vesion number
    pub shading_language: Version,
    /// The extensions supported by the implementation
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn get(gl: &gl::Gl) -> Info {
        let platform_name = PlatformName::get(gl);
        let version = Version::parse(get_string(gl, gl::VERSION)).unwrap();
        let shading_language = Version::parse(get_string(gl, gl::SHADING_LANGUAGE_VERSION)).unwrap();
        let extensions = if version >= Version::new(3, 0, None, "") {
            let num_exts = get_usize(gl, gl::NUM_EXTENSIONS) as gl::types::GLuint;
            (0..num_exts)
                .map(|i| unsafe { c_str_as_static_str(gl.GetStringi(gl::EXTENSIONS, i) as *const i8) })
                .collect()
        } else {
            // Fallback
            get_string(gl, gl::EXTENSIONS).split(' ').collect()
        };
        Info {
            platform_name: platform_name,
            version: version,
            shading_language: shading_language,
            extensions: extensions,
        }
    }

    /// Returns `true` if the implementation supports the extension
    pub fn is_extension_supported(&self, s: &'static str) -> bool {
        self.extensions.contains(&s)
    }

    pub fn is_version_or_extension_supported(&self, major: u32, minor: u32, ext: &'static str) -> bool {
        self.version >= Version::new(major, minor, None, "") || self.is_extension_supported(ext)
    }
}

fn to_shader_model(v: &Version) -> shade::ShaderModel {
    use gfx::device::shade::ShaderModel;
    match v {
        v if *v < Version::new(1, 20, None, "") => ShaderModel::Unsupported,
        v if *v < Version::new(1, 50, None, "") => ShaderModel::Version30,
        v if *v < Version::new(3,  0, None, "") => ShaderModel::Version40,
        v if *v < Version::new(4, 30, None, "") => ShaderModel::Version41,
        _                                       => ShaderModel::Version50,
    }
}

/// Load the information pertaining to the driver and the corresponding device
/// capabilities.
pub fn get(gl: &gl::Gl) -> (Info, Capabilities) {
    let info = Info::get(gl);
    let caps = Capabilities {
        shader_model:                   to_shader_model(&info.shading_language),

        max_vertex_count:               get_usize(gl, gl::MAX_ELEMENTS_VERTICES),
        max_index_count:                get_usize(gl, gl::MAX_ELEMENTS_INDICES),
        max_draw_buffers:               get_usize(gl, gl::MAX_DRAW_BUFFERS),
        max_texture_size:               get_usize(gl, gl::MAX_TEXTURE_SIZE),
        max_vertex_attributes:          get_usize(gl, gl::MAX_VERTEX_ATTRIBS),

        buffer_role_change_allowed:     true, //TODO

        array_buffer_supported:         info.is_version_or_extension_supported(3, 0, "GL_ARB_vertex_array_object"),
        fragment_output_supported:      info.is_version_or_extension_supported(3, 0, "GL_EXT_gpu_shader4"),
        immutable_storage_supported:    info.is_version_or_extension_supported(4, 2, "GL_ARB_texture_storage"),
        instance_base_supported:        info.is_version_or_extension_supported(4, 2, "GL_ARB_base_instance"),
        instance_call_supported:        info.is_version_or_extension_supported(3, 1, "GL_ARB_draw_instanced"),
        instance_rate_supported:        info.is_version_or_extension_supported(3, 3, "GL_ARB_instanced_arrays"),
        render_targets_supported:       info.is_version_or_extension_supported(3, 0, "GL_ARB_framebuffer_object"),
        srgb_color_supported:           info.is_version_or_extension_supported(3, 2, "GL_ARB_framebuffer_sRGB"),
        sampler_objects_supported:      info.is_version_or_extension_supported(3, 3, "GL_ARB_sampler_objects"),
        uniform_block_supported:        info.is_version_or_extension_supported(3, 0, "GL_ARB_uniform_buffer_object"),
        vertex_base_supported:          info.is_version_or_extension_supported(3, 2, "GL_ARB_draw_elements_base_vertex"),
    };
    (info, caps)
}

#[cfg(test)]
mod tests {
    use super::Version;
    use super::to_shader_model;

    #[test]
    fn test_version_parse() {
        assert_eq!(Version::parse("1"), Err("1"));
        assert_eq!(Version::parse("1."), Err("1."));
        assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
        assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
        assert_eq!(Version::parse("1.2.3"), Ok(Version::new(1, 2, Some(3), "")));
        assert_eq!(Version::parse("1.2"), Ok(Version::new(1, 2, None, "")));
        assert_eq!(Version::parse("1.2 h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.h3l1o. W0rld"), Ok(Version::new(1, 2, None, "W0rld")));
        assert_eq!(Version::parse("1.2. h3l1o. W0rld"), Ok(Version::new(1, 2, None, "h3l1o. W0rld")));
        assert_eq!(Version::parse("1.2.3.h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "W0rld")));
        assert_eq!(Version::parse("1.2.3 h3l1o. W0rld"), Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld")));
    }

    #[test]
    fn test_shader_model() {
        use gfx::device::shade::ShaderModel;
        assert_eq!(to_shader_model(&Version::parse("1.10").unwrap()), ShaderModel::Unsupported);
        assert_eq!(to_shader_model(&Version::parse("1.20").unwrap()), ShaderModel::Version30);
        assert_eq!(to_shader_model(&Version::parse("1.50").unwrap()), ShaderModel::Version40);
        assert_eq!(to_shader_model(&Version::parse("3.00").unwrap()), ShaderModel::Version41);
        assert_eq!(to_shader_model(&Version::parse("4.30").unwrap()), ShaderModel::Version50);
    }
}
<|endoftext|>


<commit_before>//!
//! Abstraction of different session APIs.
//!
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
//! mechanisms to switch between and handle device access and permissions for every running
//! instance.
//!
//! They are crucial to allow unprivileged processes to use graphical or input devices.
//!
//! The following mechanisms are currently provided:
//!     - direct - legacy tty / virtual terminal kernel API
//!
use nix::fcntl::OFlag;
use std::{
    cell::RefCell,
    os::unix::io::RawFd,
    path::Path,
    rc::Rc,
    sync::{Arc, Mutex},
};

/// General session interface.
///
/// Provides a way to open and close devices and change the active vt.
pub trait Session {
    /// Error type of the implementation
    type Error: AsErrno;

    /// Opens a device at the given `path` with the given flags.
    ///
    /// Returns a raw file descriptor
    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
    /// Close a previously opened file descriptor
    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;

    /// Change the currently active virtual terminal
    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> String;
}

/// Signals that can be generated by a session
///
/// Objects that need to be notifier about activation and deactivation
/// of devices need to be linked to the `Signaler` of the session providing
/// these signals.
#[derive(Copy, Clone, Debug)]
pub enum Signal {
    /// The whole session has been paused
    ///
    /// All devices should be considered as paused
    PauseSession,
    /// A given device has been paused
    PauseDevice {
        /// Major number identifying the device
        major: u32,
        /// Minor number identifying the device
        minor: u32,
    },
    /// The whole session has been activated
    ActivateSession,
    /// A given device has been activated
    ActivateDevice {
        /// Major number identifying the device
        major: u32,
        /// Minor number identifying the device
        minor: u32,
        /// New file descriptor for accessing the device
        ///
        /// If it is not `None`, you should use this FD in place of the
        /// previous one for accessing this device. In case the old FD
        /// is different from the new one, the old one should be closed.
        new_fd: Option<RawFd>,
    },
}

impl Session for () {
    type Error = ();

    fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
        Err(())
    }
    fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> {
        Err(())
    }

    fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> {
        Err(())
    }

    fn is_active(&self) -> bool {
        false
    }
    fn seat(&self) -> String {
        String::from("seat0")
    }
}

impl<S: Session> Session for Rc<RefCell<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.borrow_mut().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.borrow_mut().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.borrow_mut().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.borrow().is_active()
    }

    fn seat(&self) -> String {
        self.borrow().seat()
    }
}

impl<S: Session> Session for Arc<Mutex<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.lock().unwrap().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.lock().unwrap().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.lock().unwrap().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.lock().unwrap().is_active()
    }

    fn seat(&self) -> String {
        self.lock().unwrap().seat()
    }
}

/// Allows errors to be described by an error number
pub trait AsErrno: ::std::fmt::Debug {
    /// Returns the error number representing this error if any
    fn as_errno(&self) -> Option<i32>;
}

impl AsErrno for () {
    fn as_errno(&self) -> Option<i32> {
        None
    }
}

pub mod auto;
pub mod direct;
#[cfg(feature = "backend_session_libseat")]
pub mod libseat;

#[cfg(feature = "backend_session_logind")]
mod dbus;
#[cfg(feature = "backend_session_logind")]
pub use self::dbus::*;
<commit_msg>backend.session: review docs<commit_after>//! Abstraction of different session APIs.
//!
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
//! mechanisms to switch between and handle device access and permissions for every running
//! instance. They are crucial to allow unprivileged processes to use graphical or input
//! devices.
//!
//! The functions of the provided interfaces lies in two main components: the ability to
//! open privileged devices, and be notified about when the session is paused and resumed
//! (for example when the user switches to an other TTY, or is the computer goes to sleep).
//!
//! ## General use
//!
//! Session handling in Smithay is done through a pair of types that each session provider implements.
//!
//! The first is a handle implementing the [`Session`] trait, which allows you to request the opening
//! of devices, a VT change, or informations about the session state.
//!
//! The second is a notifier which informs you when the session is enabled or disabled by the system.
//! This notifier takes the form of a [`calloop`] event source, but it does not generates any event
//! directly. Instead, it gives you a [`Signaler`](crate::utils::signaling::Signaler) that you can
//! pass around to other backend modules that need to be notified of the new session state, to stop
//! accessing the devices while the session is disabled. Notable examples are the
//! [`libinput`](super::libinput) and [`drm`](super::drm) backends.
//!
//! ## Available providers
//!
//! This module provides session implementations based on different providers:
//!
//! - Through [libseat](https://sr.ht/~kennylevinsen/seatd/), gated by the `backend_session_libseat`
//!   cargo feature
//! - Through logind or elogind, gated by the `backend_session_logind` and `backend_session_elogind`
//!   cargo features (which are incompatible with each other)
//! - Through the direct kernel VT interface. This last option is intended as a fallback, as this is
//!   a legacy API.
//!
//! In general, you'll want to use the [`auto`] wrapper, that automatically discovers which
//! provider is available and selects the "best" one (in the order of the previous list). See the
//! module-level documentation of each provider for its precise usage.

use nix::fcntl::OFlag;
use std::{
    cell::RefCell,
    os::unix::io::RawFd,
    path::Path,
    rc::Rc,
    sync::{Arc, Mutex},
};

/// General session interface.
///
/// Provides a way to open and close devices and change the active vt.
pub trait Session {
    /// Error type of the implementation
    type Error: AsErrno;

    /// Opens a device at the given `path` with the given flags.
    ///
    /// Returns a raw file descriptor
    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
    /// Close a previously opened file descriptor
    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;

    /// Change the currently active virtual terminal
    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;

    /// Check if this session is currently active
    fn is_active(&self) -> bool;
    /// Which seat this session is on
    fn seat(&self) -> String;
}

/// Signals that can be generated by a session
///
/// Objects that need to be notifier about activation and deactivation
/// of devices need to be linked to the `Signaler` of the session providing
/// these signals.
#[derive(Copy, Clone, Debug)]
pub enum Signal {
    /// The whole session has been paused
    ///
    /// All devices should be considered as paused
    PauseSession,
    /// A given device has been paused
    PauseDevice {
        /// Major number identifying the device
        major: u32,
        /// Minor number identifying the device
        minor: u32,
    },
    /// The whole session has been activated
    ActivateSession,
    /// A given device has been activated
    ActivateDevice {
        /// Major number identifying the device
        major: u32,
        /// Minor number identifying the device
        minor: u32,
        /// New file descriptor for accessing the device
        ///
        /// If it is not `None`, you should use this FD in place of the
        /// previous one for accessing this device. In case the old FD
        /// is different from the new one, the old one should be closed.
        new_fd: Option<RawFd>,
    },
}

impl Session for () {
    type Error = ();

    fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
        Err(())
    }
    fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> {
        Err(())
    }

    fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> {
        Err(())
    }

    fn is_active(&self) -> bool {
        false
    }
    fn seat(&self) -> String {
        String::from("seat0")
    }
}

impl<S: Session> Session for Rc<RefCell<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.borrow_mut().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.borrow_mut().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.borrow_mut().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.borrow().is_active()
    }

    fn seat(&self) -> String {
        self.borrow().seat()
    }
}

impl<S: Session> Session for Arc<Mutex<S>> {
    type Error = S::Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
        self.lock().unwrap().open(path, flags)
    }

    fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
        self.lock().unwrap().close(fd)
    }

    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
        self.lock().unwrap().change_vt(vt)
    }

    fn is_active(&self) -> bool {
        self.lock().unwrap().is_active()
    }

    fn seat(&self) -> String {
        self.lock().unwrap().seat()
    }
}

/// Allows errors to be described by an error number
pub trait AsErrno: ::std::fmt::Debug {
    /// Returns the error number representing this error if any
    fn as_errno(&self) -> Option<i32>;
}

impl AsErrno for () {
    fn as_errno(&self) -> Option<i32> {
        None
    }
}

pub mod auto;
pub mod direct;
#[cfg(feature = "backend_session_libseat")]
pub mod libseat;

#[cfg(feature = "backend_session_logind")]
mod dbus;
#[cfg(feature = "backend_session_logind")]
pub use self::dbus::*;
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{Package, PackageIdSpec};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(manifest_path: &Path,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(Package::for_path(manifest_path, options.compile_opts.config));

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(manifest_path, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        let target_dir = options.compile_opts.config.target_dir(&package);
        let path = target_dir.join("doc").join(&name).join("index.html");
        if fs::metadata(&path).is_ok() {
            open_docs(&path);
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) {
    // trying xdg-open
    match Command::new("xdg-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying gnome-open
    match Command::new("gnome-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };

    // trying kde-open
    match Command::new("kde-open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) {
    match Command::new("cmd").arg("/C").arg("start").arg("").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) {
    match Command::new("open").arg(path).status() {
        Ok(_) => return,
        Err(_) => ()
    };
}
<commit_msg>Auto merge of #2468 - TheNeikos:add-warning_if_no_browser, r=alexcrichton<commit_after>use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::process::Command;

use core::{Package, PackageIdSpec};
use ops;
use util::CargoResult;

pub struct DocOptions<'a> {
    pub open_result: bool,
    pub compile_opts: ops::CompileOptions<'a>,
}

pub fn doc(manifest_path: &Path,
           options: &DocOptions) -> CargoResult<()> {
    let package = try!(Package::for_path(manifest_path, options.compile_opts.config));

    let mut lib_names = HashSet::new();
    let mut bin_names = HashSet::new();
    if options.compile_opts.spec.is_empty() {
        for target in package.targets().iter().filter(|t| t.documented()) {
            if target.is_lib() {
                assert!(lib_names.insert(target.crate_name()));
            } else {
                assert!(bin_names.insert(target.crate_name()));
            }
        }
        for bin in bin_names.iter() {
            if lib_names.contains(bin) {
                bail!("cannot document a package where a library and a binary \
                       have the same name. Consider renaming one or marking \
                       the target as `doc = false`")
            }
        }
    }

    try!(ops::compile(manifest_path, &options.compile_opts));

    if options.open_result {
        let name = if options.compile_opts.spec.len() > 1 {
            bail!("Passing multiple packages and `open` is not supported")
        } else if options.compile_opts.spec.len() == 1 {
            try!(PackageIdSpec::parse(&options.compile_opts.spec[0]))
                                             .name().replace("-", "_")
        } else {
            match lib_names.iter().chain(bin_names.iter()).nth(0) {
                Some(s) => s.to_string(),
                None => return Ok(())
            }
        };

        let target_dir = options.compile_opts.config.target_dir(&package);
        let path = target_dir.join("doc").join(&name).join("index.html");
        if fs::metadata(&path).is_ok() {
            let mut shell = options.compile_opts.config.shell();
            match open_docs(&path) {
                Ok(m) => try!(shell.status("Launching", m)),
                Err(e) => {
                    try!(shell.warn(
                            "warning: could not determine a browser to open docs with, tried:"));
                    for method in e {
                        try!(shell.warn(format!("\t{}", method)));
                    }
                }
            }
        }
    }

    Ok(())
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    use std::env;
    let mut methods = Vec::new();
    // trying $BROWSER
    match env::var("BROWSER"){
        Ok(name) => match Command::new(name).arg(path).status() {
            Ok(_) => return Ok("$BROWSER"),
            Err(_) => methods.push("$BROWSER")
        },
        Err(_) => () // Do nothing here if $BROWSER is not found
    }

    for m in ["xdg-open", "gnome-open", "kde-open"].iter() {
        match Command::new(m).arg(path).status() {
            Ok(_) => return Ok(m),
            Err(_) => methods.push(m)
        }
    }

    Err(methods)
}

#[cfg(target_os = "windows")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("cmd").arg("/C").arg("start").arg("").arg(path).status() {
        Ok(_) => return Ok("cmd /C start"),
        Err(_) => return Err(vec!["cmd /C start"])
    };
}

#[cfg(target_os = "macos")]
fn open_docs(path: &Path) -> Result<&'static str, Vec<&'static str>> {
    match Command::new("open").arg(path).status() {
        Ok(_) => return Ok("open"),
        Err(_) => return Err(vec!["open"])
    };
}
<|endoftext|>


<commit_before>// compile-flags:-Z unstable-options --show-coverage
// check-pass

//! Make sure to have some docs on your crate root

#[allow(missing_docs)]
pub mod mod_foo {
    pub struct Bar;
}

/// This is a struct with a `#[allow(missing_docs)]`
pub struct AllowTheMissingDocs {
    #[allow(missing_docs)]
    pub empty_str: String,

    /// This has
    #[allow(missing_docs)]
    /// but also has documentation comments
    pub hello: usize,

    /// The doc id just to create a boilerplate comment
    pub doc_id: Vec<u8>,
}

/// A function that has a documentation
pub fn this_is_func() {}

#[allow(missing_docs)]
pub struct DemoStruct {
    something: usize,
}

#[allow(missing_docs)]
pub mod bar {
    #[warn(missing_docs)]
    pub struct Bar { //~ WARN
        pub f: u32, //~ WARN
    }
}
<commit_msg>Extend test to ensure that items inherit lint level from the parent<commit_after>// compile-flags:-Z unstable-options --show-coverage
// check-pass

//! Make sure to have some docs on your crate root

#[allow(missing_docs)]
pub mod mod_foo {
    pub struct Bar;
}

/// This is a struct with a `#[allow(missing_docs)]`
pub struct AllowTheMissingDocs {
    #[allow(missing_docs)]
    pub empty_str: String,

    /// This has
    #[allow(missing_docs)]
    /// but also has documentation comments
    pub hello: usize,

    /// The doc id just to create a boilerplate comment
    pub doc_id: Vec<u8>,
}

/// A function that has a documentation
pub fn this_is_func() {}

#[allow(missing_docs)]
pub struct DemoStruct {
    something: usize,
}

#[allow(missing_docs)]
pub mod bar {
    #[warn(missing_docs)]
    pub struct Bar { //~ WARN
        pub f: u32, //~ WARN
    }

    pub struct NeedsNoDocs;
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

extern crate libc;
extern crate seccomp;

use std::env::args;
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};

/// Returns a list of rules that allow syscalls required for running a rust program.
fn rust_required_rules() -> Vec<(i64, Vec<seccomp::SeccompRule>)> {
    vec![
        (
            libc::SYS_sigaltstack,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_munmap,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_exit_group,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
    ]
}

/// Returns a list of rules that allow syscalls required for executing another program.
fn jailer_required_rules() -> Vec<(i64, Vec<seccomp::SeccompRule>)> {
    vec![
        (
            libc::SYS_rt_sigprocmask,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_rt_sigaction,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_execve,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_mmap,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_arch_prctl,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_set_tid_address,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_readlink,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_open,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_read,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_close,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_brk,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_sched_getaffinity,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
    ]
}

fn main() {
    let args: Vec<String> = args().collect();
    let exec_file = &args[1];
    let mut context = seccomp::SeccompFilterContext::new(
        vec![].into_iter().collect(),
        seccomp::SeccompAction::Trap,
    ).unwrap();

    // Adds required rules.
    rust_required_rules()
        .into_iter()
        .try_for_each(|(syscall_number, rules)| context.add_rules(syscall_number, None, rules))
        .unwrap();

    jailer_required_rules()
        .into_iter()
        .try_for_each(|(syscall_number, rules)| context.add_rules(syscall_number, None, rules))
        .unwrap();

    // Adds rule to allow the harmless demo Firecracker.
    context.add_rule(
        libc::SYS_write,
        None,
        seccomp::SeccompRule::new(
            vec![
                seccomp::SeccompCondition::new(
                    0,
                    seccomp::SeccompCmpOp::Eq,
                    libc::STDOUT_FILENO as u64,
                ).unwrap(),
                seccomp::SeccompCondition::new(2, seccomp::SeccompCmpOp::Eq, 14).unwrap(),
            ],
            seccomp::SeccompAction::Allow,
        ),
    );

    // Loads filters generated by the context.
    seccomp::setup_seccomp(seccomp::SeccompLevel::Advanced(context)).unwrap();

    Command::new(exec_file)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .exec();
}
<commit_msg>tests: adapt to seccomp crate changes<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

extern crate libc;
extern crate seccomp;

use std::env::args;
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};

/// Returns a list of rules that allow syscalls required for running a rust program.
fn rust_required_rules() -> Vec<(i64, Vec<seccomp::SeccompRule>)> {
    vec![
        (
            libc::SYS_sigaltstack,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_munmap,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_exit_group,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
    ]
}

/// Returns a list of rules that allow syscalls required for executing another program.
fn jailer_required_rules() -> Vec<(i64, Vec<seccomp::SeccompRule>)> {
    vec![
        (
            libc::SYS_rt_sigprocmask,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_rt_sigaction,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_execve,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_mmap,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_arch_prctl,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_set_tid_address,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_readlink,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_open,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_read,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_close,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_brk,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
        (
            libc::SYS_sched_getaffinity,
            vec![seccomp::SeccompRule::new(
                vec![],
                seccomp::SeccompAction::Allow,
            )],
        ),
    ]
}

fn main() {
    let args: Vec<String> = args().collect();
    let exec_file = &args[1];
    let mut context = seccomp::SeccompFilterContext::new(
        vec![].into_iter().collect(),
        seccomp::SeccompAction::Trap,
    ).unwrap();

    // Adds required rules.
    rust_required_rules()
        .into_iter()
        .try_for_each(|(syscall_number, rules)| context.add_rules(syscall_number, None, rules))
        .unwrap();

    jailer_required_rules()
        .into_iter()
        .try_for_each(|(syscall_number, rules)| context.add_rules(syscall_number, None, rules))
        .unwrap();

    // Adds rule to allow the harmless demo Firecracker.
    context.add_rules(
        libc::SYS_write,
        None,
        vec![seccomp::SeccompRule::new(
            vec![
                seccomp::SeccompCondition::new(
                    0,
                    seccomp::SeccompCmpOp::Eq,
                    libc::STDOUT_FILENO as u64,
                ).unwrap(),
                seccomp::SeccompCondition::new(2, seccomp::SeccompCmpOp::Eq, 14).unwrap(),
            ],
            seccomp::SeccompAction::Allow,
        )],
    ).unwrap();

    // Loads filters generated by the context.
    seccomp::setup_seccomp(seccomp::SeccompLevel::Advanced(context)).unwrap();

    Command::new(exec_file)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .exec();
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! Runtime calls emitted by the compiler.

use cast::transmute;
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
use managed::raw::BoxRepr;
use str;
use sys;
use unstable::exchange_alloc;
use cast::transmute;
#[cfg(not(stage0))]
use rt::{context, OldTaskContext};
#[cfg(not(stage0))]
use rt::local_services::borrow_local_services;

#[allow(non_camel_case_types)]
pub type rust_task = c_void;

#[cfg(target_word_size = "32")]
pub static FROZEN_BIT: uint = 0x80000000;
#[cfg(target_word_size = "64")]
pub static FROZEN_BIT: uint = 0x8000000000000000;

pub mod rustrt {
    use libc::{c_char, uintptr_t};

    pub extern {
        #[rust_stack]
        unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char;

        #[rust_stack]
        unsafe fn rust_upcall_free(ptr: *c_char);

        #[fast_ffi]
        unsafe fn rust_upcall_malloc_noswitch(td: *c_char,
                                              size: uintptr_t)
                                           -> *c_char;

        #[fast_ffi]
        unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
    }
}

#[lang="fail_"]
pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
    sys::begin_unwind_(expr, file, line);
}

#[lang="fail_bounds_check"]
pub fn fail_bounds_check(file: *c_char, line: size_t,
                                index: size_t, len: size_t) {
    let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                    len as int, index as int);
    do str::as_buf(msg) |p, _len| {
        fail_(p as *c_char, file, line);
    }
}

pub fn fail_borrowed() {
    let msg = "borrowed";
    do str::as_buf(msg) |msg_p, _| {
        do str::as_buf("???") |file_p, _| {
            fail_(msg_p as *c_char, file_p as *c_char, 0);
        }
    }
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[lang="exchange_malloc"]
#[inline(always)]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="exchange_free"]
#[inline(always)]
pub unsafe fn exchange_free(ptr: *c_char) {
    exchange_alloc::free(transmute(ptr))
}

#[lang="malloc"]
#[inline(always)]
#[cfg(stage0)] // For some reason this isn't working on windows in stage0
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    return rustrt::rust_upcall_malloc_noswitch(td, size);
}

#[lang="malloc"]
#[inline(always)]
#[cfg(not(stage0))]
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    match context() {
        OldTaskContext => {
            return rustrt::rust_upcall_malloc_noswitch(td, size);
        }
        _ => {
            let mut alloc = ::ptr::null();
            do borrow_local_services |srv| {
                alloc = srv.heap.alloc(td as *c_void, size as uint) as *c_char;
            }
            return alloc;
        }
    }
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[inline(always)]
#[cfg(stage0)]
pub unsafe fn local_free(ptr: *c_char) {
    rustrt::rust_upcall_free_noswitch(ptr);
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[inline(always)]
#[cfg(not(stage0))]
pub unsafe fn local_free(ptr: *c_char) {
    match context() {
        OldTaskContext => {
            rustrt::rust_upcall_free_noswitch(ptr);
        }
        _ => {
            do borrow_local_services |srv| {
                srv.heap.free(ptr as *c_void);
            }
        }
    }
}

#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    (*a).header.ref_count |= FROZEN_BIT;
}

#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        (*a).header.ref_count &= !FROZEN_BIT;
    }
}

#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    if ((*a).header.ref_count & FROZEN_BIT) != 0 {
        fail_borrowed();
    }
}

#[lang="strdup_uniq"]
#[inline(always)]
pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
    str::raw::from_buf_len(ptr, len)
}

#[lang="start"]
#[cfg(stage0)]
pub fn start(main: *u8, argc: int, argv: *c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: *c_char,
                      crate_map: *c_void) -> c_int;
    }
}

#[lang="start"]
#[cfg(not(stage0))]
pub fn start(main: *u8, argc: int, argv: **c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: **c_char,
                      crate_map: *c_void) -> c_int;
    }
}
<commit_msg>Reduce code bloat from managed allocations<commit_after>// Copyright 2012 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.

//! Runtime calls emitted by the compiler.

use cast::transmute;
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
use managed::raw::BoxRepr;
use str;
use sys;
use unstable::exchange_alloc;
use cast::transmute;
#[cfg(not(stage0))]
use rt::{context, OldTaskContext};
#[cfg(not(stage0))]
use rt::local_services::borrow_local_services;

#[allow(non_camel_case_types)]
pub type rust_task = c_void;

#[cfg(target_word_size = "32")]
pub static FROZEN_BIT: uint = 0x80000000;
#[cfg(target_word_size = "64")]
pub static FROZEN_BIT: uint = 0x8000000000000000;

pub mod rustrt {
    use libc::{c_char, uintptr_t};

    pub extern {
        #[rust_stack]
        unsafe fn rust_upcall_malloc(td: *c_char, size: uintptr_t) -> *c_char;

        #[rust_stack]
        unsafe fn rust_upcall_free(ptr: *c_char);

        #[fast_ffi]
        unsafe fn rust_upcall_malloc_noswitch(td: *c_char,
                                              size: uintptr_t)
                                           -> *c_char;

        #[fast_ffi]
        unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
    }
}

#[lang="fail_"]
pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
    sys::begin_unwind_(expr, file, line);
}

#[lang="fail_bounds_check"]
pub fn fail_bounds_check(file: *c_char, line: size_t,
                                index: size_t, len: size_t) {
    let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                    len as int, index as int);
    do str::as_buf(msg) |p, _len| {
        fail_(p as *c_char, file, line);
    }
}

pub fn fail_borrowed() {
    let msg = "borrowed";
    do str::as_buf(msg) |msg_p, _| {
        do str::as_buf("???") |file_p, _| {
            fail_(msg_p as *c_char, file_p as *c_char, 0);
        }
    }
}

// FIXME #4942: Make these signatures agree with exchange_alloc's signatures
#[lang="exchange_malloc"]
#[inline(always)]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="exchange_free"]
#[inline(always)]
pub unsafe fn exchange_free(ptr: *c_char) {
    exchange_alloc::free(transmute(ptr))
}

#[lang="malloc"]
#[inline(always)]
#[cfg(stage0)] // For some reason this isn't working on windows in stage0
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    return rustrt::rust_upcall_malloc_noswitch(td, size);
}

#[lang="malloc"]
#[cfg(not(stage0))]
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    match context() {
        OldTaskContext => {
            return rustrt::rust_upcall_malloc_noswitch(td, size);
        }
        _ => {
            let mut alloc = ::ptr::null();
            do borrow_local_services |srv| {
                alloc = srv.heap.alloc(td as *c_void, size as uint) as *c_char;
            }
            return alloc;
        }
    }
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[inline(always)]
#[cfg(stage0)]
pub unsafe fn local_free(ptr: *c_char) {
    rustrt::rust_upcall_free_noswitch(ptr);
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler. If a
// problem occurs, call exit instead.
#[lang="free"]
#[cfg(not(stage0))]
pub unsafe fn local_free(ptr: *c_char) {
    match context() {
        OldTaskContext => {
            rustrt::rust_upcall_free_noswitch(ptr);
        }
        _ => {
            do borrow_local_services |srv| {
                srv.heap.free(ptr as *c_void);
            }
        }
    }
}

#[lang="borrow_as_imm"]
#[inline(always)]
pub unsafe fn borrow_as_imm(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    (*a).header.ref_count |= FROZEN_BIT;
}

#[lang="return_to_mut"]
#[inline(always)]
pub unsafe fn return_to_mut(a: *u8) {
    // Sometimes the box is null, if it is conditionally frozen.
    // See e.g. #4904.
    if !a.is_null() {
        let a: *mut BoxRepr = transmute(a);
        (*a).header.ref_count &= !FROZEN_BIT;
    }
}

#[lang="check_not_borrowed"]
#[inline(always)]
pub unsafe fn check_not_borrowed(a: *u8) {
    let a: *mut BoxRepr = transmute(a);
    if ((*a).header.ref_count & FROZEN_BIT) != 0 {
        fail_borrowed();
    }
}

#[lang="strdup_uniq"]
#[inline(always)]
pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
    str::raw::from_buf_len(ptr, len)
}

#[lang="start"]
#[cfg(stage0)]
pub fn start(main: *u8, argc: int, argv: *c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: *c_char,
                      crate_map: *c_void) -> c_int;
    }
}

#[lang="start"]
#[cfg(not(stage0))]
pub fn start(main: *u8, argc: int, argv: **c_char,
             crate_map: *u8) -> int {
    use libc::getenv;
    use rt::start;

    unsafe {
        let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| {
            getenv(s).is_null()
        };
        if use_old_rt {
            return rust_start(main as *c_void, argc as c_int, argv,
                              crate_map as *c_void) as int;
        } else {
            return start(main, argc, argv, crate_map);
        }
    }

    extern {
        fn rust_start(main: *c_void, argc: c_int, argv: **c_char,
                      crate_map: *c_void) -> c_int;
    }
}
<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;

use remain::sorted;
use thiserror::Error as ThisError;

#[cfg_attr(windows, path = "sys/windows/tube.rs")]
#[cfg_attr(not(windows), path = "sys/unix/tube.rs")]
mod tube;
use std::time::Duration;

use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::Serialize;
pub use tube::*;

impl Tube {
    /// Creates a Send/Recv pair of Tubes.
    pub fn directional_pair() -> Result<(SendTube, RecvTube)> {
        let (t1, t2) = Self::pair()?;
        Ok((SendTube(t1), RecvTube(t2)))
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only send messages. Cloneable.
pub struct SendTube(Tube);

#[allow(dead_code)]
impl SendTube {
    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_send_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    pub fn send<T: Serialize>(&self, msg: &T) -> Result<()> {
        self.0.send(msg)
    }

    pub fn try_clone(&self) -> Result<Self> {
        Ok(SendTube(
            #[allow(deprecated)]
            self.0.try_clone()?,
        ))
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only recv messages.
pub struct RecvTube(Tube);

#[allow(dead_code)]
impl RecvTube {
    pub fn recv<T: DeserializeOwned>(&self) -> Result<T> {
        self.0.recv()
    }

    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_recv_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
    #[cfg(windows)]
    #[error("attempt to duplicate descriptor via broker failed")]
    BrokerDupDescriptor,
    #[error("failed to clone transport: {0}")]
    Clone(io::Error),
    #[error("tube was disconnected")]
    Disconnected,
    #[error("failed to duplicate descriptor: {0}")]
    DupDescriptor(io::Error),
    #[cfg(windows)]
    #[error("failed to flush named pipe: {0}")]
    Flush(io::Error),
    #[error("failed to serialize/deserialize json from packet: {0}")]
    Json(serde_json::Error),
    #[error("cancelled a queued async operation")]
    OperationCancelled,
    #[error("failed to crate tube pair: {0}")]
    Pair(io::Error),
    #[error("failed to receive packet: {0}")]
    Recv(io::Error),
    #[error("Received a message with a zero sized body. This should not happen.")]
    RecvUnexpectedEmptyBody,
    #[error("failed to send packet: {0}")]
    Send(crate::platform::Error),
    #[error("failed to send packet: {0}")]
    SendIo(io::Error),
    #[error("failed to write packet to intermediate buffer: {0}")]
    SendIoBuf(io::Error),
    #[error("failed to set recv timeout: {0}")]
    SetRecvTimeout(io::Error),
    #[error("failed to set send timeout: {0}")]
    SetSendTimeout(io::Error),
}

pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::sync::Arc;
    use std::sync::Barrier;
    use std::thread;
    use std::time::Duration;

    use serde::Deserialize;
    use serde::Serialize;

    use super::*;
    use crate::Event;

    #[derive(Serialize, Deserialize)]
    struct DataStruct {
        x: u32,
    }

    // Magics to identify which producer sent a message (& detect corruption).
    const PRODUCER_ID_1: u32 = 801279273;
    const PRODUCER_ID_2: u32 = 345234861;

    #[track_caller]
    fn test_event_pair(send: Event, recv: Event) {
        send.write(1).unwrap();
        recv.read_timeout(Duration::from_secs(1)).unwrap();
    }

    #[test]
    fn send_recv_no_fd() {
        let (s1, s2) = Tube::pair().unwrap();

        let test_msg = "hello world";
        s1.send(&test_msg).unwrap();
        let recv_msg: String = s2.recv().unwrap();

        assert_eq!(test_msg, recv_msg);
    }

    #[test]
    fn send_recv_one_fd() {
        #[derive(Serialize, Deserialize)]
        struct EventStruct {
            x: u32,
            b: Event,
        }

        let (s1, s2) = Tube::pair().unwrap();

        let test_msg = EventStruct {
            x: 100,
            b: Event::new().unwrap(),
        };
        s1.send(&test_msg).unwrap();
        let recv_msg: EventStruct = s2.recv().unwrap();

        assert_eq!(test_msg.x, recv_msg.x);

        test_event_pair(test_msg.b, recv_msg.b);
    }

    /// Send messages to a Tube with the given identifier (see `consume_messages`; we use this to
    /// track different message producers).
    #[track_caller]
    fn produce_messages(tube: SendTube, data: u32, barrier: Arc<Barrier>) -> SendTube {
        let data = DataStruct { x: data };
        barrier.wait();
        for _ in 0..100 {
            tube.send(&data).unwrap();
        }
        tube
    }

    /// Consumes the given number of messages from a Tube, returning the number messages read with
    /// each producer ID.
    #[track_caller]
    fn consume_messages(
        tube: RecvTube,
        count: usize,
        barrier: Arc<Barrier>,
    ) -> (RecvTube, usize, usize) {
        barrier.wait();

        let mut id1_count = 0usize;
        let mut id2_count = 0usize;

        for _ in 0..count {
            let msg = tube.recv::<DataStruct>().unwrap();
            match msg.x {
                PRODUCER_ID_1 => id1_count += 1,
                PRODUCER_ID_2 => id2_count += 1,
                _ => panic!(
                    "want message with ID {} or {}; got message w/ ID {}.",
                    PRODUCER_ID_1, PRODUCER_ID_2, msg.x
                ),
            }
        }
        (tube, id1_count, id2_count)
    }

    #[test]
    fn send_recv_mpsc() {
        let (p1, consumer) = Tube::directional_pair().unwrap();
        let p2 = p1.try_clone().unwrap();
        let start_block_p1 = Arc::new(Barrier::new(3));
        let start_block_p2 = start_block_p1.clone();
        let start_block_consumer = start_block_p1.clone();

        let p1_thread = thread::spawn(move || produce_messages(p1, PRODUCER_ID_1, start_block_p1));
        let p2_thread = thread::spawn(move || produce_messages(p2, PRODUCER_ID_2, start_block_p2));

        let (_tube, id1_count, id2_count) = consume_messages(consumer, 200, start_block_consumer);
        assert_eq!(id1_count, 100);
        assert_eq!(id2_count, 100);

        p1_thread.join().unwrap();
        p2_thread.join().unwrap();
    }

    #[test]
    fn send_recv_hash_map() {
        let (s1, s2) = Tube::pair().unwrap();

        let mut test_msg = HashMap::new();
        test_msg.insert("Red".to_owned(), Event::new().unwrap());
        test_msg.insert("White".to_owned(), Event::new().unwrap());
        test_msg.insert("Blue".to_owned(), Event::new().unwrap());
        test_msg.insert("Orange".to_owned(), Event::new().unwrap());
        test_msg.insert("Green".to_owned(), Event::new().unwrap());
        s1.send(&test_msg).unwrap();
        let mut recv_msg: HashMap<String, Event> = s2.recv().unwrap();

        let mut test_msg_keys: Vec<_> = test_msg.keys().collect();
        test_msg_keys.sort();
        let mut recv_msg_keys: Vec<_> = recv_msg.keys().collect();
        recv_msg_keys.sort();
        assert_eq!(test_msg_keys, recv_msg_keys);

        for (key, test_event) in test_msg {
            let recv_event = recv_msg.remove(&key).unwrap();
            test_event_pair(test_event, recv_event);
        }
    }
}
<commit_msg>base: RecvTube impl ReadNotifier<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;

use remain::sorted;
use thiserror::Error as ThisError;

#[cfg_attr(windows, path = "sys/windows/tube.rs")]
#[cfg_attr(not(windows), path = "sys/unix/tube.rs")]
mod tube;
use std::time::Duration;

use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::Serialize;
pub use tube::*;

impl Tube {
    /// Creates a Send/Recv pair of Tubes.
    pub fn directional_pair() -> Result<(SendTube, RecvTube)> {
        let (t1, t2) = Self::pair()?;
        Ok((SendTube(t1), RecvTube(t2)))
    }
}

use crate::AsRawDescriptor;
use crate::ReadNotifier;

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only send messages. Cloneable.
pub struct SendTube(Tube);

#[allow(dead_code)]
impl SendTube {
    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_send_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    pub fn send<T: Serialize>(&self, msg: &T) -> Result<()> {
        self.0.send(msg)
    }

    pub fn try_clone(&self) -> Result<Self> {
        Ok(SendTube(
            #[allow(deprecated)]
            self.0.try_clone()?,
        ))
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
/// A Tube end which can only recv messages.
pub struct RecvTube(Tube);

#[allow(dead_code)]
impl RecvTube {
    pub fn recv<T: DeserializeOwned>(&self) -> Result<T> {
        self.0.recv()
    }

    /// TODO(b/145998747, b/184398671): this method should be removed.
    pub fn set_recv_timeout(&self, _timeout: Option<Duration>) -> Result<()> {
        unimplemented!("To be removed/refactored upstream.");
    }

    /// Never call this function, it is for use by cros_async to provide
    /// directional wrapper types only. Using it in any other context may
    /// violate concurrency assumptions. (Type splitting across crates has put
    /// us in a situation where we can't use Rust privacy to enforce this.)
    #[deprecated]
    pub fn into_tube(self) -> Tube {
        self.0
    }
}

impl ReadNotifier for RecvTube {
    fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
        self.0.get_read_notifier()
    }
}

#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
    #[cfg(windows)]
    #[error("attempt to duplicate descriptor via broker failed")]
    BrokerDupDescriptor,
    #[error("failed to clone transport: {0}")]
    Clone(io::Error),
    #[error("tube was disconnected")]
    Disconnected,
    #[error("failed to duplicate descriptor: {0}")]
    DupDescriptor(io::Error),
    #[cfg(windows)]
    #[error("failed to flush named pipe: {0}")]
    Flush(io::Error),
    #[error("failed to serialize/deserialize json from packet: {0}")]
    Json(serde_json::Error),
    #[error("cancelled a queued async operation")]
    OperationCancelled,
    #[error("failed to crate tube pair: {0}")]
    Pair(io::Error),
    #[error("failed to receive packet: {0}")]
    Recv(io::Error),
    #[error("Received a message with a zero sized body. This should not happen.")]
    RecvUnexpectedEmptyBody,
    #[error("failed to send packet: {0}")]
    Send(crate::platform::Error),
    #[error("failed to send packet: {0}")]
    SendIo(io::Error),
    #[error("failed to write packet to intermediate buffer: {0}")]
    SendIoBuf(io::Error),
    #[error("failed to set recv timeout: {0}")]
    SetRecvTimeout(io::Error),
    #[error("failed to set send timeout: {0}")]
    SetSendTimeout(io::Error),
}

pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use std::sync::Arc;
    use std::sync::Barrier;
    use std::thread;
    use std::time::Duration;

    use serde::Deserialize;
    use serde::Serialize;

    use super::*;
    use crate::Event;

    #[derive(Serialize, Deserialize)]
    struct DataStruct {
        x: u32,
    }

    // Magics to identify which producer sent a message (& detect corruption).
    const PRODUCER_ID_1: u32 = 801279273;
    const PRODUCER_ID_2: u32 = 345234861;

    #[track_caller]
    fn test_event_pair(send: Event, recv: Event) {
        send.write(1).unwrap();
        recv.read_timeout(Duration::from_secs(1)).unwrap();
    }

    #[test]
    fn send_recv_no_fd() {
        let (s1, s2) = Tube::pair().unwrap();

        let test_msg = "hello world";
        s1.send(&test_msg).unwrap();
        let recv_msg: String = s2.recv().unwrap();

        assert_eq!(test_msg, recv_msg);
    }

    #[test]
    fn send_recv_one_fd() {
        #[derive(Serialize, Deserialize)]
        struct EventStruct {
            x: u32,
            b: Event,
        }

        let (s1, s2) = Tube::pair().unwrap();

        let test_msg = EventStruct {
            x: 100,
            b: Event::new().unwrap(),
        };
        s1.send(&test_msg).unwrap();
        let recv_msg: EventStruct = s2.recv().unwrap();

        assert_eq!(test_msg.x, recv_msg.x);

        test_event_pair(test_msg.b, recv_msg.b);
    }

    /// Send messages to a Tube with the given identifier (see `consume_messages`; we use this to
    /// track different message producers).
    #[track_caller]
    fn produce_messages(tube: SendTube, data: u32, barrier: Arc<Barrier>) -> SendTube {
        let data = DataStruct { x: data };
        barrier.wait();
        for _ in 0..100 {
            tube.send(&data).unwrap();
        }
        tube
    }

    /// Consumes the given number of messages from a Tube, returning the number messages read with
    /// each producer ID.
    #[track_caller]
    fn consume_messages(
        tube: RecvTube,
        count: usize,
        barrier: Arc<Barrier>,
    ) -> (RecvTube, usize, usize) {
        barrier.wait();

        let mut id1_count = 0usize;
        let mut id2_count = 0usize;

        for _ in 0..count {
            let msg = tube.recv::<DataStruct>().unwrap();
            match msg.x {
                PRODUCER_ID_1 => id1_count += 1,
                PRODUCER_ID_2 => id2_count += 1,
                _ => panic!(
                    "want message with ID {} or {}; got message w/ ID {}.",
                    PRODUCER_ID_1, PRODUCER_ID_2, msg.x
                ),
            }
        }
        (tube, id1_count, id2_count)
    }

    #[test]
    fn send_recv_mpsc() {
        let (p1, consumer) = Tube::directional_pair().unwrap();
        let p2 = p1.try_clone().unwrap();
        let start_block_p1 = Arc::new(Barrier::new(3));
        let start_block_p2 = start_block_p1.clone();
        let start_block_consumer = start_block_p1.clone();

        let p1_thread = thread::spawn(move || produce_messages(p1, PRODUCER_ID_1, start_block_p1));
        let p2_thread = thread::spawn(move || produce_messages(p2, PRODUCER_ID_2, start_block_p2));

        let (_tube, id1_count, id2_count) = consume_messages(consumer, 200, start_block_consumer);
        assert_eq!(id1_count, 100);
        assert_eq!(id2_count, 100);

        p1_thread.join().unwrap();
        p2_thread.join().unwrap();
    }

    #[test]
    fn send_recv_hash_map() {
        let (s1, s2) = Tube::pair().unwrap();

        let mut test_msg = HashMap::new();
        test_msg.insert("Red".to_owned(), Event::new().unwrap());
        test_msg.insert("White".to_owned(), Event::new().unwrap());
        test_msg.insert("Blue".to_owned(), Event::new().unwrap());
        test_msg.insert("Orange".to_owned(), Event::new().unwrap());
        test_msg.insert("Green".to_owned(), Event::new().unwrap());
        s1.send(&test_msg).unwrap();
        let mut recv_msg: HashMap<String, Event> = s2.recv().unwrap();

        let mut test_msg_keys: Vec<_> = test_msg.keys().collect();
        test_msg_keys.sort();
        let mut recv_msg_keys: Vec<_> = recv_msg.keys().collect();
        recv_msg_keys.sort();
        assert_eq!(test_msg_keys, recv_msg_keys);

        for (key, test_event) in test_msg {
            let recv_event = recv_msg.remove(&key).unwrap();
            test_event_pair(test_event, recv_event);
        }
    }
}
<|endoftext|>