<commit_before>use std::fs::File;
use std::io::{Write, BufWriter};

use inflector::Inflector;

use Service;
use botocore::{Shape, ShapeType};
use self::json::JsonGenerator;
use self::query::QueryGenerator;
use self::rest_json::RestJsonGenerator;
use self::rest_xml::RestXmlGenerator;
use self::error_types::{GenerateErrorTypes, JsonErrorTypes, XmlErrorTypes};
use self::tests::generate_tests;
use self::type_filter::filter_types;
use util;

mod error_types;
mod json;
mod query;
mod rest_json;
pub mod tests;
mod rest_xml;
mod xml_payload_parser;
mod rest_response_parser;
mod type_filter;

type FileWriter = BufWriter<File>;
type IoResult = ::std::io::Result<()>;

/// Abstracts the generation of Rust code for various AWS protocols
pub trait GenerateProtocol {
    /// Generate the various `use` statements required by the module generatedfor this service
    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    fn generate_method_signatures(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    /// Generate a method for each `Operation` in the `Service` to execute that method remotely
    ///
    /// The method generated by this method are inserted into an enclosing `impl FooClient {}` block
    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    /// Add any attributes that should decorate the struct for the given type (typically `Debug`, `Clone`, etc.)
    fn generate_struct_attributes(&self, serialized: bool, deserialized: bool) -> String;

    /// If necessary, generate a serializer for the specified type
    fn generate_serializer(&self,
                           _name: &str,
                           _shape: &Shape,
                           _service: &Service)
                           -> Option<String> {
        None
    }

    /// If necessary, generate a deserializer for the specified type
    fn generate_deserializer(&self,
                             _name: &str,
                             _shape: &Shape,
                             _service: &Service)
                             -> Option<String> {
        None
    }

    /// Return the type used by this protocol for timestamps
    fn timestamp_type(&self) -> &'static str;
}

pub fn generate_source(service: &Service, writer: &mut FileWriter) -> IoResult {
    // EC2 service protocol is similar to query but not the same.  Rusoto is able to generate Rust code
    // from the service definition through the same QueryGenerator, but botocore uses a special class.
    // See https://github.com/boto/botocore/blob/dff99fdf2666accf6b448aef7f03fe3d66dd38fa/botocore/serialize.py#L259-L266 .
    match service.protocol() {
        "json" => generate(writer, service, JsonGenerator, JsonErrorTypes),
        "query" | "ec2" => generate(writer, service, QueryGenerator, XmlErrorTypes),
        "rest-json" => generate(writer, service, RestJsonGenerator, JsonErrorTypes),
        "rest-xml" => generate(writer, service, RestXmlGenerator, XmlErrorTypes),
        protocol => panic!("Unknown protocol {}", protocol),
    }
}

/// Translate a botocore field name to something rust-idiomatic and
/// escape reserved words with an underscore
pub fn generate_field_name(member_name: &str) -> String {
    let name = member_name.to_snake_case();
    if name == "return" || name == "type" {
        name + "_"
    } else {
        name
    }
}

/// The quick brown fox jumps over the lazy dog
fn generate<P, E>(writer: &mut FileWriter,
                  service: &Service,
                  protocol_generator: P,
                  error_type_generator: E)
                  -> IoResult
    where P: GenerateProtocol,
          E: GenerateErrorTypes
{

    writeln!(writer, "
        // =================================================================
        //
        //                           * WARNING *
        //
        //                    This file is generated!
        //
        //  Changes made to this file will be overwritten. If changes are
        //  required to the generated code, the service_crategen project
        //  must be updated to generate the changes.
        //
        // =================================================================

        #[allow(warnings)]
        use hyper::Client;
        use hyper::status::StatusCode;
        use rusoto_core::request::DispatchSignedRequest;
        use rusoto_core::region;

        use std::fmt;
        use std::error::Error;
        use rusoto_core::request::HttpDispatchError;
        use rusoto_core::credential::{{CredentialsError, ProvideAwsCredentials}};
    ")?;

    protocol_generator.generate_prelude(writer, service)?;
    generate_types(writer, service, &protocol_generator)?;
    error_type_generator
        .generate_error_types(writer, service)?;
    generate_client(writer, service, &protocol_generator)?;
    generate_tests(writer, service)?;

    Ok(())

}

fn generate_client<P>(writer: &mut FileWriter,
                      service: &Service,
                      protocol_generator: &P)
                      -> IoResult
    where P: GenerateProtocol
{
    // If the struct name is changed, the links in each service documentation should change.
    // See https://github.com/rusoto/rusoto/issues/519
    writeln!(writer,
             "/// Trait representing the capabilities of the {service_name} API. {service_name} clients implement this trait.
        pub trait {trait_name} {{
        ",
             trait_name = service.service_type_name(),
             service_name = service.name())?;

    protocol_generator
        .generate_method_signatures(writer, service)?;

    writeln!(writer, "}}")?;

    writeln!(writer,
        "/// A client for the {service_name} API.
        pub struct {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
            credentials_provider: P,
            region: region::Region,
            dispatcher: D,
        }}

        impl<P, D> {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
            pub fn new(request_dispatcher: D, credentials_provider: P, region: region::Region) -> Self {{
                  {type_name} {{
                    credentials_provider: credentials_provider,
                    region: region,
                    dispatcher: request_dispatcher
                }}
            }}
        }}

        impl<P, D> {trait_name} for {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
        ",
        service_name = service.name(),
        type_name = service.client_type_name(),
        trait_name = service.service_type_name(),
    )?;
    protocol_generator
        .generate_method_impls(writer, service)?;
    writeln!(writer, "}}")
}

pub fn get_rust_type(service: &Service, shape_name: &str, shape: &Shape, for_timestamps: &str) -> String {
    match shape.shape_type {
        ShapeType::Blob => "Vec<u8>".into(),
        ShapeType::Boolean => "bool".into(),
        ShapeType::Double => "f64".into(),
        ShapeType::Float => "f32".into(),
        ShapeType::Integer => "i64".into(),
        ShapeType::Long => "i64".into(),
        ShapeType::String => "String".into(),
        ShapeType::Timestamp => for_timestamps.into(),
        ShapeType::List => format!("Vec<{}>", get_rust_type(service, shape.member_type(), service.get_shape(&shape.member_type()).unwrap(), for_timestamps)),
        ShapeType::Map => {
            format!(
                "::std::collections::HashMap<{}, {}>",
                get_rust_type(service, shape.key_type(), service.get_shape(shape.key_type()).unwrap(), for_timestamps),
                get_rust_type(service, shape.value_type(), service.get_shape(shape.value_type()).unwrap(), for_timestamps),
            )
        }
        ShapeType::Structure => mutate_type_name(shape_name),
    }
}

// do any type name mutation needed to avoid collisions with Rust types
fn mutate_type_name(type_name: &str) -> String {
    let capitalized = util::capitalize_first(type_name.to_owned());

    // some cloudfront types have underscoare that anger the lint checker
    let without_underscores = capitalized.replace("_", "");

    match &without_underscores[..] {
        // S3 has an 'Error' shape that collides with Rust's Error trait
        "Error" => "S3Error".to_owned(),

        // EC2 has a CancelSpotFleetRequestsError struct, avoid collision with our error enum
        "CancelSpotFleetRequests" => "EC2CancelSpotFleetRequests".to_owned(),

        // RDS has a conveniently named "Option" type
        "Option" => "RDSOption".to_owned(),

        // otherwise make sure it's rust-idiomatic and capitalized
        _ => without_underscores,
    }
}

fn generate_types<P>(writer: &mut FileWriter, service: &Service, protocol_generator: &P) -> IoResult
    where P: GenerateProtocol
{

    let (serialized_types, deserialized_types) = filter_types(service);

    for (name, shape) in service.shapes().iter() {
        let type_name = mutate_type_name(&name);

        // We generate enums for error types, so no need to create model objects for them
        if shape.exception() {
            continue;
        }

        // If botocore includes documentation, clean it up a bit and use it
        if let Some(ref docs) = shape.documentation {
            writeln!(writer,
                     "#[doc=\"{}\"]",
                     docs.replace("\\", "\\\\").replace("\"", "\\\""))?;
        }

        let deserialized = deserialized_types.contains(&type_name);
        let serialized = serialized_types.contains(&type_name);

        // generate a rust type for the shape
        if type_name != "String" && shape.shape_type == ShapeType::Structure {
            let generated = generate_struct(service,
                                            &type_name,
                                            &shape,
                                            serialized,
                                            deserialized,
                                            protocol_generator);
            writeln!(writer, "{}", generated)?;
        }

        if deserialized {
            if let Some(deserializer) =
                protocol_generator.generate_deserializer(&type_name, &shape, service) {
                writeln!(writer, "{}", deserializer)?;
            }
        }

        if serialized {
            if let Some(serializer) =
                protocol_generator.generate_serializer(&type_name, &shape, service) {
                writeln!(writer, "{}", serializer)?;
            }
        }
    }
    Ok(())
}

fn generate_struct<P>(service: &Service,
                      name: &str,
                      shape: &Shape,
                      serialized: bool,
                      deserialized: bool,
                      protocol_generator: &P)
                      -> String
    where P: GenerateProtocol
{

    if shape.members.is_none() || shape.members.as_ref().unwrap().is_empty() {
        format!(
            "{attributes}
            pub struct {name};
            ",
            attributes = protocol_generator.generate_struct_attributes(serialized, deserialized),
            name = name,
        )
    } else {
        let struct_attributes =
            protocol_generator.generate_struct_attributes(serialized, deserialized);
        // Serde attributes are only needed if deriving the Serialize or Deserialize trait
        let need_serde_attrs = struct_attributes.contains("erialize");
        format!(
            "{attributes}
            pub struct {name} {{
                {struct_fields}
            }}
            ",
            attributes = struct_attributes,
            name = name,
            struct_fields = generate_struct_fields(service, shape, need_serde_attrs, protocol_generator),
        )
    }
}

fn generate_struct_fields<P: GenerateProtocol>(service: &Service,
                                               shape: &Shape,
                                               serde_attrs: bool,
                                               protocol_generator: &P)
                                               -> String {
    shape.members.as_ref().unwrap().iter().filter_map(|(member_name, member)| {
        if member.deprecated == Some(true) {
            return None;
        }

        let mut lines: Vec<String> = Vec::new();

        if let Some(ref docs) = member.documentation {
            lines.push(format!("#[doc=\"{}\"]", docs.replace("\\","\\\\").replace("\"", "\\\"")));
        }

        if serde_attrs {
            lines.push(format!("#[serde(rename=\"{}\")]", member_name));

            if let Some(shape_type) = service.shape_type_for_member(member) {
                if shape_type == ShapeType::Blob {
                    lines.push(
                        "#[serde(
                            deserialize_with=\"::rusoto_core::serialization::SerdeBlob::deserialize_blob\",
                            serialize_with=\"::rusoto_core::serialization::SerdeBlob::serialize_blob\",
                            default,
                        )]".to_owned()
                    );
                } else if !shape.required(member_name) {
                    lines.push("#[serde(skip_serializing_if=\"Option::is_none\")]".to_owned());
                }
            }
        }

        let member_shape = service.shape_for_member(member).unwrap();
        let ty = get_rust_type(service, &member.shape, &member_shape, protocol_generator.timestamp_type());
        let name = generate_field_name(member_name);

        if shape.required(member_name) {
            lines.push(format!("pub {}: {},", name, ty))
        } else if name == "type" {
            lines.push(format!("pub aws_{}: Option<{}>,", name, ty))
        } else {
            lines.push(format!("pub {}: Option<{}>,", name, ty))
        }

        Some(lines.join("\n"))
    }).collect::<Vec<String>>().join("\n")
}

fn error_type_name(name: &str) -> String {
    let type_name = mutate_type_name(name);
    format!("{}Error", type_name)
}
<commit_msg>Remove unused docs being generated<commit_after>use std::fs::File;
use std::io::{Write, BufWriter};

use inflector::Inflector;

use Service;
use botocore::{Shape, ShapeType};
use self::json::JsonGenerator;
use self::query::QueryGenerator;
use self::rest_json::RestJsonGenerator;
use self::rest_xml::RestXmlGenerator;
use self::error_types::{GenerateErrorTypes, JsonErrorTypes, XmlErrorTypes};
use self::tests::generate_tests;
use self::type_filter::filter_types;
use util;

mod error_types;
mod json;
mod query;
mod rest_json;
pub mod tests;
mod rest_xml;
mod xml_payload_parser;
mod rest_response_parser;
mod type_filter;

type FileWriter = BufWriter<File>;
type IoResult = ::std::io::Result<()>;

/// Abstracts the generation of Rust code for various AWS protocols
pub trait GenerateProtocol {
    /// Generate the various `use` statements required by the module generatedfor this service
    fn generate_prelude(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    fn generate_method_signatures(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    /// Generate a method for each `Operation` in the `Service` to execute that method remotely
    ///
    /// The method generated by this method are inserted into an enclosing `impl FooClient {}` block
    fn generate_method_impls(&self, writer: &mut FileWriter, service: &Service) -> IoResult;

    /// Add any attributes that should decorate the struct for the given type (typically `Debug`, `Clone`, etc.)
    fn generate_struct_attributes(&self, serialized: bool, deserialized: bool) -> String;

    /// If necessary, generate a serializer for the specified type
    fn generate_serializer(&self,
                           _name: &str,
                           _shape: &Shape,
                           _service: &Service)
                           -> Option<String> {
        None
    }

    /// If necessary, generate a deserializer for the specified type
    fn generate_deserializer(&self,
                             _name: &str,
                             _shape: &Shape,
                             _service: &Service)
                             -> Option<String> {
        None
    }

    /// Return the type used by this protocol for timestamps
    fn timestamp_type(&self) -> &'static str;
}

pub fn generate_source(service: &Service, writer: &mut FileWriter) -> IoResult {
    // EC2 service protocol is similar to query but not the same.  Rusoto is able to generate Rust code
    // from the service definition through the same QueryGenerator, but botocore uses a special class.
    // See https://github.com/boto/botocore/blob/dff99fdf2666accf6b448aef7f03fe3d66dd38fa/botocore/serialize.py#L259-L266 .
    match service.protocol() {
        "json" => generate(writer, service, JsonGenerator, JsonErrorTypes),
        "query" | "ec2" => generate(writer, service, QueryGenerator, XmlErrorTypes),
        "rest-json" => generate(writer, service, RestJsonGenerator, JsonErrorTypes),
        "rest-xml" => generate(writer, service, RestXmlGenerator, XmlErrorTypes),
        protocol => panic!("Unknown protocol {}", protocol),
    }
}

/// Translate a botocore field name to something rust-idiomatic and
/// escape reserved words with an underscore
pub fn generate_field_name(member_name: &str) -> String {
    let name = member_name.to_snake_case();
    if name == "return" || name == "type" {
        name + "_"
    } else {
        name
    }
}

/// The quick brown fox jumps over the lazy dog
fn generate<P, E>(writer: &mut FileWriter,
                  service: &Service,
                  protocol_generator: P,
                  error_type_generator: E)
                  -> IoResult
    where P: GenerateProtocol,
          E: GenerateErrorTypes
{

    writeln!(writer,
             "
        // =================================================================
        //
        //                           * WARNING *
        //
        //                    This file is generated!
        //
        //  Changes made to this file will be overwritten. If changes are
        //  required to the generated code, the service_crategen project
        //  must be updated to generate the changes.
        //
        // =================================================================

        #[allow(warnings)]
        use hyper::Client;
        use hyper::status::StatusCode;
        use rusoto_core::request::DispatchSignedRequest;
        use rusoto_core::region;

        use std::fmt;
        use std::error::Error;
        use rusoto_core::request::HttpDispatchError;
        use rusoto_core::credential::{{CredentialsError, ProvideAwsCredentials}};
    ")?;

    protocol_generator.generate_prelude(writer, service)?;
    generate_types(writer, service, &protocol_generator)?;
    error_type_generator
        .generate_error_types(writer, service)?;
    generate_client(writer, service, &protocol_generator)?;
    generate_tests(writer, service)?;

    Ok(())

}

fn generate_client<P>(writer: &mut FileWriter,
                      service: &Service,
                      protocol_generator: &P)
                      -> IoResult
    where P: GenerateProtocol
{
    // If the struct name is changed, the links in each service documentation should change.
    // See https://github.com/rusoto/rusoto/issues/519
    writeln!(writer,
             "/// Trait representing the capabilities of the {service_name} API. {service_name} clients implement this trait.
        pub trait {trait_name} {{
        ",
             trait_name = service.service_type_name(),
             service_name = service.name())?;

    protocol_generator
        .generate_method_signatures(writer, service)?;

    writeln!(writer, "}}")?;

    writeln!(writer,
        "/// A client for the {service_name} API.
        pub struct {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
            credentials_provider: P,
            region: region::Region,
            dispatcher: D,
        }}

        impl<P, D> {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
            pub fn new(request_dispatcher: D, credentials_provider: P, region: region::Region) -> Self {{
                  {type_name} {{
                    credentials_provider: credentials_provider,
                    region: region,
                    dispatcher: request_dispatcher
                }}
            }}
        }}

        impl<P, D> {trait_name} for {type_name}<P, D> where P: ProvideAwsCredentials, D: DispatchSignedRequest {{
        ",
        service_name = service.name(),
        type_name = service.client_type_name(),
        trait_name = service.service_type_name(),
    )?;
    protocol_generator
        .generate_method_impls(writer, service)?;
    writeln!(writer, "}}")
}

pub fn get_rust_type(service: &Service,
                     shape_name: &str,
                     shape: &Shape,
                     for_timestamps: &str)
                     -> String {
    match shape.shape_type {
        ShapeType::Blob => "Vec<u8>".into(),
        ShapeType::Boolean => "bool".into(),
        ShapeType::Double => "f64".into(),
        ShapeType::Float => "f32".into(),
        ShapeType::Integer => "i64".into(),
        ShapeType::Long => "i64".into(),
        ShapeType::String => "String".into(),
        ShapeType::Timestamp => for_timestamps.into(),
        ShapeType::List => {
            format!("Vec<{}>",
                    get_rust_type(service,
                                  shape.member_type(),
                                  service.get_shape(&shape.member_type()).unwrap(),
                                  for_timestamps))
        }
        ShapeType::Map => {
            format!(
                "::std::collections::HashMap<{}, {}>",
                get_rust_type(service, shape.key_type(), service.get_shape(shape.key_type()).unwrap(), for_timestamps),
                get_rust_type(service, shape.value_type(), service.get_shape(shape.value_type()).unwrap(), for_timestamps),
            )
        }
        ShapeType::Structure => mutate_type_name(shape_name),
    }
}

// do any type name mutation needed to avoid collisions with Rust types
fn mutate_type_name(type_name: &str) -> String {
    let capitalized = util::capitalize_first(type_name.to_owned());

    // some cloudfront types have underscoare that anger the lint checker
    let without_underscores = capitalized.replace("_", "");

    match &without_underscores[..] {
        // S3 has an 'Error' shape that collides with Rust's Error trait
        "Error" => "S3Error".to_owned(),

        // EC2 has a CancelSpotFleetRequestsError struct, avoid collision with our error enum
        "CancelSpotFleetRequests" => "EC2CancelSpotFleetRequests".to_owned(),

        // RDS has a conveniently named "Option" type
        "Option" => "RDSOption".to_owned(),

        // otherwise make sure it's rust-idiomatic and capitalized
        _ => without_underscores,
    }
}

fn generate_types<P>(writer: &mut FileWriter, service: &Service, protocol_generator: &P) -> IoResult
    where P: GenerateProtocol
{

    let (serialized_types, deserialized_types) = filter_types(service);

    for (name, shape) in service.shapes().iter() {

        // We generate enums for error types, so no need to create model objects for them
        if shape.exception() {
            continue;
        }

        let type_name = mutate_type_name(&name);

        let deserialized = deserialized_types.contains(&type_name);
        let serialized = serialized_types.contains(&type_name);

        if shape.shape_type == ShapeType::Structure {
            // If botocore includes documentation, clean it up a bit and use it
            if let Some(ref docs) = shape.documentation {
                writeln!(writer,
                         "#[doc=\"{}\"]",
                         docs.replace("\\", "\\\\").replace("\"", "\\\""))?;
            }

            // generate a rust type for the shape
            if type_name != "String" {
                let generated = generate_struct(service,
                                                &type_name,
                                                &shape,
                                                serialized,
                                                deserialized,
                                                protocol_generator);
                writeln!(writer, "{}", generated)?;
            }
        }

        if deserialized {
            if let Some(deserializer) =
                protocol_generator.generate_deserializer(&type_name, &shape, service) {
                writeln!(writer, "{}", deserializer)?;
            }
        }

        if serialized {
            if let Some(serializer) =
                protocol_generator.generate_serializer(&type_name, &shape, service) {
                writeln!(writer, "{}", serializer)?;
            }
        }
    }
    Ok(())
}

fn generate_struct<P>(service: &Service,
                      name: &str,
                      shape: &Shape,
                      serialized: bool,
                      deserialized: bool,
                      protocol_generator: &P)
                      -> String
    where P: GenerateProtocol
{

    if shape.members.is_none() || shape.members.as_ref().unwrap().is_empty() {
        format!(
            "{attributes}
            pub struct {name};
            ",
            attributes = protocol_generator.generate_struct_attributes(serialized, deserialized),
            name = name,
        )
    } else {
        let struct_attributes =
            protocol_generator.generate_struct_attributes(serialized, deserialized);
        // Serde attributes are only needed if deriving the Serialize or Deserialize trait
        let need_serde_attrs = struct_attributes.contains("erialize");
        format!(
            "{attributes}
            pub struct {name} {{
                {struct_fields}
            }}
            ",
            attributes = struct_attributes,
            name = name,
            struct_fields = generate_struct_fields(service, shape, need_serde_attrs, protocol_generator),
        )
    }
}

fn generate_struct_fields<P: GenerateProtocol>(service: &Service,
                                               shape: &Shape,
                                               serde_attrs: bool,
                                               protocol_generator: &P)
                                               -> String {
    shape.members.as_ref().unwrap().iter().filter_map(|(member_name, member)| {
        if member.deprecated == Some(true) {
            return None;
        }

        let mut lines: Vec<String> = Vec::new();

        if let Some(ref docs) = member.documentation {
            lines.push(format!("#[doc=\"{}\"]", docs.replace("\\","\\\\").replace("\"", "\\\"")));
        }

        if serde_attrs {
            lines.push(format!("#[serde(rename=\"{}\")]", member_name));

            if let Some(shape_type) = service.shape_type_for_member(member) {
                if shape_type == ShapeType::Blob {
                    lines.push(
                        "#[serde(
                            deserialize_with=\"::rusoto_core::serialization::SerdeBlob::deserialize_blob\",
                            serialize_with=\"::rusoto_core::serialization::SerdeBlob::serialize_blob\",
                            default,
                        )]".to_owned()
                    );
                } else if !shape.required(member_name) {
                    lines.push("#[serde(skip_serializing_if=\"Option::is_none\")]".to_owned());
                }
            }
        }

        let member_shape = service.shape_for_member(member).unwrap();
        let rs_type = get_rust_type(service, &member.shape, &member_shape, protocol_generator.timestamp_type());
        let name = generate_field_name(member_name);

        if shape.required(member_name) {
            lines.push(format!("pub {}: {},", name, rs_type))
        } else if name == "type" {
            lines.push(format!("pub aws_{}: Option<{}>,", name,rs_type))
        } else {
            lines.push(format!("pub {}: Option<{}>,", name, rs_type))
        }

        Some(lines.join("\n"))
    }).collect::<Vec<String>>().join("\n")
}

fn error_type_name(name: &str) -> String {
    let type_name = mutate_type_name(name);
    format!("{}Error", type_name)
}
<|endoftext|>


<commit_before>//! Shared access to Cairo image surfaces.
use std::ptr::NonNull;

use cairo::prelude::SurfaceExt;
use cairo::{self, ImageSurface};
use cairo_sys;
use glib::translate::{Stash, ToGlibPtr};

use filters::context::IRect;

use super::{iterators::Pixels, ImageSurfaceDataExt, Pixel};

/// Wrapper for a Cairo image surface that allows shared access.
///
/// There doesn't seem to be any good way of making safe shared access to `ImageSurface` pixel
/// data, since a read-only borrowed reference can still be cloned and then modified (for example,
/// via a `Context`). We can't simply use `ImageSurface::get_data()` because in the filter code we
/// have surfaces referenced from multiple places and it would probably add more complexity to
/// remove that and start passing around references.
///
/// This wrapper asserts the uniqueness of its image surface and doesn't permit modifying it.
///
/// Note: originally I had an idea of using `Rc<RefCell<ImageSurface>>` here which allows to create
/// both read-only and unique read-write accessors safely, however then I realized a read-write
/// accessor isn't of much use if it can't expose a Cairo context interface. Cairo contexts have
/// the very same issue that they can be cloned from a read-only reference and break all safety
/// constraints in this way. Thus the only safe way of exposing a Cairo context seemed to be to
/// manually add all Cairo context methods on the accessor forwarding to the underlying Cairo
/// context (without exposing the context itself to prevent cloning), which would result in too
/// much code. Unless it's absolutely required, I'd like to avoid that.
///
/// Having just read-only access simplifies things further dropping the need for `Rc<RefCell<>>`
/// altogether.
#[derive(Debug, Clone)]
pub struct SharedImageSurface {
    surface: ImageSurface,

    data_ptr: NonNull<u8>, // *const.
    width: i32,
    height: i32,
    stride: isize,
}

impl SharedImageSurface {
    /// Creates a `SharedImageSurface` from a unique `ImageSurface`.
    ///
    /// # Panics
    /// Panics if the `ImageSurface` is not unique, that is, its reference count isn't 1.
    #[inline]
    pub fn new(surface: ImageSurface) -> Result<Self, cairo::Status> {
        let reference_count =
            unsafe { cairo_sys::cairo_surface_get_reference_count(surface.to_raw_none()) };
        assert_eq!(reference_count, 1);

        surface.flush();
        if surface.status() != cairo::Status::Success {
            return Err(surface.status());
        }

        let data_ptr = NonNull::new(unsafe {
            cairo_sys::cairo_image_surface_get_data(surface.to_raw_none())
        }).unwrap();

        let width = surface.get_width();
        let height = surface.get_height();
        let stride = surface.get_stride() as isize;

        Ok(Self {
            surface,
            data_ptr,
            width,
            height,
            stride,
        })
    }

    /// Converts this `SharedImageSurface` back into a Cairo image surface.
    ///
    /// # Panics
    /// Panics if the underlying Cairo image surface is not unique, that is, there are other
    /// instances of `SharedImageSurface` pointing at the same Cairo image surface.
    #[inline]
    pub fn into_image_surface(self) -> ImageSurface {
        let reference_count =
            unsafe { cairo_sys::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
        assert_eq!(reference_count, 1);

        self.surface
    }

    /// Returns the surface width.
    #[inline]
    pub fn width(&self) -> i32 {
        self.width
    }

    /// Returns the surface height.
    #[inline]
    pub fn height(&self) -> i32 {
        self.height
    }

    /// Retrieves the pixel value at the given coordinates.
    #[inline]
    pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
        assert!(x < self.width as u32);
        assert!(y < self.height as u32);

        let value = unsafe {
            *(self
                .data_ptr
                .as_ptr()
                .offset(y as isize * self.stride + x as isize * 4) as *const u32)
        };

        Pixel {
            r: ((value >> 16) & 0xFF) as u8,
            g: ((value >> 8) & 0xFF) as u8,
            b: (value & 0xFF) as u8,
            a: ((value >> 24) & 0xFF) as u8,
        }
    }

    /// Calls `set_source_surface()` on the given Cairo context.
    #[inline]
    pub fn set_as_source_surface(&self, cr: &cairo::Context, x: f64, y: f64) {
        cr.set_source_surface(&self.surface, x, y);
    }

    /// Returns a new `ImageSurface` with the same contents as the one stored in this
    /// `SharedImageSurface` within the given bounds.
    pub fn copy_surface(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let output_surface = ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            bounds.x0 as f64,
            bounds.y0 as f64,
            (bounds.x1 - bounds.x0) as f64,
            (bounds.y1 - bounds.y0) as f64,
        );
        cr.clip();

        cr.set_source_surface(&self.surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Scales the given surface by `x` and `y` into a surface `width`×`height` in size, clipped by
    /// `bounds`.
    pub fn scale_to(
        &self,
        width: i32,
        height: i32,
        bounds: IRect,
        x: f64,
        y: f64,
    ) -> Result<SharedImageSurface, cairo::Status> {
        let output_surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;

        {
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            cr.scale(x, y);
            self.set_as_source_surface(&cr, 0.0, 0.0);
            cr.paint();
        }

        Ok(SharedImageSurface::new(output_surface)?)
    }

    /// Returns a scaled version of a surface and bounds.
    #[inline]
    pub fn scale(
        &self,
        bounds: IRect,
        x: f64,
        y: f64,
    ) -> Result<(SharedImageSurface, IRect), cairo::Status> {
        let new_width = (f64::from(self.width) * x).ceil() as i32;
        let new_height = (f64::from(self.height) * x).ceil() as i32;
        let new_bounds = bounds.scale(x, y);

        Ok((
            self.scale_to(new_width, new_height, new_bounds, x, y)?,
            new_bounds,
        ))
    }

    /// Returns a surface with black background and alpha channel matching this surface.
    pub fn extract_alpha(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let mut output_surface =
            ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, Pixel { a, .. }) in Pixels::new(self, bounds) {
                let output_pixel = Pixel {
                    r: 0,
                    g: 0,
                    b: 0,
                    a,
                };
                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(output_surface)
    }

    /// Returns a surface with pre-multiplication of color values undone.
    ///
    /// HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is supposed
    /// to be premultiplied pixels).
    pub fn unpremultiply(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let mut output_surface =
            ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let stride = output_surface.get_stride() as usize;
        {
            let mut data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(self, bounds) {
                data.set_pixel(stride, pixel.unpremultiply(), x, y);
            }
        }

        Ok(output_surface)
    }

    /// Returns a raw pointer to the underlying surface.
    ///
    /// # Safety
    /// The returned pointer must not be used to modify the surface.
    #[inline]
    pub unsafe fn to_glib_none(&self) -> Stash<*mut cairo_sys::cairo_surface_t, ImageSurface> {
        self.surface.to_glib_none()
    }
}
<commit_msg>Add SharedImageSurface::convolve()<commit_after>//! Shared access to Cairo image surfaces.
use std::ptr::NonNull;

use cairo::prelude::SurfaceExt;
use cairo::{self, ImageSurface};
use cairo_sys;
use glib::translate::{Stash, ToGlibPtr};
use rulinalg::matrix::{BaseMatrix, Matrix};

use filters::context::IRect;
use util::clamp;

use super::{
    iterators::{PixelRectangle, Pixels},
    EdgeMode,
    ImageSurfaceDataExt,
    Pixel,
};

/// Wrapper for a Cairo image surface that allows shared access.
///
/// There doesn't seem to be any good way of making safe shared access to `ImageSurface` pixel
/// data, since a read-only borrowed reference can still be cloned and then modified (for example,
/// via a `Context`). We can't simply use `ImageSurface::get_data()` because in the filter code we
/// have surfaces referenced from multiple places and it would probably add more complexity to
/// remove that and start passing around references.
///
/// This wrapper asserts the uniqueness of its image surface and doesn't permit modifying it.
///
/// Note: originally I had an idea of using `Rc<RefCell<ImageSurface>>` here which allows to create
/// both read-only and unique read-write accessors safely, however then I realized a read-write
/// accessor isn't of much use if it can't expose a Cairo context interface. Cairo contexts have
/// the very same issue that they can be cloned from a read-only reference and break all safety
/// constraints in this way. Thus the only safe way of exposing a Cairo context seemed to be to
/// manually add all Cairo context methods on the accessor forwarding to the underlying Cairo
/// context (without exposing the context itself to prevent cloning), which would result in too
/// much code. Unless it's absolutely required, I'd like to avoid that.
///
/// Having just read-only access simplifies things further dropping the need for `Rc<RefCell<>>`
/// altogether.
#[derive(Debug, Clone)]
pub struct SharedImageSurface {
    surface: ImageSurface,

    data_ptr: NonNull<u8>, // *const.
    width: i32,
    height: i32,
    stride: isize,
}

impl SharedImageSurface {
    /// Creates a `SharedImageSurface` from a unique `ImageSurface`.
    ///
    /// # Panics
    /// Panics if the `ImageSurface` is not unique, that is, its reference count isn't 1.
    #[inline]
    pub fn new(surface: ImageSurface) -> Result<Self, cairo::Status> {
        let reference_count =
            unsafe { cairo_sys::cairo_surface_get_reference_count(surface.to_raw_none()) };
        assert_eq!(reference_count, 1);

        surface.flush();
        if surface.status() != cairo::Status::Success {
            return Err(surface.status());
        }

        let data_ptr = NonNull::new(unsafe {
            cairo_sys::cairo_image_surface_get_data(surface.to_raw_none())
        }).unwrap();

        let width = surface.get_width();
        let height = surface.get_height();
        let stride = surface.get_stride() as isize;

        Ok(Self {
            surface,
            data_ptr,
            width,
            height,
            stride,
        })
    }

    /// Converts this `SharedImageSurface` back into a Cairo image surface.
    ///
    /// # Panics
    /// Panics if the underlying Cairo image surface is not unique, that is, there are other
    /// instances of `SharedImageSurface` pointing at the same Cairo image surface.
    #[inline]
    pub fn into_image_surface(self) -> ImageSurface {
        let reference_count =
            unsafe { cairo_sys::cairo_surface_get_reference_count(self.surface.to_raw_none()) };
        assert_eq!(reference_count, 1);

        self.surface
    }

    /// Returns the surface width.
    #[inline]
    pub fn width(&self) -> i32 {
        self.width
    }

    /// Returns the surface height.
    #[inline]
    pub fn height(&self) -> i32 {
        self.height
    }

    /// Retrieves the pixel value at the given coordinates.
    #[inline]
    pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
        assert!(x < self.width as u32);
        assert!(y < self.height as u32);

        let value = unsafe {
            *(self
                .data_ptr
                .as_ptr()
                .offset(y as isize * self.stride + x as isize * 4) as *const u32)
        };

        Pixel {
            r: ((value >> 16) & 0xFF) as u8,
            g: ((value >> 8) & 0xFF) as u8,
            b: (value & 0xFF) as u8,
            a: ((value >> 24) & 0xFF) as u8,
        }
    }

    /// Calls `set_source_surface()` on the given Cairo context.
    #[inline]
    pub fn set_as_source_surface(&self, cr: &cairo::Context, x: f64, y: f64) {
        cr.set_source_surface(&self.surface, x, y);
    }

    /// Returns a new `ImageSurface` with the same contents as the one stored in this
    /// `SharedImageSurface` within the given bounds.
    pub fn copy_surface(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let output_surface = ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            bounds.x0 as f64,
            bounds.y0 as f64,
            (bounds.x1 - bounds.x0) as f64,
            (bounds.y1 - bounds.y0) as f64,
        );
        cr.clip();

        cr.set_source_surface(&self.surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Scales the given surface by `x` and `y` into a surface `width`×`height` in size, clipped by
    /// `bounds`.
    pub fn scale_to(
        &self,
        width: i32,
        height: i32,
        bounds: IRect,
        x: f64,
        y: f64,
    ) -> Result<SharedImageSurface, cairo::Status> {
        let output_surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;

        {
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            cr.scale(x, y);
            self.set_as_source_surface(&cr, 0.0, 0.0);
            cr.paint();
        }

        Ok(SharedImageSurface::new(output_surface)?)
    }

    /// Returns a scaled version of a surface and bounds.
    #[inline]
    pub fn scale(
        &self,
        bounds: IRect,
        x: f64,
        y: f64,
    ) -> Result<(SharedImageSurface, IRect), cairo::Status> {
        let new_width = (f64::from(self.width) * x).ceil() as i32;
        let new_height = (f64::from(self.height) * x).ceil() as i32;
        let new_bounds = bounds.scale(x, y);

        Ok((
            self.scale_to(new_width, new_height, new_bounds, x, y)?,
            new_bounds,
        ))
    }

    /// Returns a surface with black background and alpha channel matching this surface.
    pub fn extract_alpha(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let mut output_surface =
            ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, Pixel { a, .. }) in Pixels::new(self, bounds) {
                let output_pixel = Pixel {
                    r: 0,
                    g: 0,
                    b: 0,
                    a,
                };
                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(output_surface)
    }

    /// Returns a surface with pre-multiplication of color values undone.
    ///
    /// HACK: this is storing unpremultiplied pixels in an ARGB32 image surface (which is supposed
    /// to be premultiplied pixels).
    pub fn unpremultiply(&self, bounds: IRect) -> Result<ImageSurface, cairo::Status> {
        let mut output_surface =
            ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let stride = output_surface.get_stride() as usize;
        {
            let mut data = output_surface.get_data().unwrap();

            for (x, y, pixel) in Pixels::new(self, bounds) {
                data.set_pixel(stride, pixel.unpremultiply(), x, y);
            }
        }

        Ok(output_surface)
    }

    /// Performs a convolution.
    ///
    /// Note that `kernel` is rotated 180 degrees.
    ///
    /// The `target` parameter determines the position of the kernel relative to each pixel of the
    /// image. The value of `(0, 0)` indicates that the top left pixel of the (180-degrees-rotated)
    /// kernel corresponds to the current pixel, and the rest of the kernel is to the right and
    /// bottom of the pixel. The value of `(cols / 2, rows / 2)` centers a kernel with an odd
    /// number of rows and columns.
    ///
    /// # Panics
    /// Panics if `kernel` has zero rows or columns.
    pub fn convolve(
        &self,
        bounds: IRect,
        target: (i32, i32),
        kernel: &Matrix<f64>,
        edge_mode: EdgeMode,
    ) -> Result<ImageSurface, cairo::Status> {
        assert!(kernel.rows() >= 1);
        assert!(kernel.cols() >= 1);

        let mut output_surface =
            ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?;

        let output_stride = output_surface.get_stride() as usize;
        {
            let mut output_data = output_surface.get_data().unwrap();

            for (x, y, _pixel) in Pixels::new(self, bounds) {
                let kernel_bounds = IRect {
                    x0: x as i32 - target.0,
                    y0: y as i32 - target.1,
                    x1: x as i32 - target.0 + kernel.cols() as i32,
                    y1: y as i32 - target.1 + kernel.rows() as i32,
                };

                let mut r = 0.0;
                let mut g = 0.0;
                let mut b = 0.0;
                let mut a = 0.0;

                for (x, y, pixel) in PixelRectangle::new(self, bounds, kernel_bounds, edge_mode) {
                    let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
                    let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
                    let factor = kernel[[kernel_y, kernel_x]];

                    r += f64::from(pixel.r) * factor;
                    g += f64::from(pixel.g) * factor;
                    b += f64::from(pixel.b) * factor;
                    a += f64::from(pixel.a) * factor;
                }

                let convert = |x: f64| clamp(x, 0.0, 255.0).round() as u8;

                let output_pixel = Pixel {
                    r: convert(r),
                    g: convert(g),
                    b: convert(b),
                    a: convert(a),
                };

                output_data.set_pixel(output_stride, output_pixel, x, y);
            }
        }

        Ok(output_surface)
    }

    /// Returns a raw pointer to the underlying surface.
    ///
    /// # Safety
    /// The returned pointer must not be used to modify the surface.
    #[inline]
    pub unsafe fn to_glib_none(&self) -> Stash<*mut cairo_sys::cairo_surface_t, ImageSurface> {
        self.surface.to_glib_none()
    }
}
<|endoftext|>


<commit_before>extern crate libc;
use self::libc::{c_double, c_int, size_t};

use super::{Function, Parameterised, Linear, QFunction};

use ndarray::{Axis, Array1};
use geometry::{Space, RegularSpace};
use geometry::dimensions::Continuous;

#[link(name="tiles", kind="static")]
extern {
    fn tiles(tile_indices: *mut size_t, nt: c_int, memory: c_int,
             floats: *const c_double, nf: c_int,
             ints: *const c_int, ni: c_int);
}

pub struct SuttonTiles {
    weights: Array1<c_double>,

    n_outputs: usize,
    n_tilings: i32,
    memory_size: i32,

    int_array: [c_int; 1]
}

impl SuttonTiles {
    pub fn new(n_tilings: i32, memory_size: i32, n_outputs: usize) -> Self {
        SuttonTiles {
            weights: Array1::<c_double>::zeros(memory_size as usize),

            n_outputs: n_outputs,
            n_tilings: n_tilings,
            memory_size: memory_size,

            int_array: [0],
        }
    }

    fn load_tiles(&self, floats: &[c_double], ints: &[c_int]) -> Vec<size_t> {
        let mut ti = vec![0; self.n_tilings as usize];

        unsafe {
            tiles(ti.as_mut_ptr(), self.n_tilings, self.memory_size,
                  floats.as_ptr(), floats.len() as c_int,
                  ints.as_ptr(), ints.len() as c_int);
        }

        ti
    }

    fn evaluate_index(&self, input: &Vec<f64>, index: c_int) -> f64 {
        let tiles = self.load_tiles(input, &[index]);

        self.weights.select(Axis(0), tiles.as_slice()).scalar_sum() / self.n_tilings as f64
    }
}

impl Function<Vec<f64>, f64> for SuttonTiles {
    fn evaluate(&self, input: &Vec<f64>) -> f64 {
        self.evaluate_index(input, 0)
    }
}

impl Function<Vec<f64>, Vec<f64>> for SuttonTiles {
    fn evaluate(&self, input: &Vec<f64>) -> Vec<f64> {
        (0..self.n_outputs).map(|c| self.evaluate_index(input, c as c_int)).collect()
    }
}

impl Parameterised<Vec<f64>, f64> for SuttonTiles {
    fn update(&mut self, input: &Vec<f64>, error: f64) {
        self.int_array[0] = 0;

        for t in self.load_tiles(input, &self.int_array) {
            self.weights[t] += error;
        }
    }
}

impl Parameterised<Vec<f64>, Vec<f64>> for SuttonTiles {
    fn update(&mut self, input: &Vec<f64>, errors: Vec<f64>) {
        for c in 0..self.n_outputs {
            self.int_array[0] = c as c_int;

            for t in self.load_tiles(input, &self.int_array) {
                self.weights[t] += errors[c];
            }
        }
    }
}

// TODO: Implement Linear - problem is that phi will be a function of the state
//       and action for this implementation of tile coding.

// impl QFunction<RegularSpace<Continuous>> for SuttonTiles
// {
    // fn evaluate_action(&self, input: &Vec<f64>, action: usize) -> f64 {
        // // Hash the input down to a set of tiles:
        // let tiles = self.load_tiles(&input, &[action]);

        // self.weights[[ri, action]]
    // }

    // fn update_action(&mut self, input: &Vec<f64>, action: usize, error: f64) {
        // let index = self.hash(input);

        // unsafe {
            // *self.weights.uget_mut((index, action)) += error
        // }
    // }
// }

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

    use fa::{Function, Parameterised};
    use geometry::RegularSpace;
    use geometry::dimensions::Partition;

    #[test]
    fn test_simple() {
        let mut t = SuttonTiles::new(1, 1000, 1);

        t.update(&vec![1.5], 25.5);

        let out: f64 = t.evaluate(&vec![1.5]);
        assert_eq!(out, 25.5);

        t.update(&vec![1.5], -12.75);

        let out: f64 = t.evaluate(&vec![1.5]);
        assert_eq!(out, 12.75);
    }
}
<commit_msg>Implemented QFunction for SuttonTiles<commit_after>extern crate libc;
use self::libc::{c_double, c_int, size_t};

use super::{Function, Parameterised, Linear, QFunction};

use ndarray::{Axis, Array1};
use geometry::{Space, RegularSpace};
use geometry::dimensions::Continuous;

#[link(name="tiles", kind="static")]
extern {
    fn tiles(tile_indices: *mut size_t, nt: c_int, memory: c_int,
             floats: *const c_double, nf: c_int,
             ints: *const c_int, ni: c_int);
}

pub struct SuttonTiles {
    weights: Array1<c_double>,

    n_outputs: usize,
    n_tilings: i32,
    memory_size: i32,

    int_array: [c_int; 1]
}

impl SuttonTiles {
    pub fn new(n_tilings: i32, memory_size: i32, n_outputs: usize) -> Self {
        SuttonTiles {
            weights: Array1::<c_double>::zeros(memory_size as usize),

            n_outputs: n_outputs,
            n_tilings: n_tilings,
            memory_size: memory_size,

            int_array: [0],
        }
    }

    fn load_tiles(&self, floats: &[c_double], ints: &[c_int]) -> Vec<size_t> {
        let mut ti = vec![0; self.n_tilings as usize];

        unsafe {
            tiles(ti.as_mut_ptr(), self.n_tilings, self.memory_size,
                  floats.as_ptr(), floats.len() as c_int,
                  ints.as_ptr(), ints.len() as c_int);
        }

        ti
    }

    fn evaluate_index(&self, input: &Vec<f64>, index: c_int) -> f64 {
        let tiles = self.load_tiles(input, &[index]);

        self.weights.select(Axis(0), tiles.as_slice()).scalar_sum() / self.n_tilings as f64
    }
}

impl Function<Vec<f64>, f64> for SuttonTiles {
    fn evaluate(&self, input: &Vec<f64>) -> f64 {
        self.evaluate_index(input, 0)
    }
}

impl Function<Vec<f64>, Vec<f64>> for SuttonTiles {
    fn evaluate(&self, input: &Vec<f64>) -> Vec<f64> {
        (0..self.n_outputs).map(|c| self.evaluate_index(input, c as c_int)).collect()
    }
}

impl Parameterised<Vec<f64>, f64> for SuttonTiles {
    fn update(&mut self, input: &Vec<f64>, error: f64) {
        self.int_array[0] = 0;

        for t in self.load_tiles(input, &self.int_array) {
            self.weights[t] += error;
        }
    }
}

impl Parameterised<Vec<f64>, Vec<f64>> for SuttonTiles {
    fn update(&mut self, input: &Vec<f64>, errors: Vec<f64>) {
        for c in 0..self.n_outputs {
            self.int_array[0] = c as c_int;

            for t in self.load_tiles(input, &self.int_array) {
                self.weights[t] += errors[c];
            }
        }
    }
}

// TODO: Implement Linear - problem is that phi will be a function of the state
//       and action for this implementation of tile coding.

impl QFunction<RegularSpace<Continuous>> for SuttonTiles
{
    fn evaluate_action(&self, input: &Vec<f64>, action: usize) -> f64 {
        self.evaluate_index(input, action as c_int)
    }

    fn update_action(&mut self, input: &Vec<f64>, action: usize, error: f64) {
        self.int_array[0] = action as c_int;

        for t in self.load_tiles(input, &self.int_array) {
            self.weights[t] += error;
        }
    }
}

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

    use fa::{Function, Parameterised};
    use geometry::RegularSpace;
    use geometry::dimensions::Partition;

    #[test]
    fn test_simple() {
        let mut t = SuttonTiles::new(1, 1000, 1);

        t.update(&vec![1.5], 25.5);

        let out: f64 = t.evaluate(&vec![1.5]);
        assert_eq!(out, 25.5);

        t.update(&vec![1.5], -12.75);

        let out: f64 = t.evaluate(&vec![1.5]);
        assert_eq!(out, 12.75);
    }
}
<|endoftext|>


<commit_before>use std::{fs, path::Path};

use chromiumoxide::{
    cdp::browser_protocol::page::CaptureScreenshotFormat, handler::viewport::Viewport, Browser,
    BrowserConfig,
};

use codec::{
    common::{async_trait::async_trait, base64, eyre::Result, futures::StreamExt, tokio},
    stencila_schema::{ImageObject, Node},
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions, EncodeOptions,
};
use codec_html::HtmlCodec;

/// Encode and decode a document node to a PNG image.
///
/// This codec will "decode" strings or files to an `ImageObject` and uses a
/// headless browser to encode nodes as a screenshot of the HTML
/// encoding of the node.
pub struct PngCodec {}

#[async_trait]
impl CodecTrait for PngCodec {
    fn spec() -> Codec {
        Codec {
            status: "alpha".to_string(),
            formats: vec_string!["png"],
            root_types: vec_string!["*"],
            ..Default::default()
        }
    }

    /// Decode a document node from a string
    ///
    /// Simply returns an `ImageObject` with the string as its `content_url`
    /// (i.e assumes that the string is a DataURI with a Base64 encoded PNG)
    fn from_str(str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        Ok(Node::ImageObject(ImageObject {
            content_url: str.to_string(),
            ..Default::default()
        }))
    }

    /// Decode a document node from a file system path
    ///
    /// Simply returns an `ImageObject` with the path as its `content_url`
    async fn from_path(path: &Path, _options: Option<DecodeOptions>) -> Result<Node> {
        Ok(Node::ImageObject(ImageObject {
            content_url: path.display().to_string(),
            ..Default::default()
        }))
    }

    /// Encode a document node to a string
    ///
    /// Returns a Base64 encoded dataURI with media type `image/png`.
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        let string = ["data:image/png;base64,", &base64::encode(&bytes[0])].concat();
        Ok(string)
    }

    /// Encode a document node to a file system path
    ///
    /// This override is necessary to avoid the dataURI prefix and Base64 encoding that `to_string_async`
    /// does. It simply writes that bytes to a file at the path.
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        fs::write(path, &bytes[0])?;
        Ok(())
    }
}

/// Encode a set of document nodes to PNGs as bytes
///
/// This function is based around creating multiple PNGs, rather than a single one, to
/// reduce the per-image overhead of starting the browser, loading the theme etc.
pub async fn nodes_to_bytes(
    nodes: &[&Node],
    options: Option<EncodeOptions>,
) -> Result<Vec<Vec<u8>>> {
    // Return early if possible to avoid the following, including requiring Chrome.
    if nodes.is_empty() {
        return Ok(Vec::new());
    }

    // Generate HTML for each node
    let mut nodes_html = String::new();
    for (index, node) in nodes.iter().enumerate() {
        let node_html = HtmlCodec::to_string(
            node,
            Some(EncodeOptions {
                standalone: false,
                bundle: true,
                ..Default::default()
            }),
        )?;
        nodes_html.push_str(&format!(
            r#"
{}
"#,
            index, node_html
        ));
    }

    let theme = options
        .unwrap_or_default()
        .theme
        .unwrap_or_else(|| "rpng".to_string());

    // Wrap the generated HTML into a standalone page with bundled CSS & JS
    let html = codec_html::wrap_standalone(
        &nodes_html,
        EncodeOptions {
            bundle: true,
            theme: Some(theme),
            components: false,
            ..Default::default()
        },
        "PngCodec",
        r"
        body {{
            width: 640px; /* Avoid having images of block node that are too wide */
        }}
        div.node {{
            margin: 10px; /* Mainly to improve spacing when previewing HTML during development */
            min-height: 1px; /* To avoid 'Error -32000: Cannot take screenshot with 0 height' */
            min-width: 1px;
        }}",
    );

    // Launch the browser
    let chrome = binaries::require_any(&[("chrome", "*"), ("chromium", "*")]).await?;
    let config = BrowserConfig::builder()
        .chrome_executable(chrome.path)
        .viewport(Viewport {
            // Increase the scale for higher resolution images. See https://github.com/puppeteer/puppeteer/issues/571#issuecomment-325404760
            device_scale_factor: Some(2.0),
            ..Default::default()
        })
        .build()
        .expect("Should build config");
    let (browser, mut handler) = Browser::launch(config).await?;
    let handler_task = tokio::task::spawn(async move {
        loop {
            let _ = handler.next().await.unwrap();
        }
    });

    // Create a page, set its HTML and wait for "navigation"
    let page = browser.new_page("about:blank").await?;
    page.set_content(html).await?.wait_for_navigation().await?;

    // Take a screenshot of each element
    // This uses `:first-child`, rather than screen-shotting the entire div, so that for
    // inline elements we do not get wide (page width) images. Assumes that the node is represented
    // by one element.
    let mut pngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        let element = page
            .find_element(&format!("#node-{} *:first-child", index))
            .await?;
        let bytes = element.screenshot(CaptureScreenshotFormat::Png).await?;
        pngs.push(bytes)
    }

