<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//    https://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.

mod function_wrapper_cpp;
pub(crate) mod type_to_cpp;

use crate::{conversion::analysis::fun::FnAnalysisBody, types::QualifiedName, CppFilePair};
use autocxx_parser::IncludeCppConfig;
use itertools::Itertools;
use std::collections::HashSet;
use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap};

use super::{
    analysis::fun::{
        function_wrapper::{CppFunction, CppFunctionBody},
        FnAnalysis,
    },
    api::Api,
    ConvertError,
};

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    type_definition: Option<String>, // are output before main declarations
    declaration: Option<String>,
    definition: Option<String>,
    headers: Vec<Header>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct CppCodeGenerator<'a> {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
    original_name_map: CppNameMap,
    config: &'a IncludeCppConfig,
}

impl<'a> CppCodeGenerator<'a> {
    pub(crate) fn generate_cpp_code(
        inclusions: String,
        apis: &[Api<FnAnalysis>],
        config: &'a IncludeCppConfig,
    ) -> Result<Option<CppFilePair>, ConvertError> {
        let mut gen = CppCodeGenerator::new(inclusions, original_name_map_from_apis(apis), config);
        // The 'filter' on the following line is designed to ensure we don't accidentally
        // end up out of sync with needs_cpp_codegen
        gen.add_needs(apis.iter().filter(|api| api.needs_cpp_codegen()))?;
        Ok(gen.generate())
    }

    fn new(
        inclusions: String,
        original_name_map: CppNameMap,
        config: &'a IncludeCppConfig,
    ) -> Self {
        CppCodeGenerator {
            additional_functions: Vec::new(),
            inclusions,
            original_name_map,
            config,
        }
    }

    // It's important to keep this in sync with Api::needs_cpp_codegen.
    fn add_needs<'b>(
        &mut self,
        apis: impl Iterator<Item = &'a Api<FnAnalysis>>,
    ) -> Result<(), ConvertError> {
        for api in apis {
            match &api {
                Api::StringConstructor { .. } => self.generate_string_constructor(),
                Api::Function {
                    analysis:
                        FnAnalysisBody {
                            cpp_wrapper: Some(cpp_wrapper),
                            ..
                        },
                    ..
                } => self.generate_by_value_wrapper(cpp_wrapper)?,
                Api::ConcreteType { rs_definition, .. } => self.generate_typedef(
                    api.name(),
                    type_to_cpp(rs_definition, &self.original_name_map)?,
                ),
                Api::CType { typename, .. } => self.generate_ctype_typedef(typename),
                _ => panic!("Should have filtered on needs_cpp_codegen"),
            }
        }
        Ok(())
    }

    fn generate(&self) -> Option<CppFilePair> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
            let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
            let declarations = format!(
                "#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
                headers, self.inclusions, type_definitions, declarations
            );
            let definitions = self.concat_additional_items(|x| x.definition.as_ref());
            log::info!("Additional C++ decls:\n{}", declarations);
            let header_name = format!("autocxxgen_{}.h", self.config.get_mod_name());
            let definitions = format!("#include \"{}\"\n{}", header_name, definitions);
            log::info!("Additional C++ defs:\n{}", definitions);
            Some(CppFilePair {
                header: declarations.into_bytes(),
                implementation: definitions.into_bytes(),
                header_name,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> Option<&String>,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .flatten()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let makestring_name = self.config.get_makestring_name();
        let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            definition: None,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_by_value_wrapper(&mut self, details: &CppFunction) -> Result<(), ConvertError> {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = &details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                // TODO this may not be the case any longer. We
                // may be able to remove this.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| {
                Ok(format!(
                    "{} {}",
                    ty.unconverted_type(&self.original_name_map)?,
                    get_arg_name(counter)
                ))
            })
            .collect();
        let args = args?.join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or(Ok("void".to_string()), |x| {
                x.converted_type(&self.original_name_map)
            })?;
        let declaration = format!("{} {}({})", ret_type, name, args);
        let arg_list: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| {
                conv.cpp_conversion(&get_arg_name(counter), &self.original_name_map)
            })
            .collect();
        let mut arg_list = arg_list?.into_iter();
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match &details.payload {
            CppFunctionBody::Constructor => arg_list,
            CppFunctionBody::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
            CppFunctionBody::StaticMethodCall(ns, ty_id, fn_id) => {
                let underlying_function_call = ns
                    .into_iter()
                    .cloned()
                    .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
                    .join("::");
                format!("{}({})", underlying_function_call, arg_list)
            }
        };
        if let Some(ret) = &details.return_conversion {
            underlying_function_call = format!(
                "return {}",
                ret.cpp_conversion(&underlying_function_call, &self.original_name_map)?
            );
        };
        let declaration = Some(format!(
            "{} {{ {}; }}",
            declaration, underlying_function_call,
        ));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            definition: None,
            headers: vec![Header::system("memory")],
        });
        Ok(())
    }

    fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
        let cpp_name = tn.to_cpp_name();
        self.generate_typedef(tn, cpp_name)
    }

    fn generate_typedef(&mut self, tn: &QualifiedName, definition: String) {
        let our_name = tn.get_final_item();
        self.additional_functions.push(AdditionalFunction {
            type_definition: Some(format!("typedef {} {};", definition, our_name)),
            declaration: None,
            definition: None,
            headers: Vec::new(),
        })
    }
}
<commit_msg>Rename.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//    https://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.

mod function_wrapper_cpp;
pub(crate) mod type_to_cpp;

use crate::{conversion::analysis::fun::FnAnalysisBody, types::QualifiedName, CppFilePair};
use autocxx_parser::IncludeCppConfig;
use itertools::Itertools;
use std::collections::HashSet;
use type_to_cpp::{original_name_map_from_apis, type_to_cpp, CppNameMap};

use super::{
    analysis::fun::{
        function_wrapper::{CppFunction, CppFunctionBody},
        FnAnalysis,
    },
    api::Api,
    ConvertError,
};

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
struct Header {
    name: &'static str,
    system: bool,
}

impl Header {
    fn system(name: &'static str) -> Self {
        Header { name, system: true }
    }

    fn user(name: &'static str) -> Self {
        Header {
            name,
            system: false,
        }
    }

    fn include_stmt(&self) -> String {
        if self.system {
            format!("#include <{}>", self.name)
        } else {
            format!("#include \"{}\"", self.name)
        }
    }
}

struct AdditionalFunction {
    type_definition: Option<String>, // are output before main declarations
    declaration: Option<String>,
    definition: Option<String>,
    headers: Vec<Header>,
}

/// Generates additional C++ glue functions needed by autocxx.
/// In some ways it would be preferable to be able to pass snippets
/// of C++ through to `cxx` for inclusion in the C++ file which it
/// generates, and perhaps we'll explore that in future. But for now,
/// autocxx generates its own _additional_ C++ files which therefore
/// need to be built and included in linking procedures.
pub(crate) struct CppCodeGenerator<'a> {
    additional_functions: Vec<AdditionalFunction>,
    inclusions: String,
    original_name_map: CppNameMap,
    config: &'a IncludeCppConfig,
}

impl<'a> CppCodeGenerator<'a> {
    pub(crate) fn generate_cpp_code(
        inclusions: String,
        apis: &[Api<FnAnalysis>],
        config: &'a IncludeCppConfig,
    ) -> Result<Option<CppFilePair>, ConvertError> {
        let mut gen = CppCodeGenerator::new(inclusions, original_name_map_from_apis(apis), config);
        // The 'filter' on the following line is designed to ensure we don't accidentally
        // end up out of sync with needs_cpp_codegen
        gen.add_needs(apis.iter().filter(|api| api.needs_cpp_codegen()))?;
        Ok(gen.generate())
    }

    fn new(
        inclusions: String,
        original_name_map: CppNameMap,
        config: &'a IncludeCppConfig,
    ) -> Self {
        CppCodeGenerator {
            additional_functions: Vec::new(),
            inclusions,
            original_name_map,
            config,
        }
    }

