<commit_before>// Bit Flags for Genetic Algorithm Configuration
bitflags!
{
    pub flags GAFlags: u32
    {
        const DEBUG_FLAG = 0b00000001
    }
}
impl Default for GAFlags
{
    fn default() -> GAFlags { GAFlags {bits : 0} }
}

// Genetic Algorithm Configuration
pub trait GAConfig
{
    fn flags(&self) -> GAFlags;
    fn max_generations(&self) -> i32;
    fn percentage_crossover(&self) -> f32;
    fn probability_mutation(&self) -> f32;
}

// Genetic Algorithm Solution
pub trait GASolution
{
    fn evaluate(&mut self) -> f32;
    fn crossover(&self, other : &Self) -> &Self;
    fn mutate(&mut self);
}

// Genetic Algorithm
pub trait GeneticAlgorithm<T: GASolution>
{
    // GENERIC GA METHODS - Should not be overriden frequently
    fn initialize(&mut self)
    {
        if self.config().flags().contains(DEBUG_FLAG)
		{
		    println!("Genetic Algorithm - Initialized");
		}
        self.initialize_internal()
    }

    fn step(&mut self) -> i32
    {
        if self.config().flags().contains(DEBUG_FLAG)
		{
		    println!("Genetic Algorithm - Step");
		}
        self.step_internal()
    }

    fn done(&mut self) -> bool
    {
        if self.config().flags().contains(DEBUG_FLAG)
        {
          println!("Genetic Algorithm - Done");
        }
        self.done_internal()
    }

    // IMPLEMENTATION SPECIFIC
    fn config(&mut self) -> &GAConfig;
    fn population(&mut self) -> &Vec<T>;

    fn initialize_internal(&mut self) {}
    fn step_internal(&mut self) -> i32 { 0 }
    fn done_internal(&mut self) -> bool { true }
}

// Simple Genetic Algorithm Config
#[derive(Copy, Clone, Default, Debug)]
pub struct SimpleGeneticAlgorithmCfg
{
    pub d_seed : i32,
    pub pconv  : f32,
    pub is_min : bool,

    // GAConfig Trait
    pub max_generations        : i32,
    pub flags                  : GAFlags,
    pub percentage_crossover   : f32,
    pub probability_mutation   : f32,
}
impl GAConfig for SimpleGeneticAlgorithmCfg
{
    fn flags(&self) -> GAFlags
    {
        self.flags
    }
    fn max_generations(&self) -> i32
    {
        self.max_generations
    }
    fn percentage_crossover(&self) -> f32 { 0.0 }
    fn probability_mutation(&self) -> f32 { 0.0 }
}

// Simple Genetic Algorithm
pub struct SimpleGeneticAlgorithm<T: GASolution>
{
  current_generation : i32,
  config : SimpleGeneticAlgorithmCfg,
  population : Vec<T>
}
impl<T: GASolution> SimpleGeneticAlgorithm<T>
{
    pub fn new(cfg_:SimpleGeneticAlgorithmCfg) -> SimpleGeneticAlgorithm<T>
    {
        //TODO: Initialize population. Maybe that's for initialize_internal (?)
        let p : Vec<T> = vec![];
        SimpleGeneticAlgorithm { current_generation: 0, config : cfg_, population : k}
    }
}
impl<T: GASolution> GeneticAlgorithm<T> for SimpleGeneticAlgorithm <T>
{
    fn config(&mut self) -> &GAConfig
    {
        &self.config
    }

    fn population(&mut self) -> &Vec<T>
    {
        return &self.population
    }

    fn step_internal(&mut self) -> i32
    {
        self.current_generation += 1;
        self.current_generation
    }

    fn done_internal(&mut self) -> bool
    {
        self.current_generation >= self.config().max_generations()
    }
}
<commit_msg>Indent Fixup<commit_after>// Bit Flags for Genetic Algorithm Configuration
bitflags!
{
    pub flags GAFlags: u32
    {
        const DEBUG_FLAG = 0b00000001
    }
}
impl Default for GAFlags
{
    fn default() -> GAFlags { GAFlags {bits : 0} }
}

// Genetic Algorithm Configuration
pub trait GAConfig
{
    fn flags(&self) -> GAFlags;
    fn max_generations(&self) -> i32;
    fn percentage_crossover(&self) -> f32;
    fn probability_mutation(&self) -> f32;
}

// Genetic Algorithm Solution
pub trait GASolution
{
    fn evaluate(&mut self) -> f32;
    fn crossover(&self, other : &Self) -> &Self;
    fn mutate(&mut self);
}

// Genetic Algorithm
pub trait GeneticAlgorithm<T: GASolution>
{
    // GENERIC GA METHODS - Should not be overriden frequently
    fn initialize(&mut self)
    {
        if self.config().flags().contains(DEBUG_FLAG)
        {
            println!("Genetic Algorithm - Initialized");
        }
        self.initialize_internal()
    }

    fn step(&mut self) -> i32
    {
        if self.config().flags().contains(DEBUG_FLAG)
        {
            println!("Genetic Algorithm - Step");
        }
        self.step_internal()
    }

    fn done(&mut self) -> bool
    {
        if self.config().flags().contains(DEBUG_FLAG)
        {
            println!("Genetic Algorithm - Done");
        }
        self.done_internal()
    }

    // IMPLEMENTATION SPECIFIC
    fn config(&mut self) -> &GAConfig;
    fn population(&mut self) -> &Vec<T>;

    fn initialize_internal(&mut self) {}
    fn step_internal(&mut self) -> i32 { 0 }
    fn done_internal(&mut self) -> bool { true }
}

// Simple Genetic Algorithm Config
#[derive(Copy, Clone, Default, Debug)]
pub struct SimpleGeneticAlgorithmCfg
{
    pub d_seed : i32,
    pub pconv  : f32,
    pub is_min : bool,

    // GAConfig Trait
    pub max_generations        : i32,
    pub flags                  : GAFlags,
    pub percentage_crossover   : f32,
    pub probability_mutation   : f32,
}
impl GAConfig for SimpleGeneticAlgorithmCfg
{
    fn flags(&self) -> GAFlags
    {
        self.flags
    }
    fn max_generations(&self) -> i32
    {
        self.max_generations
    }
    fn percentage_crossover(&self) -> f32 { 0.0 }
    fn probability_mutation(&self) -> f32 { 0.0 }
}

// Simple Genetic Algorithm
pub struct SimpleGeneticAlgorithm<T: GASolution>
{
  current_generation : i32,
  config : SimpleGeneticAlgorithmCfg,
  population : Vec<T>
}
impl<T: GASolution> SimpleGeneticAlgorithm<T>
{
    pub fn new(cfg_:SimpleGeneticAlgorithmCfg) -> SimpleGeneticAlgorithm<T>
    {
        //TODO: Initialize population. Maybe that's for initialize_internal (?)
        let p : Vec<T> = vec![];
        SimpleGeneticAlgorithm { current_generation: 0, config : cfg_, population : k}
    }
}
impl<T: GASolution> GeneticAlgorithm<T> for SimpleGeneticAlgorithm <T>
{
    fn config(&mut self) -> &GAConfig
    {
        &self.config
    }

    fn population(&mut self) -> &Vec<T>
    {
        return &self.population
    }

    fn step_internal(&mut self) -> i32
    {
        self.current_generation += 1;
        self.current_generation
    }

    fn done_internal(&mut self) -> bool
    {
        self.current_generation >= self.config().max_generations()
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Read, Write};
use std::result;
use std::string;

use byteorder::{self, ReadBytesExt, WriteBytesExt};

/// A short-hand for `result::Result<T, io::Error>`.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Utf8(string::FromUtf8Error),
    UnexpectedTag(u8),
    UnexpectedEOF,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error { Error::Io(err) }
}

impl From<string::FromUtf8Error> for Error {
    fn from(err: string::FromUtf8Error) -> Error { Error::Utf8(err) }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        match err {
            byteorder::Error::Io(err) => Error::Io(err),
            byteorder::Error::UnexpectedEOF => Error::UnexpectedEOF,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnexpectedTag(v) => write!(f, "Unexpected tag 0x{:x}.", v),
            Error::UnexpectedEOF => write!(f, "Unexpected end of file."),
            Error::Utf8(ref err) => err.fmt(f),
            Error::Io(ref err) => err.fmt(f),
        }
    }
}

/// Extension for reading length-prefixed strings.
pub trait ReadVariableExt: Read {
    /// Reads a variable-length encoded 32-bit integer.
    /// https://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx
    fn read_variable_uint(&mut self) -> Result<u32> {
        let mut val = 0u32;
        let mut nread = 0usize;
        while nread < 5 {
            let byte = try!(self.read_u8());
            val = val | (((byte & 0x7f) as u32) << (nread * 7));
            nread += 1;
            if byte & 0x80 == 0 {
                break;
            }
        }
        Ok(val)
    }