    // Abort the handler task (if this is not done can get a `ResetWithoutClosingHandshake`
    // when this function ends)
    handler_task.abort();

    Ok(pngs)
}

#[cfg(test)]
mod tests {
    use codec::stencila_schema::CodeChunk;
    use test_utils::common::tempfile;

    use super::*;

    #[tokio::test]
    async fn encode() -> super::Result<()> {
        let node = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        PngCodec::to_path(&node, &path, None).await?;
        assert!(path.exists());

        let data = PngCodec::to_string_async(&node, None).await?;
        assert!(data.starts_with("data:image/png;base64,"));

        Ok(())
    }
}
<commit_msg>fix(PNG codec): Increase viewport size<commit_after>use std::{fs, path::Path};

use chromiumoxide::{
    cdp::browser_protocol::page::CaptureScreenshotFormat, handler::viewport::Viewport, Browser,
    BrowserConfig,
};

use codec::{
    common::{async_trait::async_trait, base64, eyre::Result, futures::StreamExt, tokio},
    stencila_schema::{ImageObject, Node},
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions, EncodeOptions,
};
use codec_html::HtmlCodec;

/// Encode and decode a document node to a PNG image.
///
/// This codec will "decode" strings or files to an `ImageObject` and uses a
/// headless browser to encode nodes as a screenshot of the HTML
/// encoding of the node.
pub struct PngCodec {}