    // It's important to keep this in sync with Api::needs_cpp_codegen.
    fn add_needs<'b>(
        &mut self,
        apis: impl Iterator<Item = &'a Api<FnAnalysis>>,
    ) -> Result<(), ConvertError> {
        for api in apis {
            match &api {
                Api::StringConstructor { .. } => self.generate_string_constructor(),
                Api::Function {
                    analysis:
                        FnAnalysisBody {
                            cpp_wrapper: Some(cpp_wrapper),
                            ..
                        },
                    ..
                } => self.generate_cpp_function(cpp_wrapper)?,
                Api::ConcreteType { rs_definition, .. } => self.generate_typedef(
                    api.name(),
                    type_to_cpp(rs_definition, &self.original_name_map)?,
                ),
                Api::CType { typename, .. } => self.generate_ctype_typedef(typename),
                _ => panic!("Should have filtered on needs_cpp_codegen"),
            }
        }
        Ok(())
    }

    fn generate(&self) -> Option<CppFilePair> {
        if self.additional_functions.is_empty() {
            None
        } else {
            let headers: HashSet<Header> = self
                .additional_functions
                .iter()
                .map(|x| x.headers.iter().cloned())
                .flatten()
                .collect();
            let headers = headers.iter().map(|x| x.include_stmt()).join("\n");
            let type_definitions = self.concat_additional_items(|x| x.type_definition.as_ref());
            let declarations = self.concat_additional_items(|x| x.declaration.as_ref());
            let declarations = format!(
                "#ifndef __AUTOCXXGEN_H__\n#define __AUTOCXXGEN_H__\n\n{}\n{}\n{}\n{}#endif // __AUTOCXXGEN_H__\n",
                headers, self.inclusions, type_definitions, declarations
            );
            let definitions = self.concat_additional_items(|x| x.definition.as_ref());
            log::info!("Additional C++ decls:\n{}", declarations);
            let header_name = format!("autocxxgen_{}.h", self.config.get_mod_name());
            let definitions = format!("#include \"{}\"\n{}", header_name, definitions);
            log::info!("Additional C++ defs:\n{}", definitions);
            Some(CppFilePair {
                header: declarations.into_bytes(),
                implementation: definitions.into_bytes(),
                header_name,
            })
        }
    }

    fn concat_additional_items<F>(&self, field_access: F) -> String
    where
        F: FnMut(&AdditionalFunction) -> Option<&String>,
    {
        let mut s = self
            .additional_functions
            .iter()
            .map(field_access)
            .flatten()
            .join("\n");
        s.push('\n');
        s
    }

    fn generate_string_constructor(&mut self) {
        let makestring_name = self.config.get_makestring_name();
        let declaration = Some(format!("inline std::unique_ptr<std::string> {}(::rust::Str str) {{ return std::make_unique<std::string>(std::string(str)); }}", makestring_name));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            definition: None,
            headers: vec![
                Header::system("memory"),
                Header::system("string"),
                Header::user("cxx.h"),
            ],
        })
    }

    fn generate_cpp_function(&mut self, details: &CppFunction) -> Result<(), ConvertError> {
        // Even if the original function call is in a namespace,
        // we generate this wrapper in the global namespace.
        // We could easily do this the other way round, and when
        // cxx::bridge comes to support nested namespace mods then
        // we wil wish to do that to avoid name conflicts. However,
        // at the moment this is simpler because it avoids us having
        // to generate namespace blocks in the generated C++.
        let is_a_method = details.is_a_method;
        let name = &details.wrapper_function_name;
        let get_arg_name = |counter: usize| -> String {
            if is_a_method && counter == 0 {
                // For method calls that we generate, the first
                // argument name needs to be such that we recognize
                // it as a method in the second invocation of
                // bridge_converter after it's flowed again through
                // bindgen.
                // TODO this may not be the case any longer. We
                // may be able to remove this.
                "autocxx_gen_this".to_string()
            } else {
                format!("arg{}", counter)
            }
        };
        let args: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, ty)| {
                Ok(format!(
                    "{} {}",
                    ty.unconverted_type(&self.original_name_map)?,
                    get_arg_name(counter)
                ))
            })
            .collect();
        let args = args?.join(", ");
        let ret_type = details
            .return_conversion
            .as_ref()
            .map_or(Ok("void".to_string()), |x| {
                x.converted_type(&self.original_name_map)
            })?;
        let declaration = format!("{} {}({})", ret_type, name, args);
        let arg_list: Result<Vec<_>, _> = details
            .argument_conversion
            .iter()
            .enumerate()
            .map(|(counter, conv)| {
                conv.cpp_conversion(&get_arg_name(counter), &self.original_name_map)
            })
            .collect();
        let mut arg_list = arg_list?.into_iter();
        let receiver = if is_a_method { arg_list.next() } else { None };
        let arg_list = arg_list.join(", ");
        let mut underlying_function_call = match &details.payload {
            CppFunctionBody::Constructor => arg_list,
            CppFunctionBody::FunctionCall(ns, id) => match receiver {
                Some(receiver) => format!("{}.{}({})", receiver, id.to_string(), arg_list),
                None => {
                    let underlying_function_call = ns
                        .into_iter()
                        .cloned()
                        .chain(std::iter::once(id.to_string()))
                        .join("::");
                    format!("{}({})", underlying_function_call, arg_list)
                }
            },
            CppFunctionBody::StaticMethodCall(ns, ty_id, fn_id) => {
                let underlying_function_call = ns
                    .into_iter()
                    .cloned()
                    .chain([ty_id.to_string(), fn_id.to_string()].iter().cloned())
                    .join("::");
                format!("{}({})", underlying_function_call, arg_list)
            }
        };
        if let Some(ret) = &details.return_conversion {
            underlying_function_call = format!(
                "return {}",
                ret.cpp_conversion(&underlying_function_call, &self.original_name_map)?
            );
        };
        let declaration = Some(format!(
            "{} {{ {}; }}",
            declaration, underlying_function_call,
        ));
        self.additional_functions.push(AdditionalFunction {
            type_definition: None,
            declaration,
            definition: None,
            headers: vec![Header::system("memory")],
        });
        Ok(())
    }

    fn generate_ctype_typedef(&mut self, tn: &QualifiedName) {
        let cpp_name = tn.to_cpp_name();
        self.generate_typedef(tn, cpp_name)
    }

    fn generate_typedef(&mut self, tn: &QualifiedName, definition: String) {
        let our_name = tn.get_final_item();
        self.additional_functions.push(AdditionalFunction {
            type_definition: Some(format!("typedef {} {};", definition, our_name)),
            declaration: None,
            definition: None,
            headers: Vec::new(),
        })
    }
}
<|endoftext|>


<commit_before>use std::any::TypeId;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::enums::Enum;
use crate::reflect::enums::dynamic::DynamicEnumDescriptor;
use crate::reflect::enums::generated::GeneratedEnumDescriptor;
use crate::reflect::file::index::FileIndexEnumEntry;
use crate::reflect::file::FileDescriptorImpl;
use crate::reflect::FileDescriptor;
use crate::reflect::MessageDescriptor;
use crate::EnumFull;

pub(crate) mod dynamic;
pub(crate) mod generated;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone, Eq, PartialEq)]
pub struct EnumValueDescriptor {
    pub(crate) enum_descriptor: EnumDescriptor,
    pub(crate) index: usize,
}

impl Hash for EnumValueDescriptor {
    fn hash<H: Hasher>(&self, state: &mut H) {
        Hash::hash(&self.value(), state)
    }
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("enum_descriptor", &self.enum_descriptor)
            .field("name", &self.name())
            .finish()
    }
}

impl EnumValueDescriptor {
    pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor,
            index,
        }
    }

    /// `.proto` object which declared this value.
    pub fn proto(&self) -> &EnumValueDescriptorProto {
        &self.enum_descriptor.proto().value[self.index as usize]
    }

    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    /// Fully qualified enum value name: fully qualified enum name followed by value name.
    pub fn full_name(&self) -> String {
        format!("{}.{}", self.enum_descriptor.full_name(), self.name())
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto().number()
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        &self.enum_descriptor
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// ```
    /// # use protobuf::well_known_types::NullValue;
    /// # use protobuf::EnumFull;
    /// # use protobuf::reflect::EnumValueDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: Figure out why.
    /// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
    /// let null: Option<NullValue> = value.cast();
    /// assert_eq!(Some(NullValue::NULL_VALUE), null);
    /// # }
    /// ```
    pub fn cast<E: EnumFull>(&self) -> Option<E> {
        if self.enum_descriptor != E::enum_descriptor() {
            return None;
        }
        E::from_i32(self.value())
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumDescriptor {
    file_descriptor: FileDescriptor,
    index: usize,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            // TODO
            //.field("full_name", &self.full_name)
            .finish_non_exhaustive()
    }
}

impl EnumDescriptor {
    pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get internal index.
    ///
    /// Only needed for codegen.
    #[doc(hidden)]
    pub fn index_in_file_for_codegen(&self) -> usize {
        self.index
    }

    fn get_impl(&self) -> EnumDescriptorImplRef {
        match &self.file_descriptor.imp {
            FileDescriptorImpl::Generated(g) => {
                EnumDescriptorImplRef::Generated(&g.enums[self.index])
            }
            FileDescriptorImpl::Dynamic(d) => EnumDescriptorImplRef::Dynamic(&d.enums[self.index]),
        }
    }

    /// Descriptor objects which defined this enum.
    pub fn proto(&self) -> &EnumDescriptorProto {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.proto,
            EnumDescriptorImplRef::Dynamic(d) => d.proto(),
        }
    }

    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    fn index_entry(&self) -> &FileIndexEnumEntry {
        self.file_descriptor.enum_index_entry(self.index)
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.full_name,
            EnumDescriptorImplRef::Dynamic(d) => &d.full_name,
        }
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: EnumFull>() -> EnumDescriptor {
        E::enum_descriptor()
    }

    #[doc(hidden)]
    pub fn new_generated_2(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get a message containing this message, or `None` if this message is declared at file level.
    pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
        self.index_entry()
            .enclosing_message
            .map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
    }

    /// This enum values
    pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
        let value_len = self.proto().value.len();
        (0..value_len).map(move |index| EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// First variant (also default in proto3).
    pub fn first_value(&self) -> EnumValueDescriptor {
        self.values().next().unwrap()
    }

    /// Find enum variant by name
    pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_name
            .get(name)?;
        Some(EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_number
            .get(&number)?;
        Some(self.value_by_index(index))
    }

    /// Get enum variant by index (as declared in `.proto` file).
    pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        }
    }

    /// Default enum value (first variant)
    pub fn default_value(&self) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index: 0,
        }
    }

    /// Find enum variant by number or return default (first) enum value
    pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => self.default_value(),
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::EnumFull;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: figure out why
    /// let descriptor: EnumDescriptor = Label::enum_descriptor();
    ///
    /// assert!(descriptor.is::<Label>())
    /// }
    /// ```
    pub fn is<E: Enum>(&self) -> bool {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
            EnumDescriptorImplRef::Dynamic(..) => false,
        }
    }
}