    /// Reads a variable-length encoded integer, representing the length of the string; then
    /// reads that many bytes from the underlying reader and interprets them as a utf8 string.
    fn read_variable_string(&mut self) -> Result<String> {
        let len = try!(self.read_variable_uint()) as usize;
        let mut buf = vec![0; len];
        let mut nread = 0usize;
        while nread < buf.len() {
            match self.read(&mut buf[nread..]) {
                Ok(0) => return Err(Error::UnexpectedEOF),
                Ok(n) => nread += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
                Err(e) => return Err(From::from(e))
            }
        }
        String::from_utf8(buf).map_err(Error::from)
    }
}

/// All types that implement `Read` get methods defined in `ReadVariableExt`.
impl<R: Read + ?Sized> ReadVariableExt for R {}

/// Extension for writing length-prefixed strings.
pub trait WriteVariableExt: Write {
    /// Writes a variable-length encoded 32-bit integer.
    /// https://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx
    fn write_variable_uint(&mut self, n: u32) -> Result<()> {
        let mut val = n;
        while val > 0x7f {
            try!(self.write_u8((val & 0x7f) as u8));
            val = val >> 7;
        }
        try!(self.write_u8((val & 0x7f) as u8));
        Ok(())
    }

    /// Writes a variable-length encoded integer, representing the length of the string; then
    /// writes that many bytes to the underlying writer, representing the utf8-encoded string.
    fn write_variable_string(&mut self, s: &str) -> Result<()> {
        try!(self.write_variable_uint(s.len() as u32));
        try!(self.write_all(s.as_bytes()));
        Ok(())
    }
}

/// All types that implement `Write` get methods defined in `WriteVariableExt`.
impl<W: Write + ?Sized> WriteVariableExt for W {}
<commit_msg>Fix variable-length int encoding<commit_after>use std::fmt;
use std::io::{self, Read, Write};
use std::result;
use std::string;

use byteorder::{self, ReadBytesExt, WriteBytesExt};

/// A short-hand for `result::Result<T, io::Error>`.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Utf8(string::FromUtf8Error),
    UnexpectedTag(u8),
    UnexpectedEOF,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error { Error::Io(err) }
}

impl From<string::FromUtf8Error> for Error {
    fn from(err: string::FromUtf8Error) -> Error { Error::Utf8(err) }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        match err {
            byteorder::Error::Io(err) => Error::Io(err),
            byteorder::Error::UnexpectedEOF => Error::UnexpectedEOF,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::UnexpectedTag(v) => write!(f, "Unexpected tag 0x{:x}.", v),
            Error::UnexpectedEOF => write!(f, "Unexpected end of file."),
            Error::Utf8(ref err) => err.fmt(f),
            Error::Io(ref err) => err.fmt(f),
        }
    }
}

/// Extension for reading length-prefixed strings.
pub trait ReadVariableExt: Read {
    /// Reads a variable-length encoded 32-bit integer.
    /// https://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx
    fn read_variable_uint(&mut self) -> Result<u32> {
        let mut val = 0u32;
        let mut nread = 0usize;
        while nread < 5 {
            let byte = try!(self.read_u8());
            val = val | (((byte & 0x7f) as u32) << (nread * 7));
            nread += 1;
            if byte & 0x80 == 0 {
                break;
            }
        }
        Ok(val)
    }

    /// Reads a variable-length encoded integer, representing the length of the string; then
    /// reads that many bytes from the underlying reader and interprets them as a utf8 string.
    fn read_variable_string(&mut self) -> Result<String> {
        let len = try!(self.read_variable_uint()) as usize;
        let mut buf = vec![0; len];
        let mut nread = 0usize;
        while nread < buf.len() {
            match self.read(&mut buf[nread..]) {
                Ok(0) => return Err(Error::UnexpectedEOF),
                Ok(n) => nread += n,
                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
                Err(e) => return Err(From::from(e))
            }
        }
        String::from_utf8(buf).map_err(Error::from)
    }
}

/// All types that implement `Read` get methods defined in `ReadVariableExt`.
impl<R: Read + ?Sized> ReadVariableExt for R {}

/// Extension for writing length-prefixed strings.
pub trait WriteVariableExt: Write {
    /// Writes a variable-length encoded 32-bit integer.
    /// https://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx
    fn write_variable_uint(&mut self, n: u32) -> Result<()> {
        let mut val = n;
        while val > 0x7f {
            try!(self.write_u8(((val & 0x7f) | 0x80) as u8));
            val = val >> 7;
        }
        try!(self.write_u8((val & 0x7f) as u8));
        Ok(())
    }

    /// Writes a variable-length encoded integer, representing the length of the string; then
    /// writes that many bytes to the underlying writer, representing the utf8-encoded string.
    fn write_variable_string(&mut self, s: &str) -> Result<()> {
        try!(self.write_variable_uint(s.len() as u32));
        try!(self.write_all(s.as_bytes()));
        Ok(())
    }
}

/// All types that implement `Write` get methods defined in `WriteVariableExt`.
impl<W: Write + ?Sized> WriteVariableExt for W {}
<|endoftext|>


<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 byvalue_checker;

use std::collections::{HashMap, HashSet};

use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
use syn::{FnArg, ItemEnum, ItemStruct, Type, TypePtr, Visibility};

use crate::{
    conversion::{
        analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter},
        api::{
            AnalysisPhase, Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind,
            StructDetails, TypeKind, UnanalyzedApi,
        },
        convert_error::{ConvertErrorWithContext, ErrorContext},
        error_reporter::convert_apis,
        parse::BindgenSemanticAttributes,
        ConvertError,
    },
    types::{Namespace, QualifiedName},
};

use super::tdef::{TypedefAnalysis, TypedefPhase};

pub(crate) struct PodAnalysis {
    pub(crate) kind: TypeKind,
    pub(crate) bases: HashSet<QualifiedName>,
    /// Base classes for which we should create casts.
    /// That's just those which are on the allowlist,
    /// because otherwise we don't know whether they're
    /// abstract or not.
    pub(crate) castable_bases: HashSet<QualifiedName>,
    pub(crate) field_types: HashSet<QualifiedName>,
    pub(crate) movable: bool,
}

pub(crate) struct PodPhase;

impl AnalysisPhase for PodPhase {
    type TypedefAnalysis = TypedefAnalysis;
    type StructAnalysis = PodAnalysis;
    type FunAnalysis = ();
}

/// In our set of APIs, work out which ones are safe to represent
/// by value in Rust (e.g. they don't have a destructor) and record
/// as such. Return a set of APIs annotated with extra metadata,
/// and an object which can be used to query the POD status of any
/// type whether or not it's one of the [Api]s.
pub(crate) fn analyze_pod_apis(
    apis: Vec<Api<TypedefPhase>>,
    config: &IncludeCppConfig,
) -> Result<Vec<Api<PodPhase>>, ConvertError> {
    // This next line will return an error if any of the 'generate_pod'
    // directives from the user can't be met because, for instance,
    // a type contains a std::string or some other type which can't be
    // held safely by value in Rust.
    let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
    // We'll also note which types have deleted move constructors.
    let deleted_move_constructors = find_deleted_move_and_copy_constructors(&apis);
    let mut extra_apis = Vec::new();
    let mut type_converter = TypeConverter::new(config, &apis);
    let mut results = Vec::new();
    convert_apis(
        apis,
        &mut results,
        Api::fun_unchanged,
        |name, details, _| {
            analyze_struct(
                &byvalue_checker,
                &mut type_converter,
                &mut extra_apis,
                name,
                details,
                config,
                &deleted_move_constructors,
            )
        },
        analyze_enum,
        Api::typedef_unchanged,
    );
    // Conceivably, the process of POD-analysing the first set of APIs could result
    // in us creating new APIs to concretize generic types.
    let extra_apis: Vec<Api<PodPhase>> = extra_apis.into_iter().map(add_analysis).collect();
    let mut more_extra_apis = Vec::new();
    convert_apis(
        extra_apis,
        &mut results,
        Api::fun_unchanged,
        |name, details, _| {
            analyze_struct(
                &byvalue_checker,
                &mut type_converter,
                &mut more_extra_apis,
                name,
                details,
                config,
                &deleted_move_constructors,
            )
        },
        analyze_enum,
        Api::typedef_unchanged,
    );
    assert!(more_extra_apis.is_empty());
    Ok(results)
}

fn analyze_enum(
    name: ApiName,
    mut item: ItemEnum,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
    metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
    Ok(Box::new(std::iter::once(Api::Enum { name, item })))
}