#[async_trait]
impl CodecTrait for PngCodec {
    fn spec() -> Codec {
        Codec {
            status: "alpha".to_string(),
            formats: vec_string!["png"],
            root_types: vec_string!["*"],
            ..Default::default()
        }
    }

    /// Decode a document node from a string
    ///
    /// Simply returns an `ImageObject` with the string as its `content_url`
    /// (i.e assumes that the string is a DataURI with a Base64 encoded PNG)
    fn from_str(str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        Ok(Node::ImageObject(ImageObject {
            content_url: str.to_string(),
            ..Default::default()
        }))
    }

    /// Decode a document node from a file system path
    ///
    /// Simply returns an `ImageObject` with the path as its `content_url`
    async fn from_path(path: &Path, _options: Option<DecodeOptions>) -> Result<Node> {
        Ok(Node::ImageObject(ImageObject {
            content_url: path.display().to_string(),
            ..Default::default()
        }))
    }

    /// Encode a document node to a string
    ///
    /// Returns a Base64 encoded dataURI with media type `image/png`.
    async fn to_string_async(node: &Node, options: Option<EncodeOptions>) -> Result<String> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        let string = ["data:image/png;base64,", &base64::encode(&bytes[0])].concat();
        Ok(string)
    }

    /// Encode a document node to a file system path
    ///
    /// This override is necessary to avoid the dataURI prefix and Base64 encoding that `to_string_async`
    /// does. It simply writes that bytes to a file at the path.
    async fn to_path(node: &Node, path: &Path, options: Option<EncodeOptions>) -> Result<()> {
        let bytes = nodes_to_bytes(&[node], options).await?;
        fs::write(path, &bytes[0])?;
        Ok(())
    }
}