enum EnumDescriptorImplRef<'a> {
    Generated(&'static GeneratedEnumDescriptor),
    Dynamic(&'a DynamicEnumDescriptor),
}

#[cfg(test)]
mod test {
    use crate::descriptor::field_descriptor_proto::Type;
    use crate::descriptor::FieldDescriptorProto;
    use crate::well_known_types::NullValue;
    use crate::EnumFull;
    use crate::MessageFull;

    #[test]
    #[cfg_attr(miri, ignore)] // Too slow on Miri.
    fn enclosing_message() {
        assert_eq!(
            Some(FieldDescriptorProto::descriptor_static()),
            Type::enum_descriptor().enclosing_message()
        );
        assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
    }
}
<commit_msg>Better Hash for EnumValueDescriptor<commit_after>use std::any::TypeId;
use std::fmt;
use std::hash::Hash;

use crate::descriptor::EnumDescriptorProto;
use crate::descriptor::EnumValueDescriptorProto;
use crate::enums::Enum;
use crate::reflect::enums::dynamic::DynamicEnumDescriptor;
use crate::reflect::enums::generated::GeneratedEnumDescriptor;
use crate::reflect::file::index::FileIndexEnumEntry;
use crate::reflect::file::FileDescriptorImpl;
use crate::reflect::FileDescriptor;
use crate::reflect::MessageDescriptor;
use crate::EnumFull;

pub(crate) mod dynamic;
pub(crate) mod generated;

/// Description for enum variant.
///
/// Used in reflection.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumValueDescriptor {
    pub(crate) enum_descriptor: EnumDescriptor,
    pub(crate) index: usize,
}

fn _assert_send_sync() {
    fn _assert_send_sync<T: Send + Sync>() {}
    _assert_send_sync::<EnumValueDescriptor>();
}

impl fmt::Debug for EnumValueDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumValueDescriptor")
            .field("enum_descriptor", &self.enum_descriptor)
            .field("name", &self.name())
            .finish()
    }
}

impl EnumValueDescriptor {
    pub(crate) fn new(enum_descriptor: EnumDescriptor, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor,
            index,
        }
    }

    /// `.proto` object which declared this value.
    pub fn proto(&self) -> &EnumValueDescriptorProto {
        &self.enum_descriptor.proto().value[self.index as usize]
    }

    /// Name of enum variant as specified in proto file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    /// Fully qualified enum value name: fully qualified enum name followed by value name.
    pub fn full_name(&self) -> String {
        format!("{}.{}", self.enum_descriptor.full_name(), self.name())
    }

    /// `i32` value of the enum variant
    pub fn value(&self) -> i32 {
        self.proto().number()
    }

    /// Get descriptor of enum holding this value.
    pub fn enum_descriptor(&self) -> &EnumDescriptor {
        &self.enum_descriptor
    }

    /// Convert this value descriptor into proper enum object.
    ///
    /// ```
    /// # use protobuf::well_known_types::NullValue;
    /// # use protobuf::EnumFull;
    /// # use protobuf::reflect::EnumValueDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: Figure out why.
    /// let value: EnumValueDescriptor = NullValue::NULL_VALUE.descriptor();
    /// let null: Option<NullValue> = value.cast();
    /// assert_eq!(Some(NullValue::NULL_VALUE), null);
    /// # }
    /// ```
    pub fn cast<E: EnumFull>(&self) -> Option<E> {
        if self.enum_descriptor != E::enum_descriptor() {
            return None;
        }
        E::from_i32(self.value())
    }
}

/// Dynamic representation of enum type.
///
/// Can be used in reflective operations.
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct EnumDescriptor {
    file_descriptor: FileDescriptor,
    index: usize,
}

impl fmt::Debug for EnumDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("EnumDescriptor")
            // TODO
            //.field("full_name", &self.full_name)
            .finish_non_exhaustive()
    }
}

impl EnumDescriptor {
    pub(crate) fn new(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get internal index.
    ///
    /// Only needed for codegen.
    #[doc(hidden)]
    pub fn index_in_file_for_codegen(&self) -> usize {
        self.index
    }

    fn get_impl(&self) -> EnumDescriptorImplRef {
        match &self.file_descriptor.imp {
            FileDescriptorImpl::Generated(g) => {
                EnumDescriptorImplRef::Generated(&g.enums[self.index])
            }
            FileDescriptorImpl::Dynamic(d) => EnumDescriptorImplRef::Dynamic(&d.enums[self.index]),
        }
    }

    /// Descriptor objects which defined this enum.
    pub fn proto(&self) -> &EnumDescriptorProto {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.proto,
            EnumDescriptorImplRef::Dynamic(d) => d.proto(),
        }
    }

    /// Enum name as given in `.proto` file
    pub fn name(&self) -> &str {
        self.proto().name()
    }

    fn index_entry(&self) -> &FileIndexEnumEntry {
        self.file_descriptor.enum_index_entry(self.index)
    }

    /// Fully qualified protobuf name of enum
    pub fn full_name(&self) -> &str {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => &g.full_name,
            EnumDescriptorImplRef::Dynamic(d) => &d.full_name,
        }
    }

    /// Get `EnumDescriptor` object for given enum type
    pub fn for_type<E: EnumFull>() -> EnumDescriptor {
        E::enum_descriptor()
    }

    #[doc(hidden)]
    pub fn new_generated_2(file_descriptor: FileDescriptor, index: usize) -> EnumDescriptor {
        EnumDescriptor {
            file_descriptor,
            index,
        }
    }

    /// Get a message containing this message, or `None` if this message is declared at file level.
    pub fn enclosing_message(&self) -> Option<MessageDescriptor> {
        self.index_entry()
            .enclosing_message
            .map(|i| MessageDescriptor::new(self.file_descriptor.clone(), i))
    }

    /// This enum values
    pub fn values<'a>(&'a self) -> impl Iterator<Item = EnumValueDescriptor> + 'a {
        let value_len = self.proto().value.len();
        (0..value_len).map(move |index| EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// First variant (also default in proto3).
    pub fn first_value(&self) -> EnumValueDescriptor {
        self.values().next().unwrap()
    }

    /// Find enum variant by name
    pub fn value_by_name(&self, name: &str) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_name
            .get(name)?;
        Some(EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        })
    }

    /// Find enum variant by number
    pub fn value_by_number(&self, number: i32) -> Option<EnumValueDescriptor> {
        let index = *self.file_descriptor.index().index.enums[self.index]
            .index_by_number
            .get(&number)?;
        Some(self.value_by_index(index))
    }

    /// Get enum variant by index (as declared in `.proto` file).
    pub fn value_by_index(&self, index: usize) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index,
        }
    }

    /// Default enum value (first variant)
    pub fn default_value(&self) -> EnumValueDescriptor {
        EnumValueDescriptor {
            enum_descriptor: self.clone(),
            index: 0,
        }
    }

    /// Find enum variant by number or return default (first) enum value
    pub fn value_by_number_or_default(&self, number: i32) -> EnumValueDescriptor {
        match self.value_by_number(number) {
            Some(v) => v,
            None => self.default_value(),
        }
    }

    /// Check if this enum descriptor corresponds given enum type
    ///
    /// ```
    /// # use protobuf::EnumFull;
    /// # use protobuf::descriptor::field_descriptor_proto::Label;
    /// # use protobuf::reflect::EnumDescriptor;
    ///
    /// # if !cfg!(miri) {
    /// # // TODO: figure out why
    /// let descriptor: EnumDescriptor = Label::enum_descriptor();
    ///
    /// assert!(descriptor.is::<Label>())
    /// }
    /// ```
    pub fn is<E: Enum>(&self) -> bool {
        match self.get_impl() {
            EnumDescriptorImplRef::Generated(g) => g.type_id == TypeId::of::<E>(),
            EnumDescriptorImplRef::Dynamic(..) => false,
        }
    }
}