fn analyze_struct(
    byvalue_checker: &ByValueChecker,
    type_converter: &mut TypeConverter,
    extra_apis: &mut Vec<UnanalyzedApi>,
    name: ApiName,
    mut details: Box<StructDetails>,
    config: &IncludeCppConfig,
    deleted_move_constructors: &HashSet<QualifiedName>,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
    let movable = !deleted_move_constructors.contains(&name.name);
    let id = name.name.get_final_ident();
    if details.vis != CppVisibility::Public {
        return Err(ConvertErrorWithContext(
            ConvertError::NonPublicNestedType,
            Some(ErrorContext::Item(id)),
        ));
    }
    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
    metadata.check_for_fatal_attrs(&id)?;
    let bases = get_bases(&details.item);
    let mut field_types = HashSet::new();
    let field_conversion_errors = get_struct_field_types(
        type_converter,
        name.name.get_namespace(),
        &details.item,
        &mut field_types,
        extra_apis,
    );
    let type_kind = if byvalue_checker.is_pod(&name.name) {
        // It's POD so any errors encountered parsing its fields are important.
        // Let's not allow anything to be POD if it's got rvalue reference fields.
        if details.has_rvalue_reference_fields {
            return Err(ConvertErrorWithContext(
                ConvertError::RValueReferenceField,
                Some(ErrorContext::Item(id)),
            ));
        }
        if let Some(err) = field_conversion_errors.into_iter().next() {
            return Err(ConvertErrorWithContext(err, Some(ErrorContext::Item(id))));
        }
        TypeKind::Pod
    } else {
        TypeKind::NonPod
    };
    let castable_bases = bases
        .iter()
        .filter(|(_, is_public)| **is_public)
        .map(|(base, _)| base)
        .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
        .cloned()
        .collect();
    Ok(Box::new(std::iter::once(Api::Struct {
        name,
        details,
        analysis: PodAnalysis {
            kind: type_kind,
            bases: bases.into_keys().collect(),
            castable_bases,
            field_types,
            movable,
        },
    })))
}

fn get_struct_field_types(
    type_converter: &mut TypeConverter,
    ns: &Namespace,
    s: &ItemStruct,
    deps: &mut HashSet<QualifiedName>,
    extra_apis: &mut Vec<UnanalyzedApi>,
) -> Vec<ConvertError> {
    let mut convert_errors = Vec::new();
    for f in &s.fields {
        let annotated =
            type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType);
        match annotated {
            Ok(r) => {
                extra_apis.extend(r.extra_apis);
                deps.extend(r.types_encountered);
            }
            Err(e) => convert_errors.push(e),
        };
    }
    convert_errors
}

/// Map to whether the bases are public.
fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
    item.fields
        .iter()
        .filter_map(|f| {
            let is_public = matches!(f.vis, Visibility::Public(_));
            match &f.ty {
                Type::Path(typ) => f
                    .ident
                    .as_ref()
                    .filter(|id| id.to_string().starts_with("_base"))
                    .map(|_| (QualifiedName::from_type_path(typ), is_public)),
                _ => None,
            }
        })
        .collect()
}

fn find_deleted_move_and_copy_constructors(apis: &[Api<TypedefPhase>]) -> HashSet<QualifiedName> {
    // Remove any deleted move + copy constructors from the API list and list the types
    // that they construct.
    apis.iter().filter_map(|api| match api {
        Api::Function { ref fun, .. } => match &**fun {
            FuncToConvert {
                special_member:
                    Some(SpecialMemberKind::MoveConstructor | SpecialMemberKind::CopyConstructor),
                is_deleted: true,
                inputs,
                ..
             } => match is_a_pointer_arg(inputs.iter().next()) {
                    Some(ty) => Some(ty),
                    _ => panic!("found special constructor member with something other than a pointer first arg"),
                },
            _ => None
        },
        _ => None,
    }).collect()
}

/// Determine if a function argument is a pointer, and if so, to what.
/// It's unfortunate that we need to do this during the POD analysis but
/// for now, it's the best way to identify special constructor members.
fn is_a_pointer_arg(arg: Option<&FnArg>) -> Option<QualifiedName> {
    arg.map(|arg| match arg {
        FnArg::Receiver(..) => None,
        FnArg::Typed(pt) => match &*pt.ty {
            Type::Ptr(TypePtr { elem, .. }) => match &**elem {
                Type::Path(typ) => Some(QualifiedName::from_type_path(typ)),
                _ => None,
            },
            _ => None,
        },
    })
    .flatten()
}
<commit_msg>Unrelated clippy.<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 byvalue_checker;

use std::collections::{HashMap, HashSet};

use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
use syn::{FnArg, ItemEnum, ItemStruct, Type, TypePtr, Visibility};

use crate::{
    conversion::{
        analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter},
        api::{
            AnalysisPhase, Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind,
            StructDetails, TypeKind, UnanalyzedApi,
        },
        convert_error::{ConvertErrorWithContext, ErrorContext},
        error_reporter::convert_apis,
        parse::BindgenSemanticAttributes,
        ConvertError,
    },
    types::{Namespace, QualifiedName},
};

use super::tdef::{TypedefAnalysis, TypedefPhase};

pub(crate) struct PodAnalysis {
    pub(crate) kind: TypeKind,
    pub(crate) bases: HashSet<QualifiedName>,
    /// Base classes for which we should create casts.
    /// That's just those which are on the allowlist,
    /// because otherwise we don't know whether they're
    /// abstract or not.
    pub(crate) castable_bases: HashSet<QualifiedName>,
    pub(crate) field_types: HashSet<QualifiedName>,
    pub(crate) movable: bool,
}

pub(crate) struct PodPhase;

impl AnalysisPhase for PodPhase {
    type TypedefAnalysis = TypedefAnalysis;
    type StructAnalysis = PodAnalysis;
    type FunAnalysis = ();
}

/// In our set of APIs, work out which ones are safe to represent
/// by value in Rust (e.g. they don't have a destructor) and record
/// as such. Return a set of APIs annotated with extra metadata,
/// and an object which can be used to query the POD status of any
/// type whether or not it's one of the [Api]s.
pub(crate) fn analyze_pod_apis(
    apis: Vec<Api<TypedefPhase>>,
    config: &IncludeCppConfig,
) -> Result<Vec<Api<PodPhase>>, ConvertError> {
    // This next line will return an error if any of the 'generate_pod'
    // directives from the user can't be met because, for instance,
    // a type contains a std::string or some other type which can't be
    // held safely by value in Rust.
    let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
    // We'll also note which types have deleted move constructors.
    let deleted_move_constructors = find_deleted_move_and_copy_constructors(&apis);
    let mut extra_apis = Vec::new();
    let mut type_converter = TypeConverter::new(config, &apis);
    let mut results = Vec::new();
    convert_apis(
        apis,
        &mut results,
        Api::fun_unchanged,
        |name, details, _| {
            analyze_struct(
                &byvalue_checker,
                &mut type_converter,
                &mut extra_apis,
                name,
                details,
                config,
                &deleted_move_constructors,
            )
        },
        analyze_enum,
        Api::typedef_unchanged,
    );
    // Conceivably, the process of POD-analysing the first set of APIs could result
    // in us creating new APIs to concretize generic types.
    let extra_apis: Vec<Api<PodPhase>> = extra_apis.into_iter().map(add_analysis).collect();
    let mut more_extra_apis = Vec::new();
    convert_apis(
        extra_apis,
        &mut results,
        Api::fun_unchanged,
        |name, details, _| {
            analyze_struct(
                &byvalue_checker,
                &mut type_converter,
                &mut more_extra_apis,
                name,
                details,
                config,
                &deleted_move_constructors,
            )
        },
        analyze_enum,
        Api::typedef_unchanged,
    );
    assert!(more_extra_apis.is_empty());
    Ok(results)
}

fn analyze_enum(
    name: ApiName,
    mut item: ItemEnum,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
    metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
    Ok(Box::new(std::iter::once(Api::Enum { name, item })))
}

fn analyze_struct(
    byvalue_checker: &ByValueChecker,
    type_converter: &mut TypeConverter,
    extra_apis: &mut Vec<UnanalyzedApi>,
    name: ApiName,
    mut details: Box<StructDetails>,
    config: &IncludeCppConfig,
    deleted_move_constructors: &HashSet<QualifiedName>,
) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
    let movable = !deleted_move_constructors.contains(&name.name);
    let id = name.name.get_final_ident();
    if details.vis != CppVisibility::Public {
        return Err(ConvertErrorWithContext(
            ConvertError::NonPublicNestedType,
            Some(ErrorContext::Item(id)),
        ));
    }
    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
    metadata.check_for_fatal_attrs(&id)?;
    let bases = get_bases(&details.item);
    let mut field_types = HashSet::new();
    let field_conversion_errors = get_struct_field_types(
        type_converter,
        name.name.get_namespace(),
        &details.item,
        &mut field_types,
        extra_apis,
    );
    let type_kind = if byvalue_checker.is_pod(&name.name) {
        // It's POD so any errors encountered parsing its fields are important.
        // Let's not allow anything to be POD if it's got rvalue reference fields.
        if details.has_rvalue_reference_fields {
            return Err(ConvertErrorWithContext(
                ConvertError::RValueReferenceField,
                Some(ErrorContext::Item(id)),
            ));
        }
        if let Some(err) = field_conversion_errors.into_iter().next() {
            return Err(ConvertErrorWithContext(err, Some(ErrorContext::Item(id))));
        }
        TypeKind::Pod
    } else {
        TypeKind::NonPod
    };
    let castable_bases = bases
        .iter()
        .filter(|(_, is_public)| **is_public)
        .map(|(base, _)| base)
        .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
        .cloned()
        .collect();
    Ok(Box::new(std::iter::once(Api::Struct {
        name,
        details,
        analysis: PodAnalysis {
            kind: type_kind,
            bases: bases.into_keys().collect(),
            castable_bases,
            field_types,
            movable,
        },
    })))
}