/// Encode a set of document nodes to PNGs as bytes
///
/// This function is based around creating multiple PNGs, rather than a single one, to
/// reduce the per-image overhead of starting the browser, loading the theme etc.
pub async fn nodes_to_bytes(
    nodes: &[&Node],
    options: Option<EncodeOptions>,
) -> Result<Vec<Vec<u8>>> {
    // Return early if possible to avoid the following, including requiring Chrome.
    if nodes.is_empty() {
        return Ok(Vec::new());
    }

    // Generate HTML for each node
    let mut nodes_html = String::new();
    for (index, node) in nodes.iter().enumerate() {
        let node_html = HtmlCodec::to_string(
            node,
            Some(EncodeOptions {
                standalone: false,
                bundle: true,
                ..Default::default()
            }),
        )?;
        nodes_html.push_str(&format!(
            r#"
{}
"#,
            index, node_html
        ));
    }

    let theme = options
        .unwrap_or_default()
        .theme
        .unwrap_or_else(|| "rpng".to_string());

    // Wrap the generated HTML into a standalone page with bundled CSS & JS
    let html = codec_html::wrap_standalone(
        &nodes_html,
        EncodeOptions {
            bundle: true,
            theme: Some(theme),
            components: false,
            ..Default::default()
        },
        "PngCodec",
        r"
        body {{
            width: 640px; /* Avoid having images of block nodes that are too wide */
        }}
        div.node {{
            margin: 10px; /* Mainly to improve spacing when previewing HTML during development */
            min-height: 1px; /* To avoid 'Error -32000: Cannot take screenshot with 0 height' */
            min-width: 1px;
        }}",
    );

    // It can be useful to insect the generated HTML during development. Uncomment this to do so..
    // std::fs::write("rpng.html", &html)?;

    // Launch the browser
    let chrome = binaries::require_any(&[("chrome", "*"), ("chromium", "*")]).await?;
    let config = BrowserConfig::builder()
        .chrome_executable(chrome.path)
        .viewport(Viewport {
            // Increase the scale for higher resolution images. See https://github.com/puppeteer/puppeteer/issues/571#issuecomment-325404760
            device_scale_factor: Some(2.0),
            // Increase size of viewport to be able to encompass the largest images
            height: 10000,
            width: 10000,
            ..Default::default()
        })
        .build()
        .expect("Should build config");
    let (browser, mut handler) = Browser::launch(config).await?;
    let handler_task = tokio::task::spawn(async move {
        loop {
            let _ = handler.next().await.unwrap();
        }
    });

    // Create a page, set its HTML and wait for "navigation"
    let page = browser.new_page("about:blank").await?;
    page.set_content(html).await?.wait_for_navigation().await?;

    // Take a screenshot of each element
    // This uses `:first-child`, rather than screen-shotting the entire div, so that for
    // inline elements we do not get wide (page width) images. Assumes that the node is represented
    // by one element.
    let mut pngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        let element = page
            .find_element(&format!("#node-{} :first-child", index))
            .await?;
        let bytes = element.screenshot(CaptureScreenshotFormat::Png).await?;
        pngs.push(bytes)
    }

    // Abort the handler task (if this is not done can get a `ResetWithoutClosingHandshake`
    // when this function ends)
    handler_task.abort();

    Ok(pngs)
}