enum EnumDescriptorImplRef<'a> {
    Generated(&'static GeneratedEnumDescriptor),
    Dynamic(&'a DynamicEnumDescriptor),
}

#[cfg(test)]
mod test {
    use crate::descriptor::field_descriptor_proto::Type;
    use crate::descriptor::FieldDescriptorProto;
    use crate::well_known_types::NullValue;
    use crate::EnumFull;
    use crate::MessageFull;

    #[test]
    #[cfg_attr(miri, ignore)] // Too slow on Miri.
    fn enclosing_message() {
        assert_eq!(
            Some(FieldDescriptorProto::descriptor_static()),
            Type::enum_descriptor().enclosing_message()
        );
        assert_eq!(None, NullValue::enum_descriptor().enclosing_message());
    }
}
<|endoftext|>


<commit_before>/* kernel::sgash.rs */

use core::*;
use core::str::*;
use core::option::{Some, Option, None}; // Match statement
use core::iter::Iterator;
use core::vec::Vec;
use core::mem::Allocator;
use kernel::*;
use super::super::platform::*;

pub static mut buffer: cstr = cstr {
				p: 0 as *mut u8,
				p_cstr_i: 0,
				max: 0
			      };
pub fn putchar(key: char) {
    unsafe {
	/*
	 * We need to include a blank asm call to prevent rustc
	 * from optimizing this part out
	 */
	asm!("");
	io::write_char(key, io::UART0);
    }
}

#[lang = "exchange_free"]
fn putstr(msg: &str) {
    for c in slice::iter(as_bytes(msg)) {
	putchar(*c as char);
    }
}

pub unsafe fn output(s: cstr)
{
    let mut x = 0;
    while *(((s.p as uint)+x) as *char) != '\0'
    {
	putchar(*(((s.p as uint)+x) as *char));
	x += 1;
    }
}

pub unsafe fn parsekey(x: char) {
    let x = x as u8;
    // Set this to false to learn the keycodes of various keys!
    // Key codes are printed backwards because life is hard
    if (true) {
	match x {
	    13		=>	{ prompt(); }
	    127		=>	{
		if (buffer.delete_char()) {
		    putchar('');
		    putchar(' ');
		    putchar('');
		}
	    }
	    _		=>	{
		if (buffer.add_char(x)) { putchar(x as char); }
	    }
	}
    }
    else {
	keycode(x);
    }
}

fn keycode(x: u8) {
    let mut x = x;
    while ( x != 0 ) {
	putchar((x%10+ ('0' as u8) ) as char);
	x = x/10;
    }
    putchar(' ');
}

pub unsafe fn init() {
    buffer = cstr::new(256);
    prompt();
}

unsafe fn prompt() {
    if (buffer.eq(&cstr::from_str(&"ls"))) { putstr( &"\na\tb") };
    match buffer.getarg(' ', 0) {
	Some(y)	=> {
	    if(y.eq(&cstr::from_str(&"cat"))) {
		match buffer.getarg(' ', 1) {
		    Some(x)	=> {
			if(x.eq(&cstr::from_str(&"a"))) { putstr( &"\nHello"); }
			if(x.eq(&cstr::from_str(&"b"))) { putstr( &"\nworld!"); }
		    }
		    None	=> { }
		};
	    }
	}
	None	=> { }
    };

    putstr(&"\nsgash > ");
    buffer.reset();
}

/* BUFFER MODIFICATION FUNCTIONS */

struct cstr {
    p: *mut u8,
    p_cstr_i: uint,
    max: uint
}

impl cstr {
    pub unsafe fn new(size: uint) -> cstr {
	let (x, y) = memory::allocator.alloc(size);
	let this = cstr {
	    p: x,
	    p_cstr_i: 0,
	    max: y
	};
	this
    }

    unsafe fn from_str(s: &str) -> cstr {
	let mut this = cstr::new(256);
	for c in slice::iter(as_bytes(s)) {
	    this.add_char(*c);
	};
	this
    }

    unsafe fn add_char(&mut self, x: u8) -> bool{
	if (self.p_cstr_i == self.max) { return false; }
	*(((self.p as uint)+self.p_cstr_i) as *mut u8) = x;
	self.p_cstr_i += 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn delete_char(&mut self) -> bool {
	if (self.p_cstr_i == 0) { return false; }
	self.p_cstr_i -= 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn reset(&mut self) {
	self.p_cstr_i = 0;
	*(self.p as *mut char) = '\0';
    }

    unsafe fn eq(&self, other: &cstr) -> bool {
	if (self.p_cstr_i != other.p_cstr_i) { return false; }
	else {
	    let mut x = 0;
	    let mut selfp: uint = self.p as uint;
	    let mut otherp: uint = other.p as uint;
	    while (x < self.p_cstr_i) {
		if (*(selfp as *char) != *(otherp as *char)) { return false; }
		selfp += 1;
		otherp += 1;
		x += 1;
	    }
	    true
	}
    }

    unsafe fn getarg(&self, delim: char, mut k: uint) -> Option<cstr> {
	let mut ind: uint = 0;
	let mut found = k == 0;
	let mut selfp: uint = self.p as uint;
	let mut s = cstr::new(256);
	loop {
	    if (*(selfp as *char) == '\0') {
		// End of string
		if (found) { return Some(s); }
		else { return None; }

	    };
	    if (*(selfp as *u8) == delim as u8) {
		if (found) { return Some(s); }
		k -= 1;
	    };
	    if (found) {
		s.add_char(*(selfp as *u8));
	    };
	    found = k == 0;
	    selfp += 1;
	    ind += 1;
	    if (ind == self.max) {
		putstr(&"\nSometing broke!");
		return None;
	    }
	}
    }

}

<commit_msg>Fixed freezing and infinite loops. HOwever, garbage collection still sucks, someone should look into it maybe<commit_after>/* kernel::sgash.rs */

use core::*;
use core::str::*;
use core::option::{Some, Option, None}; // Match statement
use core::iter::Iterator;
use core::vec::Vec;
use core::mem::Allocator;
use kernel::*;
use super::super::platform::*;

pub static mut buffer: cstr = cstr {
				p: 0 as *mut u8,
				p_cstr_i: 0,
				max: 0
			      };
pub fn putchar(key: char) {
    unsafe {
	/*
	 * We need to include a blank asm call to prevent rustc
	 * from optimizing this part out
	 */
	asm!("");
	io::write_char(key, io::UART0);
    }
}

#[lang = "exchange_free"]
fn putstr(msg: &str) {
    for c in slice::iter(as_bytes(msg)) {
	putchar(*c as char);
    }
}

pub unsafe fn putcstr(s: cstr)
{
    let mut p = s.p as uint;
    while *(p as *char) != '\0'
    {
	putchar(*(p as *char));
	p += 1;
    }
}

pub unsafe fn parsekey(x: char) {
    let x = x as u8;
    // Set this to false to learn the keycodes of various keys!
    // Key codes are printed backwards because life is hard
    if (true) {
	match x {
	    13		=>	{ prompt(); }
	    127		=>	{
		if (buffer.delete_char()) {
		    putchar('');
		    putchar(' ');
		    putchar('');
		}
	    }
	    _		=>	{
		if (buffer.add_char(x)) { putchar(x as char); }
	    }
	}
    }
    else {
	keycode(x);
    }
}

fn keycode(x: u8) {
    let mut x = x;
    while ( x != 0 ) {
	putchar((x%10+ ('0' as u8) ) as char);
	x = x/10;
    }
    putchar(' ');
}

pub unsafe fn init() {
    buffer = cstr::new(256);
    prompt();
}

unsafe fn prompt() {
    if (buffer.streq(&"ls")) { putstr( &"\na\tb") };
    match buffer.getarg(' ', 0) {
	Some(y)	=> {
	    if(y.streq(&"cat")) {
		match buffer.getarg(' ', 1) {
		    Some(x)	=> {
			if(x.streq(&"a")) { putstr( &"\nHello"); }
			if(x.streq(&"b")) { putstr( &"\nworld!"); }
		    }
		    None	=> { }
		};
	    }
	}
	None	=> { }
    };

    putstr(&"\nsgash > ");
    buffer.reset();
}

/* BUFFER MODIFICATION FUNCTIONS */

struct cstr {
    p: *mut u8,
    p_cstr_i: uint,
    max: uint
}

impl cstr {
    pub unsafe fn new(size: uint) -> cstr {
	// Sometimes this doesn't allocate enough memory and gets stuck...
	let (x, y) = memory::allocator.alloc(size);
	let this = cstr {
	    p: x,
	    p_cstr_i: 0,
	    max: y
	};
	*(((this.p as uint)+this.p_cstr_i) as *mut char) = '\0';
	this
    }

    unsafe fn from_str(s: &str) -> cstr {
	let mut this = cstr::new(256);
	for c in slice::iter(as_bytes(s)) {
	    this.add_char(*c);
	};
	this
    }

    fn len(&self) -> uint { self.p_cstr_i }

    // HELP THIS DOESN'T WORK THERE IS NO GARBAGE COLLECTION!!!
    unsafe fn destroy(&self) { memory::allocator.free(self.p); }

    unsafe fn add_char(&mut self, x: u8) -> bool{
	if (self.p_cstr_i == self.max) { return false; }
	*(((self.p as uint)+self.p_cstr_i) as *mut u8) = x;
	self.p_cstr_i += 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn delete_char(&mut self) -> bool {
	if (self.p_cstr_i == 0) { return false; }
	self.p_cstr_i -= 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn reset(&mut self) {
	self.p_cstr_i = 0;
	*(self.p as *mut char) = '\0';
    }

    unsafe fn eq(&self, other: &cstr) -> bool {
	if (self.len() != other.len()) { return false; }
	else {
	    let mut x = 0;
	    let mut selfp: uint = self.p as uint;
	    let mut otherp: uint = other.p as uint;
	    while (x < self.len()) {
		if (*(selfp as *char) != *(otherp as *char)) { return false; }
		selfp += 1;
		otherp += 1;
		x += 1;
	    }
	    true
	}
    }

    unsafe fn streq(&self, other: &str) -> bool {
	let mut x = 0;
	let mut selfp: uint = self.p as uint;
	for c in slice::iter(as_bytes(other)) {
	    if( *c != *(selfp as *u8) ) { return false; }
	    selfp += 1;
	};
	*(selfp as *char) == '\0'
    }

    unsafe fn getarg(&self, delim: char, mut k: uint) -> Option<cstr> {
	let mut ind: uint = 0;
	let mut found = k == 0;
	let mut selfp: uint = self.p as uint;
	let mut s = cstr::new(256);
	loop {
	    if (*(selfp as *char) == '\0') {
		// End of string
		if (found) { return Some(s); }
		else { return None; }
	    };
	    if (*(selfp as *u8) == delim as u8) {
		if (found) { return Some(s); }
		k -= 1;
	    };
	    if (found) {
		s.add_char(*(selfp as *u8));
	    };
	    found = k == 0;
	    selfp += 1;
	    ind += 1;
	    if (ind == self.max) {
		putstr(&"\nSomething broke!");
		return None;
	    }
	}
    }

    unsafe fn split(&self, delim: char) -> (cstr, cstr) {
	let mut selfp: uint = self.p as uint;
	let mut beg = cstr::new(256);
	let mut end = cstr::new(256);
	let mut found = false;
	loop {
	    if (*(selfp as *char) == '\0') {
		return (beg, end);
	    }
	    else if (*(selfp as *u8) == delim as u8) {
		found = true;
	    }
	    else if (!found) {
		beg.add_char(*(selfp as *u8));
	    }
	    else if (found) {
		end.add_char(*(selfp as *u8));
	    };
	    selfp += 1;
	}
    }

}

<|endoftext|>


<commit_before>#![deny(missing_docs)]
// Copyright 2015 Adrien Champion. See the COPYRIGHT file at the top-level
// directory of this distribution.
//
// 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.

/*! K-induction.

Unrolls backwards.
*/

extern crate term ;
extern crate system ;
extern crate event ;
extern crate unroll ;

use std::thread::sleep_ms ;

use term::Offset2 ;
use term::smt::* ;
use term::smt::sync::* ;

use event::msg::{ Info, Event, MsgDown} ;

use system::{ Sys, Prop } ;

use unroll::* ;

macro_rules! try_error {
  ($e:expr, $event:expr) => (
    match $e {
      Ok(v) => v,
      Err(e) => {
        $event.error( & format!("{:?}", e) ) ;
        $event.done(Info::Error) ;
        return ()
      },
    }
  )
}

/** K-induction. */
pub struct KInd ;
unsafe impl Send for KInd {}
impl event::CanRun for KInd {
  fn id(& self) -> event::Technique { event::Technique::KInd }

  fn run(
    & self, sys: Sys, props: Vec<Prop>, mut event: Event
  ) {
    // event.log(
    //   & format!("checking {} propertie(s) on system {}", props.len(), sys.sym())
    // ) ;

    // event.log("creating solver") ;

    let conf = SolverConf::z3().print_success() ;
    let factory = event.factory().clone() ;
    let mut actlit = Actlit::mk(factory.clone()) ;

    // Reversed to unroll backwards.
    let check_offset = Offset2::init().rev() ;
    let mut k = check_offset.clone() ;

    match Solver::mk(z3_cmd(), conf, factory.clone()) {
      Err(e) => event.error( & format!("could not create solver\n{:?}", e) ),
      Ok(mut solver) => {

        // event.log("creating manager, declaring actlits") ;
        let mut props = try_error!(
          PropManager::mk(factory.clone(), props, & mut solver),
          event
        ) ;

        // event.log("declaring functions, init and trans") ;
        try_error!(
          sys.defclare_funs(& mut solver), event
        ) ;

        // event.log("declare svar@0") ;
        try_error!(
          sys.declare_svars(& mut solver, check_offset.next()), event
          // Unrolling backwards ~~~~~~~~~~~~~~~~~~~~~~^^^^
        ) ;

        // event.log( & format!("unroll {}", k) ) ;
        try_error!( sys.unroll(& mut solver, & k), event ) ;

        'out: loop {

          props.reset_inhibited() ;

          // event.log( & format!("activating state at {}", k) ) ;
          try_error!(
            props.activate_state(& mut solver, & k), event
          ) ;

          match event.recv() {
            None => return (),
            Some(msgs) => for msg in msgs {
              match msg {
                MsgDown::Forget(ps) => try_error!(
                  props.forget(& mut solver, & ps),
                  event
                ),
                MsgDown::Invariants(_,_) => event.log(
                  "received invariants, skipping"
                ),
                _ => event.error("unknown message")
              }
            },
          } ;

          if props.none_left() {
            event.done_at(& k.curr()) ;
            break
          }

          'split: loop {

              if let Some(one_prop_false) = props.one_false_next() {

              let lit = actlit.mk_fresh_declare(& mut solver).unwrap() ;
              // event.log(
              //   & format!(
              //     "defining actlit {}\nto imply {} at {}",
              //     lit, one_prop_false, check_offset
              //   )
              // ) ;
              let check = actlit.mk_impl(& lit, one_prop_false) ;

              try_error!(solver.assert(& check, & check_offset), event) ;

              // event.log(& format!("check-sat assuming {}", lit)) ;

              let mut actlits = props.actlits() ;
              // let prop_count = actlits.len() ;
              actlits.push(lit) ;

              // event.log(
              //   & format!(
              //     "checking {} properties @{}",
              //     props.len(),
              //     check_offset.next()
              //   )
              // ) ;

              match solver.check_sat_assuming( & actlits, check_offset.next() ) {
                Ok(true) => {
                  // event.log("sat, getting falsified properties") ;
                  match props.get_false_next(& mut solver, & check_offset) {
                    Ok(falsified) => {
                      // let mut s = "falsified:".to_string() ;
                      // for sym in falsified.iter() {
                      //   s = format!("{}\n  {}", s, sym)
                      // } ;
                      // event.log(& s) ;
                      props.inhibit(& falsified) ;
                    },
                    Err(e) => {
                      event.error(
                        & format!("could not get falsifieds\n{:?}", e)
                      ) ;
                      break
                    },
                  }
                },
                Ok(false) => {
                  // event.log("unsat") ;
                  let unfalsifiable = props.not_inhibited() ;
                  // Wait until we get something.
                  loop {
                    let mut invariant = true ;
                    let at_least = k.curr().pre().unwrap() ;
                    for prop in unfalsifiable.iter() {
                      match * event.get_k_true(prop) {
                        Some(ref o) => {
                          if o < & at_least {
                            invariant = false ;
                            break
                          }
                        },
                        _ => { invariant = false ; break }
                      }
                    } ;
                    if invariant {
                      // event.log("forgetting") ;
                      try_error!(
                        props.forget(& mut solver, & unfalsifiable),
                        event
                      ) ;
                      event.proved_at(unfalsifiable, k.next()) ;
                      break 'split
                    } else {
                      // event.log("recv") ;
                      match event.recv() {
                        None => return (),
                        Some(msgs) => for msg in msgs {
                          match msg {
                            MsgDown::Forget(ps) => {
                              try_error!(
                                props.forget(& mut solver, & ps),
                                event
                              ) ;
                              continue 'out
                            },
                            MsgDown::Invariants(_,_) => event.log(
                              "received invariants, skipping"
                            ),
                            _ => event.error("unknown message")
                          }
                        },
                      } ;
                      sleep_ms(10) ;
                    }
                  }
                },
                Err(e) => {
                  event.error(
                    & format!("could not perform check-sat\n{:?}", e)
                  ) ;
                  break
                },
              } ;

              if props.all_inhibited() { break }
            } else {
              break 'split
            }
          } ;

          if props.none_left() {
            event.done_at(k.next()) ;
            break
          }

          k = k.nxt() ;

          // event.log( & format!("unroll {}", k) ) ;
          try_error!( sys.unroll(& mut solver, & k), event ) ;

          // event.log( & format!("activate next at {}", k) ) ;
          try_error!(
            props.activate_next(& mut solver, & k), event
          ) ;

          ()

        } ;
      },
    }
  }
}

<commit_msg>fixed k-induction done_at index<commit_after>#![deny(missing_docs)]
// Copyright 2015 Adrien Champion. See the COPYRIGHT file at the top-level
// directory of this distribution.
//
// 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.

/*! K-induction.

Unrolls backwards.
*/

extern crate term ;
extern crate system ;
extern crate event ;
extern crate unroll ;

use std::thread::sleep_ms ;

use term::Offset2 ;
use term::smt::* ;
use term::smt::sync::* ;

use event::msg::{ Info, Event, MsgDown} ;

use system::{ Sys, Prop } ;

use unroll::* ;

macro_rules! try_error {
  ($e:expr, $event:expr) => (
    match $e {
      Ok(v) => v,
      Err(e) => {
        $event.error( & format!("{:?}", e) ) ;
        $event.done(Info::Error) ;
        return ()
      },
    }
  )
}

/** K-induction. */
pub struct KInd ;
unsafe impl Send for KInd {}
impl event::CanRun for KInd {
  fn id(& self) -> event::Technique { event::Technique::KInd }

  fn run(
    & self, sys: Sys, props: Vec<Prop>, mut event: Event
  ) {
    // event.log(
    //   & format!("checking {} propertie(s) on system {}", props.len(), sys.sym())
    // ) ;

    // event.log("creating solver") ;

    let conf = SolverConf::z3().print_success() ;
    let factory = event.factory().clone() ;
    let mut actlit = Actlit::mk(factory.clone()) ;

    // Reversed to unroll backwards.
    let check_offset = Offset2::init().rev() ;
    let mut k = check_offset.clone() ;

    match Solver::mk(z3_cmd(), conf, factory.clone()) {
      Err(e) => event.error( & format!("could not create solver\n{:?}", e) ),
      Ok(mut solver) => {

        // event.log("creating manager, declaring actlits") ;
        let mut props = try_error!(
          PropManager::mk(factory.clone(), props, & mut solver),
          event
        ) ;

        // event.log("declaring functions, init and trans") ;
        try_error!(
          sys.defclare_funs(& mut solver), event
        ) ;

        // event.log("declare svar@0") ;
        try_error!(
          sys.declare_svars(& mut solver, check_offset.next()), event
          // Unrolling backwards ~~~~~~~~~~~~~~~~~~~~~~^^^^
        ) ;

        // event.log( & format!("unroll {}", k) ) ;
        try_error!( sys.unroll(& mut solver, & k), event ) ;

        'out: loop {

          props.reset_inhibited() ;

          // event.log( & format!("activating state at {}", k) ) ;
          try_error!(
            props.activate_state(& mut solver, & k), event
          ) ;

          match event.recv() {
            None => return (),
            Some(msgs) => for msg in msgs {
              match msg {
                MsgDown::Forget(ps) => try_error!(
                  props.forget(& mut solver, & ps),
                  event
                ),
                MsgDown::Invariants(_,_) => event.log(
                  "received invariants, skipping"
                ),
                _ => event.error("unknown message")
              }
            },
          } ;

          if props.none_left() {
            event.done_at(& k.curr()) ;
            break
          }

          'split: loop {

              if let Some(one_prop_false) = props.one_false_next() {

              let lit = actlit.mk_fresh_declare(& mut solver).unwrap() ;
              // event.log(
              //   & format!(
              //     "defining actlit {}\nto imply {} at {}",
              //     lit, one_prop_false, check_offset
              //   )
              // ) ;
              let check = actlit.mk_impl(& lit, one_prop_false) ;

              try_error!(solver.assert(& check, & check_offset), event) ;

              // event.log(& format!("check-sat assuming {}", lit)) ;

              let mut actlits = props.actlits() ;
              // let prop_count = actlits.len() ;
              actlits.push(lit) ;

              // event.log(
              //   & format!(
              //     "checking {} properties @{}",
              //     props.len(),
              //     check_offset.next()
              //   )
              // ) ;

              match solver.check_sat_assuming( & actlits, check_offset.next() ) {
                Ok(true) => {
                  // event.log("sat, getting falsified properties") ;
                  match props.get_false_next(& mut solver, & check_offset) {
                    Ok(falsified) => {
                      // let mut s = "falsified:".to_string() ;
                      // for sym in falsified.iter() {
                      //   s = format!("{}\n  {}", s, sym)
                      // } ;
                      // event.log(& s) ;
                      props.inhibit(& falsified) ;
                    },
                    Err(e) => {
                      event.error(
                        & format!("could not get falsifieds\n{:?}", e)
                      ) ;
                      break
                    },
                  }
                },
                Ok(false) => {
                  // event.log("unsat") ;
                  let unfalsifiable = props.not_inhibited() ;
                  // Wait until we get something.
                  loop {
                    let mut invariant = true ;
                    let at_least = k.curr().pre().unwrap() ;
                    for prop in unfalsifiable.iter() {
                      match * event.get_k_true(prop) {
                        Some(ref o) => {
                          if o < & at_least {
                            invariant = false ;
                            break
                          }
                        },
                        _ => { invariant = false ; break }
                      }
                    } ;
                    if invariant {
                      // event.log("forgetting") ;
                      try_error!(
                        props.forget(& mut solver, & unfalsifiable),
                        event
                      ) ;
                      event.proved_at(unfalsifiable, k.curr()) ;
                      break 'split
                    } else {
                      // event.log("recv") ;
                      match event.recv() {
                        None => return (),
                        Some(msgs) => for msg in msgs {
                          match msg {
                            MsgDown::Forget(ps) => {
                              try_error!(
                                props.forget(& mut solver, & ps),
                                event
                              ) ;
                              continue 'out
                            },
                            MsgDown::Invariants(_,_) => event.log(
                              "received invariants, skipping"
                            ),
                            _ => event.error("unknown message")
                          }
                        },
                      } ;
                      sleep_ms(10) ;
                    }
                  }
                },
                Err(e) => {
                  event.error(
                    & format!("could not perform check-sat\n{:?}", e)
                  ) ;
                  break
                },
              } ;

              if props.all_inhibited() { break }
            } else {
              break 'split
            }
          } ;

          if props.none_left() {
            event.done_at(k.curr()) ;
            break
          }

          k = k.nxt() ;

          // event.log( & format!("unroll {}", k) ) ;
          try_error!( sys.unroll(& mut solver, & k), event ) ;

          // event.log( & format!("activate next at {}", k) ) ;
          try_error!(
            props.activate_next(& mut solver, & k), event
          ) ;

          ()

        } ;
      },
    }
  }
}

<|endoftext|>


<commit_before>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//               2018 François Laignel <fengalin@free.fr>
//
// 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.

use glib;
use glib::subclass;
use glib::subclass::prelude::*;
use gst;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base;
use gst_base::subclass::prelude::*;

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::sync::Mutex;

use url::Url;

use file_location::FileLocation;

const DEFAULT_LOCATION: Option<FileLocation> = None;

#[derive(Debug)]
struct Settings {
    location: Option<FileLocation>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            location: DEFAULT_LOCATION,
        }
    }
}