fn get_struct_field_types(
    type_converter: &mut TypeConverter,
    ns: &Namespace,
    s: &ItemStruct,
    deps: &mut HashSet<QualifiedName>,
    extra_apis: &mut Vec<UnanalyzedApi>,
) -> Vec<ConvertError> {
    let mut convert_errors = Vec::new();
    for f in &s.fields {
        let annotated =
            type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType);
        match annotated {
            Ok(r) => {
                extra_apis.extend(r.extra_apis);
                deps.extend(r.types_encountered);
            }
            Err(e) => convert_errors.push(e),
        };
    }
    convert_errors
}

/// Map to whether the bases are public.
fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
    item.fields
        .iter()
        .filter_map(|f| {
            let is_public = matches!(f.vis, Visibility::Public(_));
            match &f.ty {
                Type::Path(typ) => f
                    .ident
                    .as_ref()
                    .filter(|id| id.to_string().starts_with("_base"))
                    .map(|_| (QualifiedName::from_type_path(typ), is_public)),
                _ => None,
            }
        })
        .collect()
}

fn find_deleted_move_and_copy_constructors(apis: &[Api<TypedefPhase>]) -> HashSet<QualifiedName> {
    // Remove any deleted move + copy constructors from the API list and list the types
    // that they construct.
    apis.iter().filter_map(|api| match api {
        Api::Function { ref fun, .. } => match &**fun {
            FuncToConvert {
                special_member:
                    Some(SpecialMemberKind::MoveConstructor | SpecialMemberKind::CopyConstructor),
                is_deleted: true,
                inputs,
                ..
             } => match is_a_pointer_arg(inputs.iter().next()) {
                    Some(ty) => Some(ty),
                    _ => panic!("found special constructor member with something other than a pointer first arg"),
                },
            _ => None
        },
        _ => None,
    }).collect()
}

/// Determine if a function argument is a pointer, and if so, to what.
/// It's unfortunate that we need to do this during the POD analysis but
/// for now, it's the best way to identify special constructor members.
fn is_a_pointer_arg(arg: Option<&FnArg>) -> Option<QualifiedName> {
    arg.and_then(|arg| match arg {
        FnArg::Receiver(..) => None,
        FnArg::Typed(pt) => match &*pt.ty {
            Type::Ptr(TypePtr { elem, .. }) => match &**elem {
                Type::Path(typ) => Some(QualifiedName::from_type_path(typ)),
                _ => None,
            },
            _ => None,
        },
    })
}
<|endoftext|>


<commit_before>use super::DELIMITER;

use super::super::crypto::did::DidValue;

use std::collections::{HashMap, HashSet};
use named_type::NamedType;

use indy_api_types::validation::Validatable;
use crate::utils::qualifier;

pub const MAX_ATTRIBUTES_COUNT: usize = 125;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SchemaV1 {
    pub id: SchemaId,
    pub name: String,
    pub version: String,
    #[serde(rename = "attrNames")]
    pub attr_names: AttributeNames,
    pub seq_no: Option<u32>,
}

#[derive(Debug, Serialize, Deserialize, NamedType)]
#[serde(tag = "ver")]
pub enum Schema {
    #[serde(rename = "1.0")]
    SchemaV1(SchemaV1)
}

impl Schema {
    pub fn to_unqualified(self) -> Schema {
        match self {
            Schema::SchemaV1(schema) => {
                Schema::SchemaV1(SchemaV1 {
                    id: schema.id.to_unqualified(),
                    name: schema.name,
                    version: schema.version,
                    attr_names: schema.attr_names,
                    seq_no: schema.seq_no,
                })
            }
        }
    }
}

impl From<Schema> for SchemaV1 {
    fn from(schema: Schema) -> Self {
        match schema {
            Schema::SchemaV1(schema) => schema
        }
    }
}

pub type Schemas = HashMap<SchemaId, Schema>;