#[cfg(test)]
mod tests {
    use codec::stencila_schema::CodeChunk;
    use test_utils::common::tempfile;

    use super::*;

    #[tokio::test]
    async fn encode() -> super::Result<()> {
        let node = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        PngCodec::to_path(&node, &path, None).await?;
        assert!(path.exists());

        let data = PngCodec::to_string_async(&node, None).await?;
        assert!(data.starts_with("data:image/png;base64,"));

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate kiss3d;
extern crate nalgebra as na;

use clap::Parser;
use kiss3d::light::Light;
use kiss3d::window::Window;
use na::vector;
use na::{Translation, UnitQuaternion, Vector3};

/// Program to display a 3x3 cube solution.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
   /// Solve the minotaur problem.
   #[arg(short, long)]
   minotaur: bool,

   /// Solve the planes problem.
   #[arg(short, long)]
   planes: bool,
}

fn main() {
    env_logger::init();
    let args = Args::parse();

    let mut window = Window::new("Kiss3d: cube");
    window.set_light(Light::StickToCamera);

    let mut cubes = Vec::new();
    for i in 0..27 {
        let c = window.add_cube(0.09, 0.09, 0.09);
        cubes.push(c);

        let r = (i % 3) as f32 * 0.1 - 0.1;
        let c = (i / 9) as f32 * 0.1 - 0.1;
        let d = (i % 9) as f32 * 0.05 - 0.1;
        cubes[i].append_translation(&Translation { vector: vector!(r, c, d) });
    }

    if args.minotaur {
        cubes[0].set_color(1.0, 0.0, 0.0);
        cubes[1].set_color(1.0, 0.0, 0.0);
        cubes[2].set_color(1.0, 0.0, 0.0);

        cubes[3].set_color(1.0, 0.0, 0.0);
        cubes[4].set_color(0.0, 1.0, 0.0);
        cubes[5].set_color(0.0, 0.0, 1.0);

        cubes[6].set_color(0.0, 1.0, 0.0);
        cubes[7].set_color(0.0, 1.0, 0.0);
        cubes[8].set_color(0.0, 0.0, 1.0);

        cubes[9].set_color(1.0, 1.0, 0.0);
        cubes[10].set_color(1.0, 0.0, 0.0);
        cubes[11].set_color(0.0, 1.0, 1.0);

        cubes[12].set_color(1.0, 1.0, 0.0);
        cubes[13].set_color(1.0, 1.0, 0.0);
        cubes[14].set_color(0.0, 1.0, 1.0);

        cubes[15].set_color(0.0, 1.0, 0.0);
        cubes[16].set_color(0.0, 0.0, 1.0);
        cubes[17].set_color(0.0, 0.0, 1.0);

        cubes[18].set_color(1.0, 0.0, 1.0);
        cubes[19].set_color(1.0, 0.0, 1.0);
        cubes[20].set_color(1.0, 0.0, 1.0);

        cubes[21].set_color(1.0, 1.0, 0.0);
        cubes[22].set_color(1.0, 0.0, 1.0);
        cubes[23].set_color(0.0, 1.0, 1.0);

        cubes[24].set_color(1.0, 1.0, 0.0);
        cubes[25].set_color(0.0, 1.0, 1.0);
        cubes[26].set_color(0.0, 1.0, 1.0);
    }

    if args.planes {
        for i in 0..9 {
            cubes[i].set_color(1., 0., 0.);
        }
        for i in 9..27 {
            cubes[i].set_color(0., 1., 0.);
        }
        for i in 18..27 {
            cubes[i].set_color(0., 0., 1.);
        }
    }

    let rot1 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.01);
    let rot2 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.01);

    for _ in 0..1600 {
        if window.render() {
            for i in 0..27 {
                cubes[i].append_rotation(&rot1);
            }
        }
    }
    for _ in 0..1600 {
        if window.render() {
            for i in 0..27 {
                cubes[i].append_rotation(&rot2)
            }
        }
    }
}<commit_msg>cargo clippy.<commit_after>extern crate clap;
extern crate kiss3d;
extern crate nalgebra as na;