static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| {
    glib::ParamSpec::string(
        name,
        "File Location",
        "Location of the file to read from",
        None,
        glib::ParamFlags::READWRITE,
    )
})];

enum State {
    Stopped,
    Started { file: File, position: u64 },
}

impl Default for State {
    fn default() -> State {
        State::Stopped
    }
}

pub struct FileSrc {
    cat: gst::DebugCategory,
    settings: Mutex<Settings>,
    state: Mutex<State>,
}

impl FileSrc {
    fn set_location(
        &self,
        element: &gst_base::BaseSrc,
        location: Option<FileLocation>,
    ) -> Result<(), glib::Error> {
        let state = self.state.lock().unwrap();
        if let State::Started { .. } = *state {
            return Err(gst::Error::new(
                gst::URIError::BadState,
                "Changing the `location` property on a started `filesrc` is not supported",
            ));
        }

        let mut settings = self.settings.lock().unwrap();
        settings.location = match location {
            Some(location) => {
                if !location.exists() {
                    return Err(gst::Error::new(
                        gst::URIError::BadReference,
                        format!("{} doesn't exist", location).as_str(),
                    ));
                }

                if !location.is_file() {
                    return Err(gst::Error::new(
                        gst::URIError::BadReference,
                        format!("{} is not a file", location).as_str(),
                    ));
                }

                match settings.location {
                    Some(ref location_cur) => {
                        gst_info!(
                            self.cat,
                            obj: element,
                            "Changing `location` from {:?} to {}",
                            location_cur,
                            location,
                        );
                    }
                    None => {
                        gst_info!(self.cat, obj: element, "Setting `location to {}", location,);
                    }
                }
                Some(location)
            }
            None => {
                gst_info!(self.cat, obj: element, "Resetting `location` to None",);
                None
            }
        };

        Ok(())
    }
}