pub fn schemas_map_to_schemas_v1_map(schemas: Schemas) -> HashMap<SchemaId, SchemaV1> {
    schemas.into_iter().map(|(schema_id, schema)| { (schema_id, SchemaV1::from(schema)) }).collect()
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AttributeNames(pub HashSet<String>);

#[allow(dead_code)]
impl AttributeNames {
    pub fn new() -> Self {
        AttributeNames(HashSet::new())
    }
}

impl From<HashSet<String>> for AttributeNames {
    fn from(attrs: HashSet<String>) -> Self {
        AttributeNames(attrs)
    }
}

impl Into<HashSet<String>> for AttributeNames {
    fn into(self) -> HashSet<String> {
        self.0
    }
}

impl Validatable for Schema {
    fn validate(&self) -> Result<(), String> {
        match self {
            Schema::SchemaV1(schema) => {
                schema.attr_names.validate()?;
                schema.id.validate()?;
                if let Some((_, name, version)) = schema.id.parts() {
                    if name != schema.name {
                        return Err(format!("Inconsistent Schema Id and Schema Name: {:?} and {}", schema.id, schema.name))
                    }
                    if version != schema.version {
                        return Err(format!("Inconsistent Schema Id and Schema Version: {:?} and {}", schema.id, schema.version))
                    }
                }
                Ok(())
            }
        }
    }
}

impl Validatable for AttributeNames {
    fn validate(&self) -> Result<(), String> {
        if self.0.is_empty() {
            return Err(String::from("Empty list of Schema attributes has been passed"));
        }

        if self.0.len() > MAX_ATTRIBUTES_COUNT {
            return Err(format!("The number of Schema attributes {} cannot be greater than {}", self.0.len(), MAX_ATTRIBUTES_COUNT));
        }
        Ok(())
    }
}

qualifiable_type!(SchemaId);

impl SchemaId {
    pub const PREFIX: &'static str = "schema";
    pub const MARKER: &'static str = "2";

    pub fn new(did: &DidValue, name: &str, version: &str) -> SchemaId {
        let id = SchemaId(format!("{}{}{}{}{}{}{}", did.0, DELIMITER, Self::MARKER, DELIMITER, name, DELIMITER, version));
        match did.get_method() {
            Some(method) => id.set_method(&method),
            None => id
        }
    }

    pub fn parts(&self) -> Option<(DidValue, String, String)> {
        let parts = self.0.split_terminator(DELIMITER).collect::<Vec<&str>>();

        if parts.len() == 1 {
            // 1
            return None;
        }

        if parts.len() == 4 {
            // NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0
            let did = parts[0].to_string();
            let name = parts[2].to_string();
            let version = parts[3].to_string();
            return Some((DidValue(did), name, version));
        }

        if parts.len() == 8 {
            // schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0
            let did = parts[2..5].join(DELIMITER);
            let name = parts[6].to_string();
            let version = parts[7].to_string();
            return Some((DidValue(did), name, version));
        }

        None
    }

    pub fn qualify(&self, method: &str) -> SchemaId {
        match self.parts() {
            Some((did, name, version)) => {
                SchemaId::new(&did.qualify(method), &name, &version)
            }
            None => self.clone()
        }
    }

    pub fn to_unqualified(&self) -> SchemaId {
        match self.parts() {
            Some((did, name, version)) => {
                SchemaId::new(&did.to_unqualified(), &name, &version)
            }
            None => self.clone()
        }
    }
}

impl Validatable for SchemaId {
    fn validate(&self) -> Result<(), String> {
        if self.0.parse::<i32>().is_ok() {
            return Ok(());
        }

        self.parts().ok_or(format!("SchemaId validation failed: {:?}, doesn't match pattern", self.0))?;

        Ok(())
    }
}

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

    fn _did() -> DidValue {
        DidValue("NcYxiDXkpYi6ov5FcYDi1e".to_string())
    }

    fn _did_qualified() -> DidValue {
        DidValue("did:sov:NcYxiDXkpYi6ov5FcYDi1e".to_string())
    }

    fn _schema_id_seq_no() -> SchemaId {
        SchemaId("1".to_string())
    }

    fn _schema_id_unqualified() -> SchemaId {
        SchemaId("NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0".to_string())
    }

    fn _schema_id_qualified() -> SchemaId {
        SchemaId("schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0".to_string())
    }

    fn _schema_id_invalid() -> SchemaId {
        SchemaId("NcYxiDXkpYi6ov5FcYDi1e:2".to_string())
    }

    mod to_unqualified {
        use super::*;

        #[test]
        fn test_schema_id_unqualify_for_id_as_seq_no() {
            assert_eq!(_schema_id_seq_no(), _schema_id_seq_no().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_unqualified() {
            assert_eq!(_schema_id_unqualified(), _schema_id_unqualified().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_qualified() {
            assert_eq!(_schema_id_unqualified(), _schema_id_qualified().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_invalid_unqualified() {
            assert_eq!(_schema_id_invalid(), _schema_id_invalid().to_unqualified());
        }
    }

    mod parts {
        use super::*;

        #[test]
        fn test_schema_id_parts_for_id_as_seq_no() {
            assert!(_schema_id_seq_no().parts().is_none());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_unqualified() {
            let (did, _, _) = _schema_id_unqualified().parts().unwrap();
            assert_eq!(_did(), did);
        }

        #[test]
        fn test_schema_id_parts_for_id_as_qualified() {
            let (did, _, _) = _schema_id_qualified().parts().unwrap();
            assert_eq!(_did_qualified(), did);
        }

        #[test]
        fn test_schema_id_parts_for_invalid_unqualified() {
            assert!(_schema_id_invalid().parts().is_none());
        }
    }

    mod validate {
        use super::*;

        #[test]
        fn test_validate_schema_id_as_seq_no() {
            _schema_id_seq_no().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_as_unqualified() {
            _schema_id_unqualified().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_as_fully_qualified() {
            _schema_id_qualified().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_for_invalid_unqualified() {
            _schema_id_invalid().validate().unwrap_err();
        }

        #[test]
        fn test_validate_schema_id_for_invalid_fully_qualified() {
            let id = SchemaId("schema:sov:NcYxiDXkpYi6ov5FcYDi1e:2:1.0".to_string());
            id.validate().unwrap_err();
        }
    }
}<commit_msg>IS-1430 -- Add tests for schema ID validation<commit_after>use super::DELIMITER;

use super::super::crypto::did::DidValue;

use std::collections::{HashMap, HashSet};
use named_type::NamedType;

use indy_api_types::validation::Validatable;
use crate::utils::qualifier;

pub const MAX_ATTRIBUTES_COUNT: usize = 125;

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SchemaV1 {
    pub id: SchemaId,
    pub name: String,
    pub version: String,
    #[serde(rename = "attrNames")]
    pub attr_names: AttributeNames,
    pub seq_no: Option<u32>,
}

#[derive(Debug, Serialize, Deserialize, NamedType)]
#[serde(tag = "ver")]
pub enum Schema {
    #[serde(rename = "1.0")]
    SchemaV1(SchemaV1)
}

impl Schema {
    pub fn to_unqualified(self) -> Schema {
        match self {
            Schema::SchemaV1(schema) => {
                Schema::SchemaV1(SchemaV1 {
                    id: schema.id.to_unqualified(),
                    name: schema.name,
                    version: schema.version,
                    attr_names: schema.attr_names,
                    seq_no: schema.seq_no,
                })
            }
        }
    }
}

impl From<Schema> for SchemaV1 {
    fn from(schema: Schema) -> Self {
        match schema {
            Schema::SchemaV1(schema) => schema
        }
    }
}

pub type Schemas = HashMap<SchemaId, Schema>;

pub fn schemas_map_to_schemas_v1_map(schemas: Schemas) -> HashMap<SchemaId, SchemaV1> {
    schemas.into_iter().map(|(schema_id, schema)| { (schema_id, SchemaV1::from(schema)) }).collect()
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AttributeNames(pub HashSet<String>);

#[allow(dead_code)]
impl AttributeNames {
    pub fn new() -> Self {
        AttributeNames(HashSet::new())
    }
}

impl From<HashSet<String>> for AttributeNames {
    fn from(attrs: HashSet<String>) -> Self {
        AttributeNames(attrs)
    }
}

impl Into<HashSet<String>> for AttributeNames {
    fn into(self) -> HashSet<String> {
        self.0
    }
}

impl Validatable for Schema {
    fn validate(&self) -> Result<(), String> {
        match self {
            Schema::SchemaV1(schema) => {
                schema.attr_names.validate()?;
                schema.id.validate()?;
                if let Some((_, name, version)) = schema.id.parts() {
                    if name != schema.name {
                        return Err(format!("Inconsistent Schema Id and Schema Name: {:?} and {}", schema.id, schema.name))
                    }
                    if version != schema.version {
                        return Err(format!("Inconsistent Schema Id and Schema Version: {:?} and {}", schema.id, schema.version))
                    }
                }
                Ok(())
            }
        }
    }
}

impl Validatable for AttributeNames {
    fn validate(&self) -> Result<(), String> {
        if self.0.is_empty() {
            return Err(String::from("Empty list of Schema attributes has been passed"));
        }

        if self.0.len() > MAX_ATTRIBUTES_COUNT {
            return Err(format!("The number of Schema attributes {} cannot be greater than {}", self.0.len(), MAX_ATTRIBUTES_COUNT));
        }
        Ok(())
    }
}

qualifiable_type!(SchemaId);

impl SchemaId {
    pub const PREFIX: &'static str = "schema";
    pub const MARKER: &'static str = "2";

    pub fn new(did: &DidValue, name: &str, version: &str) -> SchemaId {
        let id = SchemaId(format!("{}{}{}{}{}{}{}", did.0, DELIMITER, Self::MARKER, DELIMITER, name, DELIMITER, version));
        match did.get_method() {
            Some(method) => id.set_method(&method),
            None => id
        }
    }

    pub fn parts(&self) -> Option<(DidValue, String, String)> {
        let parts = self.0.split_terminator(DELIMITER).collect::<Vec<&str>>();

        if parts.len() == 1 {
            // 1
            return None;
        }

        if parts.len() == 4 {
            // NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0
            let did = parts[0].to_string();
            let name = parts[2].to_string();
            let version = parts[3].to_string();
            return Some((DidValue(did), name, version));
        }

        if parts.len() == 8 {
            // schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0
            let did = parts[2..5].join(DELIMITER);
            let name = parts[6].to_string();
            let version = parts[7].to_string();
            return Some((DidValue(did), name, version));
        }

        None
    }

    pub fn qualify(&self, method: &str) -> SchemaId {
        match self.parts() {
            Some((did, name, version)) => {
                SchemaId::new(&did.qualify(method), &name, &version)
            }
            None => self.clone()
        }
    }

    pub fn to_unqualified(&self) -> SchemaId {
        match self.parts() {
            Some((did, name, version)) => {
                SchemaId::new(&did.to_unqualified(), &name, &version)
            }
            None => self.clone()
        }
    }
}

impl Validatable for SchemaId {
    fn validate(&self) -> Result<(), String> {
        if self.0.parse::<i32>().is_ok() {
            return Ok(());
        }

        self.parts().ok_or(format!("SchemaId validation failed: {:?}, doesn't match pattern", self.0))?;

        Ok(())
    }
}

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

    fn _did() -> DidValue {
        DidValue("NcYxiDXkpYi6ov5FcYDi1e".to_string())
    }

    fn _did_qualified() -> DidValue {
        DidValue("did:sov:NcYxiDXkpYi6ov5FcYDi1e".to_string())
    }

    fn _schema_id_seq_no() -> SchemaId {
        SchemaId("1".to_string())
    }

    fn _schema_id_unqualified() -> SchemaId {
        SchemaId("NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0".to_string())
    }

    fn _schema_id_qualified() -> SchemaId {
        SchemaId("schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0".to_string())
    }

    fn _schema_id_invalid() -> SchemaId {
        SchemaId("NcYxiDXkpYi6ov5FcYDi1e:2".to_string())
    }

    mod to_unqualified {
        use super::*;

        #[test]
        fn test_schema_id_unqualify_for_id_as_seq_no() {
            assert_eq!(_schema_id_seq_no(), _schema_id_seq_no().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_unqualified() {
            assert_eq!(_schema_id_unqualified(), _schema_id_unqualified().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_qualified() {
            assert_eq!(_schema_id_unqualified(), _schema_id_qualified().to_unqualified());
        }

        #[test]
        fn test_schema_id_parts_for_invalid_unqualified() {
            assert_eq!(_schema_id_invalid(), _schema_id_invalid().to_unqualified());
        }
    }

    mod parts {
        use super::*;

        #[test]
        fn test_schema_id_parts_for_id_as_seq_no() {
            assert!(_schema_id_seq_no().parts().is_none());
        }

        #[test]
        fn test_schema_id_parts_for_id_as_unqualified() {
            let (did, _, _) = _schema_id_unqualified().parts().unwrap();
            assert_eq!(_did(), did);
        }

        #[test]
        fn test_schema_id_parts_for_id_as_qualified() {
            let (did, _, _) = _schema_id_qualified().parts().unwrap();
            assert_eq!(_did_qualified(), did);
        }

        #[test]
        fn test_schema_id_parts_for_invalid_unqualified() {
            assert!(_schema_id_invalid().parts().is_none());
        }
    }

    mod validate {
        use super::*;

        #[test]
        fn test_validate_schema_id_as_seq_no() {
            _schema_id_seq_no().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_as_unqualified() {
            _schema_id_unqualified().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_as_fully_qualified() {
            _schema_id_qualified().validate().unwrap();
        }

        #[test]
        fn test_validate_schema_id_for_invalid_unqualified() {
            _schema_id_invalid().validate().unwrap_err();
        }

        #[test]
        fn test_validate_schema_id_for_invalid_fully_qualified() {
            let id = SchemaId("schema:sov:NcYxiDXkpYi6ov5FcYDi1e:2:1.0".to_string());
            id.validate().unwrap_err();
        }
    }

    mod test_schema_validation {
        use super::*;

        #[test]
        fn test_valid_schema() {
            let schema_json = json!({
                "id": _schema_id_qualified(),
                "name": "gvt",
                "ver": "1.0",
                "version": "1.0",
                "attrNames": [],
            }).to_string();

            let schema: Schema = serde_json::from_str(&schema_json).unwrap();
            schema.validate().unwrap();
            match schema {
                Schema::SchemaV1(schema) => {
                    assert_eq!(schema.name, "gvt");
                    assert_eq!(schema.version, "1.0");
                }
            }
        }

        #[test]
        fn test_invalid_name_schema() {
            let schema_json = json!({
                "id": _schema_id_qualified(),
                "name": "gvt1",
                "ver": "1.0",
                "version": "1.0",
                "attrNames": [],
            }).to_string();

            let schema: Schema = serde_json::from_str(&schema_json).unwrap();
            schema.validate().unwrap_err();
        }

        #[test]
        fn test_invalid_version_schema() {
            let schema_json = json!({
                "id": _schema_id_qualified(),
                "name": "gvt",
                "ver": "1.0",
                "version": "1.1",
                "attrNames": [],
            }).to_string();

            let schema: Schema = serde_json::from_str(&schema_json).unwrap();
            schema.validate().unwrap_err();
        }
    }
}<|endoftext|>


<commit_before>use analysis::rust_type::Result;
use env::Env;
use gobjects::GStatus;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                if let &library::Type::FixedArray(_, size) = env.library.type_(tid) {
                    ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size))
                }
                else {
                    ffi_inner(env, c_tid, c_type.into())
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, inner: String) -> Result {
    match *env.library.type_(tid) {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_char",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    _ => return Ok(inner),
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name && !implements_c_type(&env.library, tid, &inner) {
                    let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                      env.library.type_(tid).get_name());
                    warn!("{}", msg);
                    Err(msg)
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}_ffi::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}_ffi::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)) == GStatus::Ignore {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}

fn rustify_pointers(c_type: &str) -> (String, String) {
    let mut input = c_type.trim();
    let leading_const = input.starts_with("const ");
    if leading_const {
        input = &input[6..];
    }
    let end = [
        input.find(" const"),
        input.find("*const"),
        input.find("*"),
        Some(input.len()),
    ].iter().filter_map(|&x| x).min().unwrap();
    let inner = input[..end].trim().into();

    let mut ptrs: Vec<_> = input[end..].rsplit('*').skip(1)
        .map(|s| if s.contains("const") { "*const" } else { "*mut" }).collect();
    if let (true, Some(p)) = (leading_const, ptrs.last_mut()) {
        *p = "*const";
    }

    let res = (ptrs.connect(" "), inner);
    trace!("rustify `{}` -> `{}` `{}`", c_type, res.0, res.1);
    res
}

#[cfg(test)]
mod tests {
    use super::rustify_pointers as rustify_ptr;

    fn s(x: &str, y: &str) -> (String, String) {
        (x.into(), y.into())
    }

    #[test]
    fn rustify_pointers() {
        assert_eq!(rustify_ptr("char"), s("", "char"));
        assert_eq!(rustify_ptr("char*"), s("*mut", "char"));
        assert_eq!(rustify_ptr("const char*"), s("*const", "char"));
        assert_eq!(rustify_ptr("char const*"), s("*const", "char"));
        assert_eq!(rustify_ptr("char const *"), s("*const", "char"));
        assert_eq!(rustify_ptr(" char * * "), s("*mut *mut", "char"));
        assert_eq!(rustify_ptr("const char**"), s("*mut *const", "char"));
        assert_eq!(rustify_ptr("char const**"), s("*mut *const", "char"));
        assert_eq!(rustify_ptr("const char* const*"), s("*const *const", "char"));
        assert_eq!(rustify_ptr("char const * const *"), s("*const *const", "char"));
        assert_eq!(rustify_ptr("char* const*"), s("*const *mut", "char"));
    }
}
<commit_msg>ffi_type: detect c_type mismatches for records, aliases and functions<commit_after>use analysis::rust_type::Result;
use env::Env;
use gobjects::GStatus;
use library;
use library::*;
use nameutil::crate_name;
use traits::*;

// FIXME: This module needs redundant allocations audit
// TODO: ffi_type computations should be cached

pub fn ffi_type(env: &Env, tid: library::TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library.type_(c_tid).maybe_ref_as::<Fundamental>().is_some() {
                if let &library::Type::FixedArray(_, size) = env.library.type_(tid) {
                    ffi_inner(env, c_tid, c_type.into())
                        .map_any(|s| format!("[{}; {}]", s, size))
                }
                else {
                    ffi_inner(env, c_tid, c_type.into())
                }
            }
            else { // c_type isn't fundamental
                ffi_inner(env, tid, inner)
            }
        }
        else { // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, inner)
        }
    }
    else { // ptr not empty
        ffi_inner(env, tid, inner)
            .map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: library::TypeId, inner: String) -> Result {
    let typ = env.library.type_(tid);
    match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "c_void",
                Boolean => "gboolean",
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "c_char",
                UChar => "c_char",
                Short => "c_short",
                UShort => "c_ushort",
                Int => "c_int",
                UInt => "c_uint",
                Long => "c_long",
                ULong => "c_ulong",
                Size => "size_t",
                SSize => "ssize_t",
                Float => "c_float",
                Double => "c_double",
                UniChar => "u32",
                Utf8 => "c_char",
                Filename => "c_char",
                Type => "GType",
                Pointer => match &inner[..]  {
                    "void" => "c_void",
                    _ => return Ok(inner),
                },
                Unsupported => return Err(format!("[Unsupported type {}]", inner)),
                VarArgs => panic!("Should not reach here"),
            };
            Ok(inner.into())
        }
        Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!("[c:type mismatch `{}` != `{}` of `{}`]",
                                      inner, declared_c_type, typ.get_name());
                    warn!("{}", msg);
                    return Err(msg);
                }
            }
            else {
                warn!("Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, &inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size) => {
            match ffi_inner(env, inner_tid, inner) {
                Ok(s) => Ok(format!("[{}; {}]", s, size)),
                Err(s) => Err(format!("[{}; {}]", s, size)),
            }
        }
        Type::Array(..) | Type::PtrArray(..)
                | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
            fix_name(env, tid, &inner)
        }
        _ => {
            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
                if inner != glib_name && !implements_c_type(&env.library, tid, &inner) {
                    let msg = format!("[c:type mismatch {} != {} of {}]", inner, glib_name,
                      env.library.type_(tid).get_name());
                    warn!("{}", msg);
                    Err(msg)
                }
                else {
                    fix_name(env, tid, &inner)
                }
            }
            else {
                let msg = format!("[Missing glib_name of {}, can't match != {}]",
                                  env.library.type_(tid).get_name(), inner);
                warn!("{}", msg);
                Err(msg)
            }
        }
    }
}

fn fix_name(env: &Env, type_id: library::TypeId, name: &str) -> Result {
    if type_id.ns_id == library::INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) | Type::PtrArray(..)
                    | Type::List(..) | Type::SList(..) | Type::HashTable(..) => {
                if Some(MAIN_NAMESPACE) == env.library.find_namespace("GLib") {
                    Ok(name.into())
                }
                else {
                    Ok(format!("{}_ffi::{}", crate_name("GLib"), name))
                }
            }
            _ => Ok(name.into())
        }
    } else {
        let name_with_prefix = if type_id.ns_id == library::MAIN_NAMESPACE {
            name.into()
        } else {
            format!("{}_ffi::{}", fix_namespace(env, type_id), name)
        };
        if env.type_status_sys(&type_id.full_name(&env.library)) == GStatus::Ignore {
            Err(name_with_prefix)
        } else {
            Ok(name_with_prefix)
        }
    }
}

//TODO: check if need to use in non sys codegen
fn fix_namespace(env: &Env, type_id: library::TypeId) -> String {
    crate_name(&env.library.namespace(type_id.ns_id).name)
}

fn implements_c_type(library: &library::Library, tid: TypeId, c_type: &str) -> bool {
    if let Some(ref klass) = library.type_(tid).maybe_ref_as::<library::Class>() {
        klass.implements.iter().chain(klass.parents.iter())
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else if let Some(ref iface) = library.type_(tid).maybe_ref_as::<library::Interface>() {
        iface.prereq_parents.iter()
            .any(|&super_tid| library.type_(super_tid).get_glib_name() == Some(c_type))
    }
    else {
        false
    }
}

fn rustify_pointers(c_type: &str) -> (String, String) {
    let mut input = c_type.trim();
    let leading_const = input.starts_with("const ");
    if leading_const {
        input = &input[6..];
    }
    let end = [
        input.find(" const"),
        input.find("*const"),
        input.find("*"),
        Some(input.len()),
    ].iter().filter_map(|&x| x).min().unwrap();
    let inner = input[..end].trim().into();

    let mut ptrs: Vec<_> = input[end..].rsplit('*').skip(1)
        .map(|s| if s.contains("const") { "*const" } else { "*mut" }).collect();
    if let (true, Some(p)) = (leading_const, ptrs.last_mut()) {
        *p = "*const";
    }

    let res = (ptrs.connect(" "), inner);
    trace!("rustify `{}` -> `{}` `{}`", c_type, res.0, res.1);
    res
}

#[cfg(test)]
mod tests {
    use super::rustify_pointers as rustify_ptr;

    fn s(x: &str, y: &str) -> (String, String) {
        (x.into(), y.into())
    }

    #[test]
    fn rustify_pointers() {
        assert_eq!(rustify_ptr("char"), s("", "char"));
        assert_eq!(rustify_ptr("char*"), s("*mut", "char"));
        assert_eq!(rustify_ptr("const char*"), s("*const", "char"));
        assert_eq!(rustify_ptr("char const*"), s("*const", "char"));
        assert_eq!(rustify_ptr("char const *"), s("*const", "char"));
        assert_eq!(rustify_ptr(" char * * "), s("*mut *mut", "char"));
        assert_eq!(rustify_ptr("const char**"), s("*mut *const", "char"));
        assert_eq!(rustify_ptr("char const**"), s("*mut *const", "char"));
        assert_eq!(rustify_ptr("const char* const*"), s("*const *const", "char"));
        assert_eq!(rustify_ptr("char const * const *"), s("*const *const", "char"));
        assert_eq!(rustify_ptr("char* const*"), s("*const *mut", "char"));
    }
}
<|endoftext|>


<commit_before>pub fn sum_of_multiples(m: u64, ns: &Vec<u64>) -> u64 {
    (0..m).filter(|k| ns.into_iter().any(|&n| k % n == 0)).sum()
}
<commit_msg>modified<commit_after>pub fn sum_of_multiples(m: u64, ns: &[u64]) -> u64 {
    (0..m).filter(|k| ns.into_iter().any(|&n| k % n == 0)).sum()
}
<|endoftext|>


<commit_before>#![cfg(feature = "engrafo")]
// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

//! base class automating dispatcher communication via ZMQ

use std::fs::File;
use std::io::{Error, Write};
use std::path::Path;
use std::process::Command;
use tempdir::TempDir;

use super::Worker;
use crate::adaptor;

/// An echo worker for testing
pub struct EngrafoWorker {
  /// the usual
  pub service: String,
  /// the usual
  pub version: f32,
  /// the usual
  pub message_size: usize,
  /// the usual
  pub source: String,
  /// the usual
  pub sink: String,
}
impl Default for EngrafoWorker {
  fn default() -> EngrafoWorker {
    EngrafoWorker {
      service: "engrafo".to_string(),
      version: 2.0,
      message_size: 100_000,
      source: "tcp://127.0.0.1:51695".to_string(),
      sink: "tcp://127.0.0.1:51696".to_string(),
    }
  }
}

impl Worker for EngrafoWorker {
  fn service(&self) -> String {
    self.service.clone()
  }
  fn source(&self) -> String {
    self.source.clone()
  }
  fn sink(&self) -> String {
    self.sink.clone()
  }
  fn message_size(&self) -> usize {
    self.message_size
  }

  fn convert(&self, path: &Path) -> Option<File> {
    match self.convert_result(path) {
      Ok(file) => Some(file),
      Err(e) => {
        println!("Error encountered while converting: {:?}", e);
        None
      }
    }
  }
}

impl EngrafoWorker {
  fn convert_result(&self, path: &Path) -> Result<File, Error> {
    let input_tmpdir = adaptor::extract_zip_to_tmpdir(path, "engrafo_input")?;
    let unpacked_dir_path = input_tmpdir.path().to_str().unwrap().to_string() + "/";
    let destination_tmpdir = TempDir::new("engrafo_output").unwrap();
    let destination_dir_path = destination_tmpdir.path().to_str().unwrap();

    let cmd_result = Command::new("docker")
      .arg("run")
      .arg("-v")
      .arg("/tmp:/tmp")
      .arg("-w")
      .arg("/tmp")
      .arg("arxivvanity/engrafo:2.0.0")
      .arg("engrafo")
      .arg(unpacked_dir_path)
      .arg(destination_dir_path)
      .output()
      .expect("failed to execute process engrafo docker process.");

    // Package the output -- cortex requires a single ZIP return,
    // with all logging information stored in a "cortex.log" file at the ZIP's root.

    let log_name = format!("{}/cortex.log", destination_dir_path);
    let cortex_log_path = Path::new(&log_name);
    {
      // write log file and close it before archiving.
      let mut log_file = File::create(&cortex_log_path)?;
      log_file.write_all(&cmd_result.stderr)?;
      log_file.write_all(&cmd_result.stdout)?;
    }

    // cleanup
    // By closing the `TempDir` explicitly, we can check that it has
    // been deleted successfully. If we don't close it explicitly,
    // the directory will still be deleted when `tmp_dir` goes out
    // of scope, but we won't know whether deleting the directory
    // succeeded.
    input_tmpdir.close().unwrap();

    adaptor::archive_tmpdir_to_zip(destination_tmpdir)
  }
}
<commit_msg>use a /workdir mount for docker hygiene<commit_after>#![cfg(feature = "engrafo")]
// Copyright 2015 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.

//! base class automating dispatcher communication via ZMQ

use std::env;
use std::fs::File;
use std::io::{Error, Write};
use std::path::Path;
use std::process::Command;
use tempdir::TempDir;

use super::Worker;
use crate::adaptor;

/// An echo worker for testing
pub struct EngrafoWorker {
  /// the usual
  pub service: String,
  /// the usual
  pub version: f32,
  /// the usual
  pub message_size: usize,
  /// the usual
  pub source: String,
  /// the usual
  pub sink: String,
}
impl Default for EngrafoWorker {
  fn default() -> EngrafoWorker {
    EngrafoWorker {
      service: "engrafo".to_string(),
      version: 2.0,
      message_size: 100_000,
      source: "tcp://127.0.0.1:51695".to_string(),
      sink: "tcp://127.0.0.1:51696".to_string(),
    }
  }
}

impl Worker for EngrafoWorker {
  fn service(&self) -> String {
    self.service.clone()
  }
  fn source(&self) -> String {
    self.source.clone()
  }
  fn sink(&self) -> String {
    self.sink.clone()
  }
  fn message_size(&self) -> usize {
    self.message_size
  }

  fn convert(&self, path: &Path) -> Option<File> {
    match self.convert_result(path) {
      Ok(file) => Some(file),
      Err(e) => {
        println!("Error encountered while converting: {:?}", e);
        None
      }
    }
  }
}

impl EngrafoWorker {
  fn convert_result(&self, path: &Path) -> Result<File, Error> {
    let input_tmpdir = adaptor::extract_zip_to_tmpdir(path, "engrafo_input")?;
    let unpacked_dir_path = input_tmpdir.path().to_str().unwrap().to_string() + "/";
    let destination_tmpdir = TempDir::new("engrafo_output").unwrap();
    let destination_dir_path = destination_tmpdir.path().to_str().unwrap();
    let tmp_dir_str = env::temp_dir().as_path().display().to_string();
    let docker_input_path = unpacked_dir_path.replace(&tmp_dir_str, "/workdir");
    let docker_output_path = destination_dir_path.replace(&tmp_dir_str, "/workdir");

    let cmd_result = Command::new("docker")
      .arg("run")
      .arg("-v")
      .arg(format!("{}:/workdir", tmp_dir_str))
      .arg("-w")
      .arg("/workdir")
      .arg("arxivvanity/engrafo:2.0.0")
      .arg("engrafo")
      .arg(dbg!(docker_input_path))
      .arg(dbg!(docker_output_path))
      .output()
      .expect("failed to execute process engrafo docker process.");

    // Package the output -- cortex requires a single ZIP return,
    // with all logging information stored in a "cortex.log" file at the ZIP's root.

    let log_name = format!("{}/cortex.log", destination_dir_path);
    let cortex_log_path = Path::new(&log_name);
    {
      // write log file and close it before archiving.
      let mut log_file = File::create(&cortex_log_path)?;
      log_file.write_all(&cmd_result.stderr)?;
      log_file.write_all(&cmd_result.stdout)?;
    }

    // cleanup
    // By closing the `TempDir` explicitly, we can check that it has
    // been deleted successfully. If we don't close it explicitly,
    // the directory will still be deleted when `tmp_dir` goes out
    // of scope, but we won't know whether deleting the directory
    // succeeded.
    input_tmpdir.close().unwrap();

    adaptor::archive_tmpdir_to_zip(destination_tmpdir)
  }
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use glib::prelude::*;
use gst::prelude::*;

use std::env;

fn main() {
    gst::init().unwrap();

    #[cfg(debug_assertions)]
    {
        use std::path::Path;

        let mut path = Path::new("target/debug");
        if !path.exists() {
            path = Path::new("../../target/debug");
        }

        gst::Registry::get().scan_path(path);
    }
    #[cfg(not(debug_assertions))]
    {
        use std::path::Path;

        let mut path = Path::new("target/release");
        if !path.exists() {
            path = Path::new("../../target/release");
        }

        gst::Registry::get().scan_path(path);
    }

    let args = env::args().collect::<Vec<_>>();
    assert_eq!(args.len(), 6);
    let n_streams: u16 = args[1].parse().unwrap();
    let source = &args[2];
    let n_groups: u32 = args[3].parse().unwrap();
    let wait: u32 = args[4].parse().unwrap();

    let l = glib::MainLoop::new(None, false);
    let pipeline = gst::Pipeline::new(None);

    for i in 0..n_streams {
        let sink =
            gst::ElementFactory::make("fakesink", Some(format!("sink-{}", i).as_str())).unwrap();
        sink.set_property("sync", &false).unwrap();
        sink.set_property("async", &false).unwrap();

        let source = match source.as_str() {
            "udpsrc" => {
                let source =
                    gst::ElementFactory::make("udpsrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("port", &(40000i32 + (i as i32)))
                    .unwrap();
                source
                    .set_property("retrieve-sender-address", &false)
                    .unwrap();

                source
            }
            "ts-udpsrc" => {
                let source =
                    gst::ElementFactory::make("ts-udpsrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("port", &(40000u32 + (i as u32)))
                    .unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            "ts-tcpclientsrc" => {
                let source = gst::ElementFactory::make(
                    "ts-tcpclientsrc",
                    Some(format!("source-{}", i).as_str()),
                )
                .unwrap();
                source.set_property("port", &(40000u32)).unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            "tonegeneratesrc" => {
                let source = gst::ElementFactory::make(
                    "tonegeneratesrc",
                    Some(format!("source-{}", i).as_str()),
                )
                .unwrap();
                source
                    .set_property("samplesperbuffer", &((wait as i32) * 8000 / 1000))
                    .unwrap();

                sink.set_property("sync", &true).unwrap();

                source
            }
            "ts-tonesrc" => {
                let source =
                    gst::ElementFactory::make("ts-tonesrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("samples-per-buffer", &((wait as u32) * 8000 / 1000))
                    .unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            _ => unimplemented!(),
        };

        pipeline.add_many(&[&source, &sink]).unwrap();
        source.link(&sink).unwrap();
    }

    let bus = pipeline.get_bus().unwrap();
    let l_clone = l.clone();
    bus.add_watch(move |_, msg| {
        use gst::MessageView;

        match msg.view() {
            MessageView::Eos(..) => l_clone.quit(),
            MessageView::Error(err) => {
                println!(
                    "Error from {:?}: {} ({:?})",
                    err.get_src().map(|s| s.get_path_string()),
                    err.get_error(),
                    err.get_debug()
                );
                l_clone.quit();
            }
            _ => (),
        };

        glib::Continue(true)
    })
    .expect("Failed to add bus watch");

    pipeline.set_state(gst::State::Playing).unwrap();

    println!("started");

    l.run();
}
<commit_msg>threadshare: benchmark: add throughput tracking<commit_after>// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.

use glib::prelude::*;
use gst::prelude::*;

use std::env;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};

const THROUGHPUT_PERIOD: Duration = Duration::from_secs(20);

fn main() {
    gst::init().unwrap();

    #[cfg(debug_assertions)]
    {
        use std::path::Path;

        let mut path = Path::new("target/debug");
        if !path.exists() {
            path = Path::new("../../target/debug");
        }

        gst::Registry::get().scan_path(path);
    }
    #[cfg(not(debug_assertions))]
    {
        use std::path::Path;

        let mut path = Path::new("target/release");
        if !path.exists() {
            path = Path::new("../../target/release");
        }

        gst::Registry::get().scan_path(path);
    }

    let args = env::args().collect::<Vec<_>>();
    assert_eq!(args.len(), 6);
    let n_streams: u16 = args[1].parse().unwrap();
    let source = &args[2];
    let n_groups: u32 = args[3].parse().unwrap();
    let wait: u32 = args[4].parse().unwrap();

    let l = glib::MainLoop::new(None, false);
    let pipeline = gst::Pipeline::new(None);
    let counter = Arc::new(AtomicU64::new(0));

    for i in 0..n_streams {
        let sink =
            gst::ElementFactory::make("fakesink", Some(format!("sink-{}", i).as_str())).unwrap();
        sink.set_property("sync", &false).unwrap();
        sink.set_property("async", &false).unwrap();

        let counter_clone = Arc::clone(&counter);
        sink.get_static_pad("sink").unwrap().add_probe(
            gst::PadProbeType::BUFFER,
            move |_pad, _probe_info| {
                let _ = counter_clone.fetch_add(1, Ordering::SeqCst);
                gst::PadProbeReturn::Ok
            },
        );

        let source = match source.as_str() {
            "udpsrc" => {
                let source =
                    gst::ElementFactory::make("udpsrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("port", &(40000i32 + (i as i32)))
                    .unwrap();
                source
                    .set_property("retrieve-sender-address", &false)
                    .unwrap();

                source
            }
            "ts-udpsrc" => {
                let source =
                    gst::ElementFactory::make("ts-udpsrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("port", &(40000u32 + (i as u32)))
                    .unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            "tcpclientsrc" => {
                let source = gst::ElementFactory::make(
                    "tcpclientsrc",
                    Some(format!("source-{}", i).as_str()),
                )
                .unwrap();
                source.set_property("host", &"127.0.0.1").unwrap();
                source.set_property("port", &(40000i32)).unwrap();

                source
            }
            "ts-tcpclientsrc" => {
                let source = gst::ElementFactory::make(
                    "ts-tcpclientsrc",
                    Some(format!("source-{}", i).as_str()),
                )
                .unwrap();
                source.set_property("address", &"127.0.0.1").unwrap();
                source.set_property("port", &(40000u32)).unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            "tonegeneratesrc" => {
                let source = gst::ElementFactory::make(
                    "tonegeneratesrc",
                    Some(format!("source-{}", i).as_str()),
                )
                .unwrap();
                source
                    .set_property("samplesperbuffer", &((wait as i32) * 8000 / 1000))
                    .unwrap();

                sink.set_property("sync", &true).unwrap();

                source
            }
            "ts-tonesrc" => {
                let source =
                    gst::ElementFactory::make("ts-tonesrc", Some(format!("source-{}", i).as_str()))
                        .unwrap();
                source
                    .set_property("samples-per-buffer", &((wait as u32) * 8000 / 1000))
                    .unwrap();
                source
                    .set_property("context", &format!("context-{}", (i as u32) % n_groups))
                    .unwrap();
                source.set_property("context-wait", &wait).unwrap();

                source
            }
            _ => unimplemented!(),
        };

        pipeline.add_many(&[&source, &sink]).unwrap();
        source.link(&sink).unwrap();
    }

    let bus = pipeline.get_bus().unwrap();
    let l_clone = l.clone();
    bus.add_watch(move |_, msg| {
        use gst::MessageView;

        match msg.view() {
            MessageView::Eos(..) => l_clone.quit(),
            MessageView::Error(err) => {
                println!(
                    "Error from {:?}: {} ({:?})",
                    err.get_src().map(|s| s.get_path_string()),
                    err.get_error(),
                    err.get_debug()
                );
                l_clone.quit();
            }
            _ => (),
        };

        glib::Continue(true)
    })
    .expect("Failed to add bus watch");

    pipeline.set_state(gst::State::Playing).unwrap();

    println!("started");

    thread::spawn(move || {
        let throughput_factor = 1_000f32 / (n_streams as f32);
        let mut prev_reset_instant: Option<Instant> = None;
        let mut count;
        let mut reset_instant;

        loop {
            count = counter.fetch_and(0, Ordering::SeqCst);
            reset_instant = Instant::now();

            if let Some(prev_reset_instant) = prev_reset_instant {
                println!(
                    "{:>5.1} / s / stream",
                    (count as f32) * throughput_factor
                        / ((reset_instant - prev_reset_instant).as_millis() as f32)
                );
            }

            if let Some(sleep_duration) = THROUGHPUT_PERIOD.checked_sub(reset_instant.elapsed()) {
                thread::sleep(sleep_duration);
            }

            prev_reset_instant = Some(reset_instant);
        }
    });

    l.run();
}
<|endoftext|>