use clap::Parser;
use kiss3d::light::Light;
use kiss3d::window::Window;
use na::vector;
use na::{Translation, UnitQuaternion, Vector3};

/// Program to display a 3x3 cube solution.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
   /// Solve the minotaur problem.
   #[arg(short, long)]
   minotaur: bool,

   /// Solve the planes problem.
   #[arg(short, long)]
   planes: bool,
}

fn main() {
    env_logger::init();
    let args = Args::parse();

    let mut window = Window::new("Kiss3d: cube");
    window.set_light(Light::StickToCamera);

    let mut cubes = Vec::new();
    for i in 0..27 {
        let c = window.add_cube(0.09, 0.09, 0.09);
        cubes.push(c);

        let r = (i % 3) as f32 * 0.1 - 0.1;
        let c = (i / 9) as f32 * 0.1 - 0.1;
        let d = (i % 9) as f32 * 0.05 - 0.1;
        cubes[i].append_translation(&Translation { vector: vector!(r, c, d) });
    }

    if args.minotaur {
        cubes[0].set_color(1.0, 0.0, 0.0);
        cubes[1].set_color(1.0, 0.0, 0.0);
        cubes[2].set_color(1.0, 0.0, 0.0);

        cubes[3].set_color(1.0, 0.0, 0.0);
        cubes[4].set_color(0.0, 1.0, 0.0);
        cubes[5].set_color(0.0, 0.0, 1.0);

        cubes[6].set_color(0.0, 1.0, 0.0);
        cubes[7].set_color(0.0, 1.0, 0.0);
        cubes[8].set_color(0.0, 0.0, 1.0);

        cubes[9].set_color(1.0, 1.0, 0.0);
        cubes[10].set_color(1.0, 0.0, 0.0);
        cubes[11].set_color(0.0, 1.0, 1.0);

        cubes[12].set_color(1.0, 1.0, 0.0);
        cubes[13].set_color(1.0, 1.0, 0.0);
        cubes[14].set_color(0.0, 1.0, 1.0);

        cubes[15].set_color(0.0, 1.0, 0.0);
        cubes[16].set_color(0.0, 0.0, 1.0);
        cubes[17].set_color(0.0, 0.0, 1.0);

        cubes[18].set_color(1.0, 0.0, 1.0);
        cubes[19].set_color(1.0, 0.0, 1.0);
        cubes[20].set_color(1.0, 0.0, 1.0);

        cubes[21].set_color(1.0, 1.0, 0.0);
        cubes[22].set_color(1.0, 0.0, 1.0);
        cubes[23].set_color(0.0, 1.0, 1.0);

        cubes[24].set_color(1.0, 1.0, 0.0);
        cubes[25].set_color(0.0, 1.0, 1.0);
        cubes[26].set_color(0.0, 1.0, 1.0);
    }

    if args.planes {
        for piece in cubes.iter_mut().take(9) {
            piece.set_color(1., 0., 0.);
        }
        for piece in cubes.iter_mut().take(27).skip(9) {
            piece.set_color(0., 1., 0.);
        }
        for piece in cubes.iter_mut().take(27).skip(18) {
            piece.set_color(0., 0., 1.);
        }
    }

    let rot1 = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.01);
    let rot2 = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.01);

    for _ in 0..1600 {
        if window.render() {
            for piece in cubes.iter_mut().take(27) {
                piece.append_rotation(&rot1);
            }
        }
    }
    for _ in 0..1600 {
        if window.render() {
            for piece in cubes.iter_mut().take(27) {
                piece.append_rotation(&rot2)
            }
        }
    }
}<|endoftext|>