impl ObjectSubclass for FileSrc {
    const NAME: &'static str = "RsFileSrc";
    type ParentType = gst_base::BaseSrc;
    type Instance = gst::subclass::ElementInstanceStruct<Self>;
    type Class = subclass::simple::ClassStruct<Self>;

    glib_object_subclass!();

    fn new() -> Self {
        Self {
            cat: gst::DebugCategory::new("rsfilesrc", gst::DebugColorFlags::empty(), "File Source"),
            settings: Mutex::new(Default::default()),
            state: Mutex::new(Default::default()),
        }
    }

    fn type_init(type_: &mut subclass::InitializingType<Self>) {
        type_.add_interface::<gst::URIHandler>();
    }

    fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
        klass.set_metadata(
            "File Source",
            "Source/File",
            "Read stream from a file",
            "François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>",
        );

        let caps = gst::Caps::new_any();
        let src_pad_template = gst::PadTemplate::new(
            "src",
            gst::PadDirection::Src,
            gst::PadPresence::Always,
            &caps,
        );
        klass.add_pad_template(src_pad_template);

        klass.install_properties(&PROPERTIES);
    }
}

impl ObjectImpl for FileSrc {
    glib_object_impl!();

    fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
        let prop = &PROPERTIES[id];
        match *prop {
            subclass::Property("location", ..) => {
                let element = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();

                let res = match value.get::<String>() {
                    Some(location) => FileLocation::try_from_path_str(location)
                        .and_then(|file_location| self.set_location(&element, Some(file_location))),
                    None => self.set_location(&element, None),
                };

                if let Err(err) = res {
                    gst_error!(
                        self.cat,
                        obj: element,
                        "Failed to set property `location`: {}",
                        err
                    );
                }
            }
            _ => unimplemented!(),
        };
    }

    fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
        let prop = &PROPERTIES[id];
        match *prop {
            subclass::Property("location", ..) => {
                let settings = self.settings.lock().unwrap();
                let location = settings
                    .location
                    .as_ref()
                    .map(|location| location.to_string());

                Ok(location.to_value())
            }
            _ => unimplemented!(),
        }
    }
}

impl ElementImpl for FileSrc {}

impl BaseSrcImpl for FileSrc {
    fn is_seekable(&self, _src: &gst_base::BaseSrc) -> bool {
        true
    }

    fn get_size(&self, _src: &gst_base::BaseSrc) -> Option<u64> {
        let state = self.state.lock().unwrap();
        if let State::Started { ref file, .. } = *state {
            file.metadata().ok().map(|m| m.len())
        } else {
            None
        }
    }

    fn start(&self, element: &gst_base::BaseSrc) -> bool {
        let mut state = self.state.lock().unwrap();
        if let State::Started { .. } = *state {
            gst_element_info!(
                element,
                gst::CoreError::StateChange,
                ["FileSrc already started"]
            );
            return false;
        }

        let settings = self.settings.lock().unwrap();
        let location = match settings.location {
            Some(ref location) => location,
            None => {
                gst_element_error!(
                    element,
                    gst::CoreError::StateChange,
                    ["File location is not defined"]
                );
                return false;
            }
        };

        let file = match File::open(location) {
            Ok(file) => file,
            Err(err) => {
                gst_element_error!(
                    element,
                    gst::ResourceError::OpenRead,
                    [
                        "Could not open file {} for reading: {}",
                        location,
                        err.to_string(),
                    ]
                );
                return false;
            }
        };

        gst_debug!(self.cat, obj: element, "Opened file {:?}", file);

        *state = State::Started { file, position: 0 };

        gst_info!(self.cat, obj: element, "Started");

        true
    }

    fn stop(&self, element: &gst_base::BaseSrc) -> bool {
        let mut state = self.state.lock().unwrap();
        if let State::Stopped = *state {
            gst_element_warning!(
                element,
                gst::CoreError::StateChange,
                ["FileSink not started"]
            );
            return false;
        }

        *state = State::Stopped;

        gst_info!(self.cat, obj: element, "Stopped");

        true
    }

    fn create(
        &self,
        element: &gst_base::BaseSrc,
        offset: u64,
        length: u32,
    ) -> Result<gst::Buffer, gst::FlowError> {
        let mut state = self.state.lock().unwrap();

        let (file, position) = match *state {
            State::Started {
                ref mut file,
                ref mut position,
            } => (file, position),
            State::Stopped => {
                gst_element_error!(element, gst::CoreError::Failed, ["Not started yet"]);
                return Err(gst::FlowError::Error);
            }
        };

        if *position != offset {
            file.seek(SeekFrom::Start(offset)).map_err(|err| {
                gst_element_error!(
                    element,
                    gst::LibraryError::Failed,
                    ["Failed to seek to {}: {}", offset, err.to_string()]
                );
                gst::FlowError::Error
            })?;

            *position = offset;
        }

        let mut buffer = match gst::Buffer::with_size(length as usize) {
            Some(buffer) => buffer,
            None => {
                gst_element_error!(
                    element,
                    gst::LibraryError::Failed,
                    ["Failed to allocate buffer"]
                );
                return Err(gst::FlowError::Error);
            }
        };

        {
            let buffer = buffer.make_mut();
            let size = {
                let mut map = match buffer.map_writable() {
                    Some(map) => map,
                    None => {
                        gst_element_error!(
                            element,
                            gst::LibraryError::Failed,
                            ["Failed to map buffer"]
                        );
                        return Err(gst::FlowError::Error);
                    }
                };

                file.read(map.as_mut()).map_err(|err| {
                    gst_element_error!(
                        element,
                        gst::LibraryError::Failed,
                        ["Failed to read at {}: {}", offset, err.to_string()]
                    );
                    gst::FlowError::Error
                })?
            };

            *position += size as u64;

            buffer.set_size(size);
        }

        Ok(buffer)
    }
}

impl URIHandlerImpl for FileSrc {
    fn get_uri(&self, _element: &gst::URIHandler) -> Option<String> {
        let settings = self.settings.lock().unwrap();

        // Conversion to Url already checked while building the `FileLocation`
        settings.location.as_ref().map(|location| {
            Url::from_file_path(location)
                .expect("FileSrc::get_uri couldn't build `Url` from `location`")
                .into_string()
        })
    }

    fn set_uri(&self, element: &gst::URIHandler, uri: Option<String>) -> Result<(), glib::Error> {
        let element = element.dynamic_cast_ref::<gst_base::BaseSrc>().unwrap();

        // Special case for "file://" as this is used by some applications to test
        // with `gst_element_make_from_uri` if there's an element that supports the URI protocol
        let uri = uri.filter(|uri| uri != "file://");

        let file_location = match uri {
            Some(uri) => Some(FileLocation::try_from_uri_str(&uri)?),
            None => None,
        };

        self.set_location(&element, file_location)
    }

    fn get_uri_type() -> gst::URIType {
        gst::URIType::Src
    }

    fn get_protocols() -> Vec<String> {
        vec!["file".to_string()]
    }
}

pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
    gst::Element::register(plugin, "rsfilesrc", 256 + 100, FileSrc::get_type())
}
<commit_msg>filesrc: Set format as gst::Format::Bytes in basesrc<commit_after>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//               2018 François Laignel <fengalin@free.fr>
//
// 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.

use glib;
use glib::subclass;
use glib::subclass::prelude::*;
use gst;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base;
use gst_base::prelude::*;
use gst_base::subclass::prelude::*;

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::sync::Mutex;

use url::Url;

use file_location::FileLocation;

const DEFAULT_LOCATION: Option<FileLocation> = None;

#[derive(Debug)]
struct Settings {
    location: Option<FileLocation>,
}

impl Default for Settings {
    fn default() -> Self {
        Settings {
            location: DEFAULT_LOCATION,
        }
    }
}

static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| {
    glib::ParamSpec::string(
        name,
        "File Location",
        "Location of the file to read from",
        None,
        glib::ParamFlags::READWRITE,
    )
})];

enum State {
    Stopped,
    Started { file: File, position: u64 },
}

impl Default for State {
    fn default() -> State {
        State::Stopped
    }
}

pub struct FileSrc {
    cat: gst::DebugCategory,
    settings: Mutex<Settings>,
    state: Mutex<State>,
}

impl FileSrc {
    fn set_location(
        &self,
        element: &gst_base::BaseSrc,
        location: Option<FileLocation>,
    ) -> Result<(), glib::Error> {
        let state = self.state.lock().unwrap();
        if let State::Started { .. } = *state {
            return Err(gst::Error::new(
                gst::URIError::BadState,
                "Changing the `location` property on a started `filesrc` is not supported",
            ));
        }

        let mut settings = self.settings.lock().unwrap();
        settings.location = match location {
            Some(location) => {
                if !location.exists() {
                    return Err(gst::Error::new(
                        gst::URIError::BadReference,
                        format!("{} doesn't exist", location).as_str(),
                    ));
                }

                if !location.is_file() {
                    return Err(gst::Error::new(
                        gst::URIError::BadReference,
                        format!("{} is not a file", location).as_str(),
                    ));
                }

                match settings.location {
                    Some(ref location_cur) => {
                        gst_info!(
                            self.cat,
                            obj: element,
                            "Changing `location` from {:?} to {}",
                            location_cur,
                            location,
                        );
                    }
                    None => {
                        gst_info!(self.cat, obj: element, "Setting `location to {}", location,);
                    }
                }
                Some(location)
            }
            None => {
                gst_info!(self.cat, obj: element, "Resetting `location` to None",);
                None
            }
        };

        Ok(())
    }
}

impl ObjectSubclass for FileSrc {
    const NAME: &'static str = "RsFileSrc";
    type ParentType = gst_base::BaseSrc;
    type Instance = gst::subclass::ElementInstanceStruct<Self>;
    type Class = subclass::simple::ClassStruct<Self>;

    glib_object_subclass!();

    fn new() -> Self {
        Self {
            cat: gst::DebugCategory::new("rsfilesrc", gst::DebugColorFlags::empty(), "File Source"),
            settings: Mutex::new(Default::default()),
            state: Mutex::new(Default::default()),
        }
    }

    fn type_init(type_: &mut subclass::InitializingType<Self>) {
        type_.add_interface::<gst::URIHandler>();
    }

    fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
        klass.set_metadata(
            "File Source",
            "Source/File",
            "Read stream from a file",
            "François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>",
        );

        let caps = gst::Caps::new_any();
        let src_pad_template = gst::PadTemplate::new(
            "src",
            gst::PadDirection::Src,
            gst::PadPresence::Always,
            &caps,
        );
        klass.add_pad_template(src_pad_template);

        klass.install_properties(&PROPERTIES);
    }
}

impl ObjectImpl for FileSrc {
    glib_object_impl!();

    fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
        let prop = &PROPERTIES[id];
        match *prop {
            subclass::Property("location", ..) => {
                let element = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();

                let res = match value.get::<String>() {
                    Some(location) => FileLocation::try_from_path_str(location)
                        .and_then(|file_location| self.set_location(&element, Some(file_location))),
                    None => self.set_location(&element, None),
                };

                if let Err(err) = res {
                    gst_error!(
                        self.cat,
                        obj: element,
                        "Failed to set property `location`: {}",
                        err
                    );
                }
            }
            _ => unimplemented!(),
        };
    }

    fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
        let prop = &PROPERTIES[id];
        match *prop {
            subclass::Property("location", ..) => {
                let settings = self.settings.lock().unwrap();
                let location = settings
                    .location
                    .as_ref()
                    .map(|location| location.to_string());

                Ok(location.to_value())
            }
            _ => unimplemented!(),
        }
    }

    fn constructed(&self, obj: &glib::Object) {
        self.parent_constructed(obj);

        let element = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
        element.set_format(gst::Format::Bytes);
    }
}

impl ElementImpl for FileSrc {}

impl BaseSrcImpl for FileSrc {
    fn is_seekable(&self, _src: &gst_base::BaseSrc) -> bool {
        true
    }

    fn get_size(&self, _src: &gst_base::BaseSrc) -> Option<u64> {
        let state = self.state.lock().unwrap();
        if let State::Started { ref file, .. } = *state {
            file.metadata().ok().map(|m| m.len())
        } else {
            None
        }
    }

    fn start(&self, element: &gst_base::BaseSrc) -> bool {
        let mut state = self.state.lock().unwrap();
        if let State::Started { .. } = *state {
            gst_element_info!(
                element,
                gst::CoreError::StateChange,
                ["FileSrc already started"]
            );
            return false;
        }

        let settings = self.settings.lock().unwrap();
        let location = match settings.location {
            Some(ref location) => location,
            None => {
                gst_element_error!(
                    element,
                    gst::CoreError::StateChange,
                    ["File location is not defined"]
                );
                return false;
            }
        };

        let file = match File::open(location) {
            Ok(file) => file,
            Err(err) => {
                gst_element_error!(
                    element,
                    gst::ResourceError::OpenRead,
                    [
                        "Could not open file {} for reading: {}",
                        location,
                        err.to_string(),
                    ]
                );
                return false;
            }
        };

        gst_debug!(self.cat, obj: element, "Opened file {:?}", file);

        *state = State::Started { file, position: 0 };

        gst_info!(self.cat, obj: element, "Started");

        true
    }

    fn stop(&self, element: &gst_base::BaseSrc) -> bool {
        let mut state = self.state.lock().unwrap();
        if let State::Stopped = *state {
            gst_element_warning!(
                element,
                gst::CoreError::StateChange,
                ["FileSink not started"]
            );
            return false;
        }

        *state = State::Stopped;

        gst_info!(self.cat, obj: element, "Stopped");

        true
    }