<commit_before>// Problem statement for `strprintf` crate: provide a way to interpolate printf-style format
// strings using native Rust types. For example, it should be possible to format a string
// "%i %.2f %x" using values 42u32, 3.1415f64, and 255u8, and get a `std::string::String` "42 3.14
// ff".
//
// This is the same as strprintf module we already have in C++.
//
// The problem can be solved by wrapping `libc::snprintf`, which is what we do both here and in
// C++. However, our experience with C++ showed that we should constrain what types can be
// formatted. Otherwise, complex objects like `String` will be passed over FFI and lead to
// unexpected results (e.g. garbage strings).
//
// To achieve that, we provide a `Printfable` trait that's implemented only for types that our
// formatting macro accepts. Everything else will result in a compile-time error. See the docs in
// `trait` module for more on that.

extern crate libc;

pub mod specifiers_iterator;
// Re-exporting so that macro can just import the whole crate and get everything it needs.
pub use specifiers_iterator::SpecifiersIterator;

pub mod traits;

use std::ffi::{CStr, CString};
use std::mem;
use std::vec::Vec;
use traits::*;

// Re-exporting platform-specific format specifiers.
#[cfg(target_pointer_width = "32")]
mod format_specifiers_32bit;
#[cfg(target_pointer_width = "32")]
pub use format_specifiers_32bit::*;

#[cfg(target_pointer_width = "64")]
mod format_specifiers_64bit;
#[cfg(target_pointer_width = "64")]
pub use format_specifiers_64bit::*;

/// Helper function to `fmt!`. **Use it only through that macro!**
///
/// Returns a formatted string, or the size of the buffer that's necessary to hold the formatted
/// string.
#[doc(hidden)]
pub fn fmt_arg_with_buffer_size<T>(
    format_cstring: &CStr,
    arg_c_repr_holder: &T,
    buf_size: usize,
) -> Result<String, usize>
where
    T: CReprHolder,
{
    let mut buffer = Vec::<u8>::with_capacity(buf_size);
    let buffer_ptr = buffer.as_mut_ptr();
    unsafe {
        // We're passing the buffer to C, so let's make Rust forget about it for a while.
        mem::forget(buffer);

        let bytes_written = libc::snprintf(
            buffer_ptr as *mut libc::c_char,
            buf_size as libc::size_t,
            format_cstring.as_ptr() as *const libc::c_char,
            arg_c_repr_holder.to_c_repr(),
        ) as usize;
        if bytes_written >= buf_size {
            let _ = Vec::from_raw_parts(buffer_ptr, buf_size, buf_size);
            Err(bytes_written + 1)
        } else {
            let buffer = Vec::from_raw_parts(buffer_ptr, bytes_written, buf_size);
            Ok(String::from_utf8_lossy(&buffer).into_owned())
        }
    }
}

/// Helper function to `fmt!`. **Use it only through that macro!**
#[doc(hidden)]
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Returns None if `format` contains a null byte
    CString::new(format).ok().and_then(|local_format_cstring| {
        // Returns None if a holder couldn't be obtained - e.g. the value is a String that
        // contains a null byte
        arg.to_c_repr_holder().and_then(|arg_c_repr_holder| {
            match fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, 1024) {
                Ok(formatted) => Some(formatted),
                Err(buf_size) => {
                    fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, buf_size)
                        .ok()
                }
            }
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
///
/// It pairs each format specifier ("%i", "%.2f" etc.) with a value, and passes those to
/// `libc::snprinf`; the results are then concatenated.
///
/// If a pair couldn't be formatted, it's omitted from the output. This can happen if:
/// - a format string contains null bytes;
/// - the string value to be formatted contains null bytes;
/// - `libc::snprintf` failed to format things even when given large enough buffer to do so.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::*;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    extern crate libc;

    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!(&format!("%{}", PRIi32), 42i32), "42");
        assert_eq!(fmt!(&format!("%{}", PRId32), 42i32), "42");

        assert_eq!(fmt!(&format!("%{}", PRIi32), std::i32::MIN), "-2147483648");
        assert_eq!(fmt!(&format!("%{}", PRId32), std::i32::MIN), "-2147483648");

        assert_eq!(fmt!(&format!("%{}", PRIi32), std::i32::MAX), "2147483647");
        assert_eq!(fmt!(&format!("%{}", PRId32), std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!(&format!("%{}", PRIu32), 42u32), "42");
        assert_eq!(fmt!(&format!("%{}", PRIu32), 0u32), "0");
        assert_eq!(fmt!(&format!("%{}", PRIu32), std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!(&format!("%{}", PRIi64), 42i64), "42");
        assert_eq!(fmt!(&format!("%{}", PRId64), 42i64), "42");

        assert_eq!(
            fmt!(&format!("%{}", PRIi64), std::i64::MIN),
            "-9223372036854775808"
        );
        assert_eq!(
            fmt!(&format!("%{}", PRId64), std::i64::MIN),
            "-9223372036854775808"
        );

        assert_eq!(
            fmt!(&format!("%{}", PRIi64), std::i64::MAX),
            "9223372036854775807"
        );
        assert_eq!(
            fmt!(&format!("%{}", PRId64), std::i64::MAX),
            "9223372036854775807"
        );
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!(&format!("%{}", PRIu64), 42u64), "42");
        assert_eq!(fmt!(&format!("%{}", PRIu64), 0u64), "0");
        assert_eq!(
            fmt!(&format!("%{}", PRIu64), std::u64::MAX),
            "18446744073709551615"
        );
    }

    #[test]
    fn formats_pointers() {
        let x = 42i64;

        let x_ptr = &x as *const i64;
        let x_ptr_formatted = fmt!("%p", x_ptr);

        let x_ptr_void = x_ptr as *const libc::c_void;
        let x_ptr_void_formatted = fmt!("%p", x_ptr_void);
        assert!(!x_ptr_formatted.is_empty());
        assert_eq!(x_ptr_formatted, x_ptr_void_formatted);
    }

    #[test]
    fn formats_all_null_pointers_the_same() {
        let i32_ptr_fmt = fmt!("%p", std::ptr::null::<i32>());
        let u32_ptr_fmt = fmt!("%p", std::ptr::null::<u32>());
        let i64_ptr_fmt = fmt!("%p", std::ptr::null::<i64>());
        let u64_ptr_fmt = fmt!("%p", std::ptr::null::<u64>());
        let f32_ptr_fmt = fmt!("%p", std::ptr::null::<f32>());
        let f64_ptr_fmt = fmt!("%p", std::ptr::null::<f64>());
        assert!(!i32_ptr_fmt.is_empty());
        assert_eq!(i32_ptr_fmt, u32_ptr_fmt);
        assert_eq!(u32_ptr_fmt, i64_ptr_fmt);
        assert_eq!(i64_ptr_fmt, u64_ptr_fmt);
        assert_eq!(u64_ptr_fmt, f32_ptr_fmt);
        assert_eq!(f32_ptr_fmt, f64_ptr_fmt);
    }

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");

        assert_eq!(fmt!("%f", std::f32::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f32::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f32::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f32::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f32::NAN), "nan");
        assert_eq!(fmt!("%F", std::f32::NAN), "NAN");
    }

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");

        assert_eq!(fmt!("%f", std::f64::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f64::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f64::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f64::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f64::NAN), "nan");
        assert_eq!(fmt!("%F", std::f64::NAN), "NAN");
    }

    #[test]
    fn formats_str_slice() {
        let input = "Hello, world!";
        assert_eq!(fmt!("%s", input), input);
    }

    #[test]
    fn formats_borrowed_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", &input), input);
    }

    #[test]
    fn formats_moved_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", input.clone()), input);
    }

    #[test]
    fn formats_2_megabyte_string() {
        let spacer = String::from(" ").repeat(1024 * 1024);
        let format = {
            let mut result = spacer.clone();
            result.push_str("%i");
            result.push_str(&spacer);
            result.push_str("%i");
            result
        };
        let expected = {
            let mut result = spacer.clone();
            result.push_str("42");
            result.push_str(&spacer);
            result.push_str("100500");
            result
        };
        assert_eq!(fmt!(&format, 42, 100500), expected);
    }
}
<commit_msg>Prepare strprintf for Rust 2018<commit_after>// Problem statement for `strprintf` crate: provide a way to interpolate printf-style format
// strings using native Rust types. For example, it should be possible to format a string
// "%i %.2f %x" using values 42u32, 3.1415f64, and 255u8, and get a `std::string::String` "42 3.14
// ff".
//
// This is the same as strprintf module we already have in C++.
//
// The problem can be solved by wrapping `libc::snprintf`, which is what we do both here and in
// C++. However, our experience with C++ showed that we should constrain what types can be
// formatted. Otherwise, complex objects like `String` will be passed over FFI and lead to
// unexpected results (e.g. garbage strings).
//
// To achieve that, we provide a `Printfable` trait that's implemented only for types that our
// formatting macro accepts. Everything else will result in a compile-time error. See the docs in
// `trait` module for more on that.