    fn create(
        &self,
        element: &gst_base::BaseSrc,
        offset: u64,
        length: u32,
    ) -> Result<gst::Buffer, gst::FlowError> {
        let mut state = self.state.lock().unwrap();

        let (file, position) = match *state {
            State::Started {
                ref mut file,
                ref mut position,
            } => (file, position),
            State::Stopped => {
                gst_element_error!(element, gst::CoreError::Failed, ["Not started yet"]);
                return Err(gst::FlowError::Error);
            }
        };

        if *position != offset {
            file.seek(SeekFrom::Start(offset)).map_err(|err| {
                gst_element_error!(
                    element,
                    gst::LibraryError::Failed,
                    ["Failed to seek to {}: {}", offset, err.to_string()]
                );
                gst::FlowError::Error
            })?;

            *position = offset;
        }

        let mut buffer = match gst::Buffer::with_size(length as usize) {
            Some(buffer) => buffer,
            None => {
                gst_element_error!(
                    element,
                    gst::LibraryError::Failed,
                    ["Failed to allocate buffer"]
                );
                return Err(gst::FlowError::Error);
            }
        };

        {
            let buffer = buffer.make_mut();
            let size = {
                let mut map = match buffer.map_writable() {
                    Some(map) => map,
                    None => {
                        gst_element_error!(
                            element,
                            gst::LibraryError::Failed,
                            ["Failed to map buffer"]
                        );
                        return Err(gst::FlowError::Error);
                    }
                };

                file.read(map.as_mut()).map_err(|err| {
                    gst_element_error!(
                        element,
                        gst::LibraryError::Failed,
                        ["Failed to read at {}: {}", offset, err.to_string()]
                    );
                    gst::FlowError::Error
                })?
            };

            *position += size as u64;

            buffer.set_size(size);
        }

        Ok(buffer)
    }
}

impl URIHandlerImpl for FileSrc {
    fn get_uri(&self, _element: &gst::URIHandler) -> Option<String> {
        let settings = self.settings.lock().unwrap();

        // Conversion to Url already checked while building the `FileLocation`
        settings.location.as_ref().map(|location| {
            Url::from_file_path(location)
                .expect("FileSrc::get_uri couldn't build `Url` from `location`")
                .into_string()
        })
    }

    fn set_uri(&self, element: &gst::URIHandler, uri: Option<String>) -> Result<(), glib::Error> {
        let element = element.dynamic_cast_ref::<gst_base::BaseSrc>().unwrap();

        // Special case for "file://" as this is used by some applications to test
        // with `gst_element_make_from_uri` if there's an element that supports the URI protocol
        let uri = uri.filter(|uri| uri != "file://");

        let file_location = match uri {
            Some(uri) => Some(FileLocation::try_from_uri_str(&uri)?),
            None => None,
        };

        self.set_location(&element, file_location)
    }

    fn get_uri_type() -> gst::URIType {
        gst::URIType::Src
    }

    fn get_protocols() -> Vec<String> {
        vec!["file".to_string()]
    }
}

pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
    gst::Element::register(plugin, "rsfilesrc", 256 + 100, FileSrc::get_type())
}
<|endoftext|>


<commit_before>
use dimensions::Dimensions;
use point::Point;
use position::Positionable;

/// Callback params.
pub type WidgetNum = usize;
pub type ColNum = usize;
pub type RowNum = usize;
pub type Width = f64;
pub type Height = f64;
pub type PosX = f64;
pub type PosY = f64;

/// Draw a matrix of any rectangular widget type, where the
/// matrix will provide a callback with the widget number,
/// it's `rows` and `cols` position, the width and height
/// for the widget and the location at which the widget
/// should be drawn.
#[derive(Copy, Clone)]
pub struct WidgetMatrix {
    cols: usize,
    rows: usize,
    pos: Point,
    dim: Dimensions,
    cell_pad_w: f64,
    cell_pad_h: f64,
}

/*
/// A cell to be returned via the cell callback.
pub struct MatrixCell<'a>(&'a mut UiContext, WidgetNum, ColNum, RowNum, PosX, PosY, Width, Height);
*/

impl WidgetMatrix {

    /// The callback called for each widget in the matrix.
    /// This should be called following all builder methods.
    pub fn each_widget<F>(&mut self, mut callback: F)
        where
            F: FnMut(WidgetNum, ColNum, RowNum, Point, Dimensions)
    {
        let widget_w = self.dim[0] / self.cols as f64;
        let widget_h = self.dim[1] / self.rows as f64;
        let mut widget_num = 0;
        for col in 0..self.cols {
            for row in 0..self.rows {
                callback(
                    widget_num,
                    col,
                    row,
                    [self.pos[0] + (widget_w * col as f64) + self.cell_pad_w,
                     self.pos[1] + (widget_h * row as f64) + self.cell_pad_h],
                    [widget_w - self.cell_pad_w * 2.0,
                     widget_h - self.cell_pad_h * 2.0],
                );
                widget_num += 1;
            }
        }
    }

    /// A builder method for adding padding to the cell.
    pub fn cell_padding(self, w: f64, h: f64) -> WidgetMatrix {
        WidgetMatrix { cell_pad_w: w, cell_pad_h: h, ..self }
    }

    /*
    /// Create an iterator over the matrix cells.
    fn iter_cells(&mut self) -> CellIterator {
    }
    */

}

/*
/// A struct used for iterating over the cells of a WidgetMatrix.
pub struct CellIterator {
    row: usize,
    col: usize,
    rows: usize,
    cols: usize,
}

impl Iterator for CellIterator {
    fn next
    */

impl WidgetMatrix {

    /// Create a widget matrix context.
    pub fn new(cols: usize, rows: usize) -> WidgetMatrix {
        WidgetMatrix {
            cols: cols,
            rows: rows,
            pos: [0.0, 0.0],
            dim: [256.0, 256.0],
            cell_pad_w: 0.0,
            cell_pad_h: 0.0,
        }
    }
}

impl Positionable for WidgetMatrix {
    fn point(mut self, pos: Point) -> Self {
        self.pos = pos;
        self
    }
}

/*
quack! {
    wm: WidgetMatrix[]
    get:
        fn () -> Size [] { Size(wm.pos) }
    set:
        fn (val: Size) [] { wm.dim = val.0 }
    action:
}
*/
<commit_msg>Implemented `Shapeable` for `WidgetMatrix`<commit_after>
use dimensions::Dimensions;
use point::Point;
use position::Positionable;
use shape::Shapeable;

/// Callback params.
pub type WidgetNum = usize;
pub type ColNum = usize;
pub type RowNum = usize;
pub type Width = f64;
pub type Height = f64;
pub type PosX = f64;
pub type PosY = f64;

/// Draw a matrix of any rectangular widget type, where the
/// matrix will provide a callback with the widget number,
/// it's `rows` and `cols` position, the width and height
/// for the widget and the location at which the widget
/// should be drawn.
#[derive(Copy, Clone)]
pub struct WidgetMatrix {
    cols: usize,
    rows: usize,
    pos: Point,
    dim: Dimensions,
    cell_pad_w: f64,
    cell_pad_h: f64,
}

/*
/// A cell to be returned via the cell callback.
pub struct MatrixCell<'a>(&'a mut UiContext, WidgetNum, ColNum, RowNum, PosX, PosY, Width, Height);
*/

impl WidgetMatrix {

    /// The callback called for each widget in the matrix.
    /// This should be called following all builder methods.
    pub fn each_widget<F>(&mut self, mut callback: F)
        where
            F: FnMut(WidgetNum, ColNum, RowNum, Point, Dimensions)
    {
        let widget_w = self.dim[0] / self.cols as f64;
        let widget_h = self.dim[1] / self.rows as f64;
        let mut widget_num = 0;
        for col in 0..self.cols {
            for row in 0..self.rows {
                callback(
                    widget_num,
                    col,
                    row,
                    [self.pos[0] + (widget_w * col as f64) + self.cell_pad_w,
                     self.pos[1] + (widget_h * row as f64) + self.cell_pad_h],
                    [widget_w - self.cell_pad_w * 2.0,
                     widget_h - self.cell_pad_h * 2.0],
                );
                widget_num += 1;
            }
        }
    }

    /// A builder method for adding padding to the cell.
    pub fn cell_padding(self, w: f64, h: f64) -> WidgetMatrix {
        WidgetMatrix { cell_pad_w: w, cell_pad_h: h, ..self }
    }

    /*
    /// Create an iterator over the matrix cells.
    fn iter_cells(&mut self) -> CellIterator {
    }
    */

}

/*
/// A struct used for iterating over the cells of a WidgetMatrix.
pub struct CellIterator {
    row: usize,
    col: usize,
    rows: usize,
    cols: usize,
}

impl Iterator for CellIterator {
    fn next
    */

impl WidgetMatrix {

    /// Create a widget matrix context.
    pub fn new(cols: usize, rows: usize) -> WidgetMatrix {
        WidgetMatrix {
            cols: cols,
            rows: rows,
            pos: [0.0, 0.0],
            dim: [256.0, 256.0],
            cell_pad_w: 0.0,
            cell_pad_h: 0.0,
        }
    }
}

impl Positionable for WidgetMatrix {
    fn point(mut self, pos: Point) -> Self {
        self.pos = pos;
        self
    }
}

impl Shapeable for WidgetMatrix {
    fn get_dim(&self) -> Dimensions { self.dim }
    fn dim(mut self, dim: Dimensions) -> Self { self.dim = dim; self }
}
<|endoftext|>