extern crate libc;

pub mod specifiers_iterator;
// Re-exporting so that macro can just import the whole crate and get everything it needs.
pub use crate::specifiers_iterator::SpecifiersIterator;

pub mod traits;

use crate::traits::*;
use std::ffi::{CStr, CString};
use std::mem;
use std::vec::Vec;

// Re-exporting platform-specific format specifiers.
#[cfg(target_pointer_width = "32")]
mod format_specifiers_32bit;
#[cfg(target_pointer_width = "32")]
pub use format_specifiers_32bit::*;

#[cfg(target_pointer_width = "64")]
mod format_specifiers_64bit;
#[cfg(target_pointer_width = "64")]
pub use crate::format_specifiers_64bit::*;

/// Helper function to `fmt!`. **Use it only through that macro!**
///
/// Returns a formatted string, or the size of the buffer that's necessary to hold the formatted
/// string.
#[doc(hidden)]
pub fn fmt_arg_with_buffer_size<T>(
    format_cstring: &CStr,
    arg_c_repr_holder: &T,
    buf_size: usize,
) -> Result<String, usize>
where
    T: CReprHolder,
{
    let mut buffer = Vec::<u8>::with_capacity(buf_size);
    let buffer_ptr = buffer.as_mut_ptr();
    unsafe {
        // We're passing the buffer to C, so let's make Rust forget about it for a while.
        mem::forget(buffer);

        let bytes_written = libc::snprintf(
            buffer_ptr as *mut libc::c_char,
            buf_size as libc::size_t,
            format_cstring.as_ptr() as *const libc::c_char,
            arg_c_repr_holder.to_c_repr(),
        ) as usize;
        if bytes_written >= buf_size {
            let _ = Vec::from_raw_parts(buffer_ptr, buf_size, buf_size);
            Err(bytes_written + 1)
        } else {
            let buffer = Vec::from_raw_parts(buffer_ptr, bytes_written, buf_size);
            Ok(String::from_utf8_lossy(&buffer).into_owned())
        }
    }
}

/// Helper function to `fmt!`. **Use it only through that macro!**
#[doc(hidden)]
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Returns None if `format` contains a null byte
    CString::new(format).ok().and_then(|local_format_cstring| {
        // Returns None if a holder couldn't be obtained - e.g. the value is a String that
        // contains a null byte
        arg.to_c_repr_holder().and_then(|arg_c_repr_holder| {
            match fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, 1024) {
                Ok(formatted) => Some(formatted),
                Err(buf_size) => {
                    fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, buf_size)
                        .ok()
                }
            }
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
///
/// It pairs each format specifier ("%i", "%.2f" etc.) with a value, and passes those to
/// `libc::snprinf`; the results are then concatenated.
///
/// If a pair couldn't be formatted, it's omitted from the output. This can happen if:
/// - a format string contains null bytes;
/// - the string value to be formatted contains null bytes;
/// - `libc::snprintf` failed to format things even when given large enough buffer to do so.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::*;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    extern crate libc;

    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!(&format!("%{}", PRIi32), 42i32), "42");
        assert_eq!(fmt!(&format!("%{}", PRId32), 42i32), "42");

        assert_eq!(fmt!(&format!("%{}", PRIi32), std::i32::MIN), "-2147483648");
        assert_eq!(fmt!(&format!("%{}", PRId32), std::i32::MIN), "-2147483648");

        assert_eq!(fmt!(&format!("%{}", PRIi32), std::i32::MAX), "2147483647");
        assert_eq!(fmt!(&format!("%{}", PRId32), std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!(&format!("%{}", PRIu32), 42u32), "42");
        assert_eq!(fmt!(&format!("%{}", PRIu32), 0u32), "0");
        assert_eq!(fmt!(&format!("%{}", PRIu32), std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!(&format!("%{}", PRIi64), 42i64), "42");
        assert_eq!(fmt!(&format!("%{}", PRId64), 42i64), "42");

        assert_eq!(
            fmt!(&format!("%{}", PRIi64), std::i64::MIN),
            "-9223372036854775808"
        );
        assert_eq!(
            fmt!(&format!("%{}", PRId64), std::i64::MIN),
            "-9223372036854775808"
        );

        assert_eq!(
            fmt!(&format!("%{}", PRIi64), std::i64::MAX),
            "9223372036854775807"
        );
        assert_eq!(
            fmt!(&format!("%{}", PRId64), std::i64::MAX),
            "9223372036854775807"
        );
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!(&format!("%{}", PRIu64), 42u64), "42");
        assert_eq!(fmt!(&format!("%{}", PRIu64), 0u64), "0");
        assert_eq!(
            fmt!(&format!("%{}", PRIu64), std::u64::MAX),
            "18446744073709551615"
        );
    }

    #[test]
    fn formats_pointers() {
        let x = 42i64;

        let x_ptr = &x as *const i64;
        let x_ptr_formatted = fmt!("%p", x_ptr);

        let x_ptr_void = x_ptr as *const libc::c_void;
        let x_ptr_void_formatted = fmt!("%p", x_ptr_void);
        assert!(!x_ptr_formatted.is_empty());
        assert_eq!(x_ptr_formatted, x_ptr_void_formatted);
    }

    #[test]
    fn formats_all_null_pointers_the_same() {
        let i32_ptr_fmt = fmt!("%p", std::ptr::null::<i32>());
        let u32_ptr_fmt = fmt!("%p", std::ptr::null::<u32>());
        let i64_ptr_fmt = fmt!("%p", std::ptr::null::<i64>());
        let u64_ptr_fmt = fmt!("%p", std::ptr::null::<u64>());
        let f32_ptr_fmt = fmt!("%p", std::ptr::null::<f32>());
        let f64_ptr_fmt = fmt!("%p", std::ptr::null::<f64>());
        assert!(!i32_ptr_fmt.is_empty());
        assert_eq!(i32_ptr_fmt, u32_ptr_fmt);
        assert_eq!(u32_ptr_fmt, i64_ptr_fmt);
        assert_eq!(i64_ptr_fmt, u64_ptr_fmt);
        assert_eq!(u64_ptr_fmt, f32_ptr_fmt);
        assert_eq!(f32_ptr_fmt, f64_ptr_fmt);
    }

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");

        assert_eq!(fmt!("%f", std::f32::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f32::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f32::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f32::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f32::NAN), "nan");
        assert_eq!(fmt!("%F", std::f32::NAN), "NAN");
    }

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");

        assert_eq!(fmt!("%f", std::f64::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f64::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f64::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f64::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f64::NAN), "nan");
        assert_eq!(fmt!("%F", std::f64::NAN), "NAN");
    }

    #[test]
    fn formats_str_slice() {
        let input = "Hello, world!";
        assert_eq!(fmt!("%s", input), input);
    }

    #[test]
    fn formats_borrowed_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", &input), input);
    }

    #[test]
    fn formats_moved_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", input.clone()), input);
    }

    #[test]
    fn formats_2_megabyte_string() {
        let spacer = String::from(" ").repeat(1024 * 1024);
        let format = {
            let mut result = spacer.clone();
            result.push_str("%i");
            result.push_str(&spacer);
            result.push_str("%i");
            result
        };
        let expected = {
            let mut result = spacer.clone();
            result.push_str("42");
            result.push_str(&spacer);
            result.push_str("100500");
            result
        };
        assert_eq!(fmt!(&format, 42, 100500), expected);
    }
}
<|endoftext|>