<commit_before>extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate pkg_config;
extern crate wayland_scanner;

use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs, io};

fn main() {
    meson();
    let protocol_header_path =
        generate_protocol_headers().expect("Could not generate header files for wayland protocols");
    let target_dir = env::var("OUT_DIR").expect("$OUT_DIR not set!");
    let mut builder = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_type(r"^xkb_.*$")
        .whitelisted_type(r"^XKB_.*$")
        .whitelisted_function(r"^_?pixman_.*$")
        .whitelisted_function(r"^_?wlr_.*$")
        .whitelisted_function(r"^xkb_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        // NOTE Necessary because they use the out directory to put
        // pragma information on what features are available in a header file
        // titled "config.h"
        .clang_arg(format!("-I{}{}", target_dir, "/include/"))
        .clang_arg(format!("-I{}", protocol_header_path.to_str().unwrap()))
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL");
    if cfg!(feature = "unstable") {
        builder = builder.clang_arg("-DWLR_USE_UNSTABLE");
    }
    if !cfg!(feature = "static") {
        // config.h won't exist, so make a dummy file.
        // We don't need it because of the following -D defines.
        fs::create_dir_all(format!("{}{}", target_dir, "/include/wlr/"))
            .expect("Could not create <out>/include/wlr");
        fs::File::create(format!("{}{}", target_dir, "/include/wlr/config.h"))
            .expect("Could not create dummy config.h file");
        // meson automatically sets up variables, but if we are linking
        // dynamically bindgen will no longer have them.
        builder = builder.clang_args(
            [
                format!("-DWLR_HAS_LIBCAP={}", cfg!(feature = "libcap") as u8),
                format!("-DWLR_HAS_SYSTEMD={}", cfg!(feature = "systemd") as u8),
                format!("-DWLR_HAS_ELOGIND={}", cfg!(feature = "elogind") as u8),
                format!("-DWLR_HAS_X11_BACKEND={}", cfg!(feature = "x11_backend") as u8),
                format!("-DWLR_HAS_XWAYLAND={}", cfg!(feature = "xwayland") as u8),
                format!("-DWLR_HAS_XCB_ERRORS={}", cfg!(feature = "xcb_errors") as u8),
                format!("-DWLR_HAS_XCB_ICCCM={}", cfg!(feature = "xcb_icccm") as u8)
            ]
            .iter()
        )
    }
    let generated = builder.generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=xcb-composite");
    println!("cargo:rustc-link-lib=dylib=xcb-xfixes");
    println!("cargo:rustc-link-lib=dylib=xcb-image");
    println!("cargo:rustc-link-lib=dylib=xcb-render");
    println!("cargo:rustc-link-lib=dylib=xcb-shm");
    println!("cargo:rustc-link-lib=dylib=xcb-icccm");
    println!("cargo:rustc-link-lib=dylib=xcb-xkb");
    println!("cargo:rustc-link-lib=dylib=xcb-xinput");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    link_optional_libs();

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    generated.write_to_file("src/gen.rs").unwrap();

    generate_protocols();
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path = PathBuf::from(env::var("OUT_DIR").expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path
        .to_str()
        .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/x11", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_backend_x11");
        println!("cargo:rustc-link-lib=static=wlr_render");
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    if Path::new("wlroots").exists() {
        meson::build("wlroots", build_path_str);
    } else {
        panic!("The `wlroots` submodule does not exist");
    }
}

/// Gets the unstable and stable protocols in /usr/share-wayland-protocols and
/// generates server headers for them.
///
/// The path to the folder with the generated headers is returned. It will
/// have two directories, `stable`, and `unstable`.
fn generate_protocol_headers() -> io::Result<PathBuf> {
    let output_dir_str = env::var("OUT_DIR").unwrap();
    let out_path: PathBuf = format!("{}/wayland-protocols", output_dir_str).into();
    fs::create_dir(&out_path).ok();
    let protocols_prefix = pkg_config::get_variable("wayland-protocols", "prefix").unwrap();
    let protocols = fs::read_dir(format!("{}/share/wayland-protocols/stable", protocols_prefix))?.chain(
        fs::read_dir(format!("{}/share/wayland-protocols/unstable", protocols_prefix))?
    );
    for entry in protocols {
        let entry = entry?;
        for entry in fs::read_dir(entry.path())? {
            let entry = entry?;
            let path = entry.path();
            let mut filename = entry.file_name().into_string().unwrap();
            if filename.ends_with(".xml") {
                let new_length = filename.len() - 4;
                filename.truncate(new_length);
            }
            filename.push_str("-protocol");
            Command::new("wayland-scanner")
                .arg("server-header")
                .arg(path.clone())
                .arg(format!("{}/{}.h", out_path.to_str().unwrap(), filename))
                .status()
                .unwrap();
        }
    }
    Ok(out_path)
}

fn generate_protocols() {
    let output_dir_str = env::var("OUT_DIR").unwrap();

    let output_dir = Path::new(&output_dir_str);

    let protocols = &[
        ("./wlroots/protocol/server-decoration.xml", "server_decoration"),
        (
            "./wlroots/protocol/wlr-gamma-control-unstable-v1.xml",
            "gamma_control"
        ),
        ("./wlroots/protocol/wlr-screencopy-unstable-v1.xml", "screencopy"),
        ("./wlroots/protocol/screenshooter.xml", "screenshooter"),
        ("./wlroots/protocol/idle.xml", "idle")
    ];

    for protocol in protocols {
        wayland_scanner::generate_c_code(
            protocol.0,
            output_dir.join(format!("{}_server_api.rs", protocol.1)),
            wayland_scanner::Side::Server
        );
        wayland_scanner::generate_c_code(
            protocol.0,
            output_dir.join(format!("{}_client_api.rs", protocol.1)),
            wayland_scanner::Side::Client
        );
        wayland_scanner::generate_c_interfaces(
            protocol.0,
            output_dir.join(format!("{}_interfaces.rs", protocol.1))
        );
    }
}

fn link_optional_libs() {
    if cfg!(feature = "libcap") && pkg_config::probe_library("libcap").is_ok() {
        println!("cargo:rustc-link-lib=dylib=cap");
    }
    if cfg!(feature = "systemd") && pkg_config::probe_library("libsystemd").is_ok() {
        println!("cargo:rustc-link-lib=dylib=systemd");
    }
    if cfg!(feature = "elogind") && pkg_config::probe_library("elogind").is_ok() {
        println!("cargo:rustc-link-lib=dylib=elogind");
    }
    if pkg_config::probe_library("xcb-errors").is_ok() {
        println!("cargo:rustc-link-lib=dylib=xcb-errors");
    }
}
<commit_msg>Fixed rebuild when going from stable -> unstable<commit_after>extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate pkg_config;
extern crate wayland_scanner;

use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, fs, io};

fn main() {
    meson();
    let protocol_header_path =
        generate_protocol_headers().expect("Could not generate header files for wayland protocols");
    let target_dir = env::var("OUT_DIR").expect("$OUT_DIR not set!");
    let mut builder = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_type(r"^xkb_.*$")
        .whitelisted_type(r"^XKB_.*$")
        .whitelisted_function(r"^_?pixman_.*$")
        .whitelisted_function(r"^_?wlr_.*$")
        .whitelisted_function(r"^xkb_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        // NOTE Necessary because they use the out directory to put
        // pragma information on what features are available in a header file
        // titled "config.h"
        .clang_arg(format!("-I{}{}", target_dir, "/include/"))
        .clang_arg(format!("-I{}", protocol_header_path.to_str().unwrap()))
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL");
    if cfg!(feature = "unstable") {
        builder = builder.clang_arg("-DWLR_USE_UNSTABLE");
    }
    if !cfg!(feature = "static") {
        // config.h won't exist, so make a dummy file.
        // We don't need it because of the following -D defines.
        fs::create_dir_all(format!("{}{}", target_dir, "/include/wlr/"))
            .expect("Could not create <out>/include/wlr");
        fs::File::create(format!("{}{}", target_dir, "/include/wlr/config.h"))
            .expect("Could not create dummy config.h file");
        // meson automatically sets up variables, but if we are linking
        // dynamically bindgen will no longer have them.
        builder = builder.clang_args(
            [
                format!("-DWLR_HAS_LIBCAP={}", cfg!(feature = "libcap") as u8),
                format!("-DWLR_HAS_SYSTEMD={}", cfg!(feature = "systemd") as u8),
                format!("-DWLR_HAS_ELOGIND={}", cfg!(feature = "elogind") as u8),
                format!("-DWLR_HAS_X11_BACKEND={}", cfg!(feature = "x11_backend") as u8),
                format!("-DWLR_HAS_XWAYLAND={}", cfg!(feature = "xwayland") as u8),
                format!("-DWLR_HAS_XCB_ERRORS={}", cfg!(feature = "xcb_errors") as u8),
                format!("-DWLR_HAS_XCB_ICCCM={}", cfg!(feature = "xcb_icccm") as u8)
            ]
            .iter()
        )
    }
    let generated = builder.generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=xcb-composite");
    println!("cargo:rustc-link-lib=dylib=xcb-xfixes");
    println!("cargo:rustc-link-lib=dylib=xcb-image");
    println!("cargo:rustc-link-lib=dylib=xcb-render");
    println!("cargo:rustc-link-lib=dylib=xcb-shm");
    println!("cargo:rustc-link-lib=dylib=xcb-icccm");
    println!("cargo:rustc-link-lib=dylib=xcb-xkb");
    println!("cargo:rustc-link-lib=dylib=xcb-xinput");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    link_optional_libs();

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    println!("cargo:rerun-if-changed={}", "src/gen.rs");
    generated.write_to_file("src/gen.rs").unwrap();

    generate_protocols();
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path = PathBuf::from(env::var("OUT_DIR").expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path
        .to_str()
        .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/x11", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_backend_x11");
        println!("cargo:rustc-link-lib=static=wlr_render");
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    if Path::new("wlroots").exists() {
        meson::build("wlroots", build_path_str);
    } else {
        panic!("The `wlroots` submodule does not exist");
    }
}

/// Gets the unstable and stable protocols in /usr/share-wayland-protocols and
/// generates server headers for them.
///
/// The path to the folder with the generated headers is returned. It will
/// have two directories, `stable`, and `unstable`.
fn generate_protocol_headers() -> io::Result<PathBuf> {
    let output_dir_str = env::var("OUT_DIR").unwrap();
    let out_path: PathBuf = format!("{}/wayland-protocols", output_dir_str).into();
    fs::create_dir(&out_path).ok();
    let protocols_prefix = pkg_config::get_variable("wayland-protocols", "prefix").unwrap();
    let protocols = fs::read_dir(format!("{}/share/wayland-protocols/stable", protocols_prefix))?.chain(
        fs::read_dir(format!("{}/share/wayland-protocols/unstable", protocols_prefix))?
    );
    for entry in protocols {
        let entry = entry?;
        for entry in fs::read_dir(entry.path())? {
            let entry = entry?;
            let path = entry.path();
            let mut filename = entry.file_name().into_string().unwrap();
            if filename.ends_with(".xml") {
                let new_length = filename.len() - 4;
                filename.truncate(new_length);
            }
            filename.push_str("-protocol");
            Command::new("wayland-scanner")
                .arg("server-header")
                .arg(path.clone())
                .arg(format!("{}/{}.h", out_path.to_str().unwrap(), filename))
                .status()
                .unwrap();
        }
    }
    Ok(out_path)
}

fn generate_protocols() {
    let output_dir_str = env::var("OUT_DIR").unwrap();

    let output_dir = Path::new(&output_dir_str);

    let protocols = &[
        ("./wlroots/protocol/server-decoration.xml", "server_decoration"),
        (
            "./wlroots/protocol/wlr-gamma-control-unstable-v1.xml",
            "gamma_control"
        ),
        ("./wlroots/protocol/wlr-screencopy-unstable-v1.xml", "screencopy"),
        ("./wlroots/protocol/screenshooter.xml", "screenshooter"),
        ("./wlroots/protocol/idle.xml", "idle")
    ];

    for protocol in protocols {
        wayland_scanner::generate_c_code(
            protocol.0,
            output_dir.join(format!("{}_server_api.rs", protocol.1)),
            wayland_scanner::Side::Server
        );
        wayland_scanner::generate_c_code(
            protocol.0,
            output_dir.join(format!("{}_client_api.rs", protocol.1)),
            wayland_scanner::Side::Client
        );
        wayland_scanner::generate_c_interfaces(
            protocol.0,
            output_dir.join(format!("{}_interfaces.rs", protocol.1))
        );
    }
}

fn link_optional_libs() {
    if cfg!(feature = "libcap") && pkg_config::probe_library("libcap").is_ok() {
        println!("cargo:rustc-link-lib=dylib=cap");
    }
    if cfg!(feature = "systemd") && pkg_config::probe_library("libsystemd").is_ok() {
        println!("cargo:rustc-link-lib=dylib=systemd");
    }
    if cfg!(feature = "elogind") && pkg_config::probe_library("elogind").is_ok() {
        println!("cargo:rustc-link-lib=dylib=elogind");
    }
    if pkg_config::probe_library("xcb-errors").is_ok() {
        println!("cargo:rustc-link-lib=dylib=xcb-errors");
    }
}
<|endoftext|>


<commit_before>use super::{BeaconChain, ClientDB, SlotClock};
pub use crate::attestation_aggregator::{Error as AggregatorError, ProcessOutcome};
use crate::canonical_head::Error as HeadError;
use types::FreeAttestation;

#[derive(Debug, PartialEq)]
pub enum Error {
    PresentSlotUnknown,
    AggregatorError(AggregatorError),
    HeadError(HeadError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    pub fn process_free_attestation(
        &self,
        free_attestation: FreeAttestation,
    ) -> Result<ProcessOutcome, Error> {
        self.attestation_aggregator
            .write()
            .process_free_attestation(&self.state.read(), &free_attestation, &self.spec)
            .map_err(|e| e.into())
    }
}

impl From<AggregatorError> for Error {
    fn from(e: AggregatorError) -> Error {
        Error::AggregatorError(e)
    }
}

impl From<HeadError> for Error {
    fn from(e: HeadError) -> Error {
        Error::HeadError(e)
    }
}
<commit_msg>Tidy attestation_processing, add docstrings.<commit_after>use super::{BeaconChain, ClientDB, SlotClock};
pub use crate::attestation_aggregator::{Error as AggregatorError, ProcessOutcome};
use types::FreeAttestation;

#[derive(Debug, PartialEq)]
pub enum Error {
    /// The free attestation was not processed succesfully.
    AggregatorError(AggregatorError),
}

impl<T, U> BeaconChain<T, U>
where
    T: ClientDB,
    U: SlotClock,
{
    /// Validate a `FreeAttestation` and either:
    ///
    /// - Create a new `Attestation`.
    /// - Aggregate it to an existing `Attestation`.
    pub fn process_free_attestation(
        &self,
        free_attestation: FreeAttestation,
    ) -> Result<ProcessOutcome, Error> {
        self.attestation_aggregator
            .write()
            .process_free_attestation(&self.state.read(), &free_attestation, &self.spec)
            .map_err(|e| e.into())
    }
}

impl From<AggregatorError> for Error {
    fn from(e: AggregatorError) -> Error {
        Error::AggregatorError(e)
    }
}
<|endoftext|>


<commit_before>extern crate crossbeam;
extern crate docopt;
extern crate image;
extern crate num;
extern crate num_cpus;
//extern crate serde;
//#[macro_use]
//extern crate serde_derive;

use image::ColorType;
use image::png::PNGEncoder;
use num::Complex;
use std::fs::File;
use std::io::Write;
use std::str::FromStr;

/*
const USAGE: &'static str = "\
mandelbrot.

Usage:
    mandelbrot <filename> <width> <height> <ulr> <uli> <lrr> <lri>
";

#[derive(Debug, Deserialize)]
struct Args {
    pub arg_filename: String,
    pub arg_width: usize,
    pub arg_height: usize,
    // TODO: figure out how deserialize works with Complex
    pub arg_ulr: f64,
    pub arg_uli: f64,
    pub arg_lrr: f64,
    pub arg_lri: f64,
}
*/

/// this is a documentation comment
fn escape_time(c: Complex<f64>, limit: u32) -> Option<u32> {
    let mut z = Complex::new(0.0, 0.0);
    for i in 0..limit {
        z = z * z + c;
        if z.norm_sqr() > 4.0 {
            return Some(i);
        }
    }
    None
}

fn pixel_to_point(
    bounds: (usize, usize),
    pixel: (usize, usize),
    upper_left: Complex<f64>,
    lower_right: Complex<f64>
) -> Complex<f64>
{
    let (width, height) = (lower_right.re - upper_left.re, upper_left.im - lower_right.im);
    Complex::new(
        upper_left.re + pixel.0 as f64 * width / bounds.0 as f64,
        upper_left.im - pixel.1 as f64 * height / bounds.1 as f64)
}

fn parse_pair<T: FromStr>(s: &str, separator: char) -> Option<(T, T)> {
    match s.find(separator) {
        None => None,
        Some(index) => {
            match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) {
                (Ok(l), Ok(r)) => Some((l, r)),
                _ => None,
            }
        }
    }
}

fn parse_complex(s: &str) -> Option<Complex<f64>> {
    match parse_pair(s, ',') {
        Some((re, im)) => Some(Complex::new(re, im)),
        None => None,
    }
}

fn render(pixels: &mut [u8], bounds: (usize, usize), upper_left: Complex<f64>, lower_right: Complex<f64>) {
    assert!(pixels.len() == bounds.0 * bounds.1);
    for row in 0..bounds.1 {
        for column in 0..bounds.0 {
            let point = pixel_to_point(bounds, (column, row), upper_left, lower_right);
            pixels[row * bounds.0 + column] = match escape_time(point, 255) {
                None => 0,
                Some(count) => 255 - count as u8
            };
        }
    }
}

fn write_image(filename: &str, pixels: &[u8], bounds: (usize, usize)) -> std::io::Result<()> {
    let output = File::create(filename)?;
    let encoder = PNGEncoder::new(output);
    encoder.encode(&pixels, bounds.0 as u32, bounds.1 as u32, ColorType::Palette(8))?;
    Ok(())
}

fn main() {
    /*
    let args: Args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    let (width, height) = (args.arg_width, args.arg_height);
    let bounds = (width, height);
    let upper_left = Complex::new(args.arg_ulr, args.arg_uli);
    let lower_right = Complex::new(args.arg_lrr, args.arg_lri);
    */
    let args: Vec<String> = std::env::args().collect();
    if args.len() != 5 {
        writeln!(std::io::stderr(), "wrong number of args mofuck!").unwrap();
        std::process::exit(1);
    }
    let bounds = parse_pair(&args[2], 'x').expect("image dim fucked");
    let upper_left = parse_complex(&args[3]).expect("upper left fucked");
    let lower_right = parse_complex(&args[4]).expect("lower right fucked");
    let (width, height) = bounds;
    let mut pixels = vec![0; width * height];

    let threads = dbg!(num_cpus::get());
    let rows_per_band = height / threads + 1;
    {
        let bands: Vec<&mut [u8]> = pixels.chunks_mut(rows_per_band * width).collect();
        // blocks until all spawned threads finish
        crossbeam::scope(|spawner| {
            // into_iter gives each iteration exclusive ownership of one band
            for (i, band) in bands.into_iter().enumerate() {
                let top = rows_per_band * i;
                let end = band.len() / width;
                let band_bounds = (width, end);
                let band_upper_left = pixel_to_point(bounds, (0, top), upper_left, lower_right);
                let band_lower_right = pixel_to_point(bounds, (width, top + end), upper_left, lower_right);
                // move makes closure takes ownership of all variables it uses
                // only the closure may use mutable slice band
                spawner.spawn(move |_| {
                    render(band, band_bounds, band_upper_left, band_lower_right);
                });
            }
        }).unwrap();
    }
    write_image(&args[1], &pixels, bounds).expect("error writing PNG file");
}
<commit_msg>switch to floats and cargo fmt<commit_after>extern crate crossbeam;
extern crate docopt;
extern crate image;
extern crate num;
extern crate num_cpus;
//extern crate serde;
//#[macro_use]
//extern crate serde_derive;

use image::ColorType;
use num::Complex;
use std::io::Write;
use std::str::FromStr;

/*
const USAGE: &'static str = "\
mandelbrot.

Usage:
    mandelbrot <filename> <width> <height> <ulr> <uli> <lrr> <lri>
";

#[derive(Debug, Deserialize)]
struct Args {
    pub arg_filename: String,
    pub arg_width: usize,
    pub arg_height: usize,
    // TODO: figure out how deserialize works with Complex
    pub arg_ulr: f64,
    pub arg_uli: f64,
    pub arg_lrr: f64,
    pub arg_lri: f64,
}
*/

/// this is a documentation comment
fn escape_time(c: Complex<f32>, limit: u32) -> Option<u32> {
    let mut z = Complex::new(0.0, 0.0);
    for i in 0..limit {
        z = z * z + c;
        if z.norm_sqr() > 4.0 {
            return Some(i);
        }
    }
    None
}

fn pixel_to_point(
    bounds: (usize, usize),
    pixel: (usize, usize),
    upper_left: Complex<f32>,
    lower_right: Complex<f32>,
) -> Complex<f32> {
    let (width, height) = (
        lower_right.re - upper_left.re,
        upper_left.im - lower_right.im,
    );
    Complex::new(
        upper_left.re + pixel.0 as f32 * width / bounds.0 as f32,
        upper_left.im - pixel.1 as f32 * height / bounds.1 as f32,
    )
}

fn parse_pair<T: FromStr>(s: &str, separator: char) -> Option<(T, T)> {
    match s.find(separator) {
        None => None,
        Some(index) => match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) {
            (Ok(l), Ok(r)) => Some((l, r)),
            _ => None,
        },
    }
}

fn parse_complex(s: &str) -> Option<Complex<f32>> {
    match parse_pair(s, ',') {
        Some((re, im)) => Some(Complex::new(re, im)),
        None => None,
    }
}

fn render(
    pixels: &mut [u8],
    bounds: (usize, usize),
    upper_left: Complex<f32>,
    lower_right: Complex<f32>,
) {
    assert!(pixels.len() == bounds.0 * bounds.1);
    for row in 0..bounds.1 {
        for column in 0..bounds.0 {
            let point = pixel_to_point(bounds, (column, row), upper_left, lower_right);
            pixels[row * bounds.0 + column] = match escape_time(point, 255) {
                None => 0,
                Some(count) => 255 - count as u8,
            };
        }
    }
}

fn main() {
    /*
    let args: Args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    let (width, height) = (args.arg_width, args.arg_height);
    let bounds = (width, height);
    let upper_left = Complex::new(args.arg_ulr, args.arg_uli);
    let lower_right = Complex::new(args.arg_lrr, args.arg_lri);
    */
    let args: Vec<String> = std::env::args().collect();
    if args.len() != 5 {
        writeln!(std::io::stderr(), "wrong number of args mofuck!").unwrap();
        std::process::exit(1);
    }
    let bounds = parse_pair(&args[2], 'x').expect("image dim fucked");
    let upper_left = parse_complex(&args[3]).expect("upper left fucked");
    let lower_right = parse_complex(&args[4]).expect("lower right fucked");
    let (width, height) = bounds;
    let mut pixels = vec![0; width * height];

    let threads = dbg!(num_cpus::get());
    let rows_per_band = height / threads + 1;
    {
        let bands: Vec<&mut [u8]> = pixels.chunks_mut(rows_per_band * width).collect();
        // blocks until all spawned threads finish
        crossbeam::scope(|spawner| {
            // into_iter gives each iteration exclusive ownership of one band
            for (i, band) in bands.into_iter().enumerate() {
                let top = rows_per_band * i;
                let end = band.len() / width;
                let band_bounds = (width, end);
                let band_upper_left = pixel_to_point(bounds, (0, top), upper_left, lower_right);
                let band_lower_right =
                    pixel_to_point(bounds, (width, top + end), upper_left, lower_right);
                // move makes closure takes ownership of all variables it uses
                // only the closure may use mutable slice band
                spawner.spawn(move |_| {
                    render(band, band_bounds, band_upper_left, band_lower_right);
                });
            }
        })
        .unwrap();
    }
    image::save_buffer(
        &args[1],
        &pixels,
        width as u32,
        height as u32,
        ColorType::Gray(8),
    )
    .expect("saving buffer failed");
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate mio;

use self::mio::tcp::TcpStream;
use self::mio::{Token, Poll, Ready, PollOpt};
use std::io::ErrorKind;
use std::io::{Read, Write};
use std::mem;

/// Structure for handling TCP connection functionality
pub struct TcpReaderConn {
    // api version for this connection
    pub api_version: u32,
    // TCP stream socket handle
    pub socket: TcpStream,
    // token for event loop
    pub socket_token: Token,
    // unique prime value
    pub value: u64,

    // values for keeping pending data information
    pending_length: usize,
    pending_index: usize,
    pending_data: Vec<Vec<u8>>,
    // for getting number out of bytes we need to read
    // 4 bytes as a u32 (unsigned int32)
    // so we need to keep indexes for that 4 bytes
    endian_bytes: Vec<u8>,
    endian_bytes_index: usize
}

pub struct TcpWriterConn {
    // api version for this connection
    pub api_version: u32,
    // TCP stream socket handle
    pub socket: TcpStream,
    // token for event loop
    pub socket_token: Token,
    // unique prime value
    pub value: u64,

    // values for keeping write queue
    write_queue: Vec<Vec<u8>>,
    write_queue_element_index: usize,
}

impl TcpReaderConn {
    pub fn new(socket: TcpStream, token: Token) -> TcpReaderConn {
        TcpReaderConn {
            api_version: 0,
            socket: socket,
            socket_token: token,
            value: 0,
            pending_length: 0,
            pending_index: 0,
            pending_data: vec![],
            endian_bytes: vec![0; 4],
            endian_bytes_index: 0
        }
    }

    #[inline(always)]
    fn read_big_endian(&mut self) -> (bool, Option<u32>) {
        let read_len = match self.socket.read(&mut self.endian_bytes[self.endian_bytes_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return (false, Some(0));
                }

                return (true, None);
            }
        };

        self.endian_bytes_index += read_len;
        if self.endian_bytes_index > 4 {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.endian_bytes_index, 4);
            return (true, None);
        }
        // if we still have some data to read
        if self.endian_bytes_index < 4 {
            return (false, Some(0));
        }

        // if we got here then just setting BigEndian bytes and returning parsed number
        self.endian_bytes_index = 0;
        (true, Some(unsafe {
            let a = [self.endian_bytes[0]
                      , self.endian_bytes[1]
                      , self.endian_bytes[2]
                      , self.endian_bytes[3]];
            let endian_num = mem::transmute::<[u8; 4], u32>(a);
            self.endian_bytes = vec![0; 4];
            u32::from_be(endian_num)
        }))
    }

    /// Registering TCP connection to given POLL event loop
    #[inline(always)]
    pub fn register(&self, poll: &Poll) -> bool {
        match poll.register(&self.socket, self.socket_token, Ready::readable(), PollOpt::edge()) {
            Ok(_) => true,
            Err(e) => {
                warn!("Unable to register connection to Poll service ! -> {}", e);
                false
            }
        }
    }

    /// Reading API version from TCP socket
    /// Function will return 'None' if there is a
    /// problem with connection and it need to be closed
    #[inline(always)]
    pub fn read_api_version(&mut self) -> Option<bool> {
        let (done, version) = self.read_big_endian();
        if !done {
            return Some(false);
        }

        if version.is_none() {
            return None;
        }

        self.api_version = version.unwrap();

        Some(true)
    }

    /// Reading Unique Prime Value from TCP Socket
    /// Function will return 'None' if there is a
    /// problem with connection and it need to be closed
    #[inline(always)]
    pub fn read_prime_value(&mut self) -> Option<bool> {
        // trying to read Prime Value as 8 bytes
        if self.pending_length == 0 {
            self.pending_length = 8;
            self.pending_data = vec![vec![0; 8]];
        }

        let read_len = match self.socket.read(&mut self.pending_data[0][self.pending_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return Some(false);
                }

                return None;
            }
        };

        self.pending_index += read_len;
        if self.pending_index > self.pending_length {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.pending_index, self.pending_length);
            return None;
        }

        if self.pending_index < self.pending_length {
            return Some(false);
        }

        // if we got here then we have all data
        self.value = unsafe {
            let data = self.pending_data.remove(0);
            let a: [u8; 8] = [data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]];
            u64::from_be(mem::transmute::<[u8; 8], u64>(a))
        };

        self.pending_length = 0;
        self.pending_index = 0;
        Some(true)
    }

    /// Reading next chunck of data from TCP socket
    /// It is based on our [BigEndian Length]:[Raw Data] API combination
    #[inline(always)]
    pub fn read_data(&mut self) -> (bool, Option<Vec<u8>>) {
        if self.pending_length == 0 {
            let (done, length) = self.read_big_endian();
            if !done {
                return (false, None);
            }

            // if there is a problem reading big endian number
            // notifying to close connection
            if length.is_none() {
                return (true, None);
            }

            // if we got here then we have pending data length
            self.pending_length = length.unwrap() as usize;
            self.pending_data = vec![vec![0; self.pending_length]];
        }

        let read_len = match self.socket.read(&mut self.pending_data[0][self.pending_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return (false, None);
                }

                return (true, None);
            }
        };

        self.pending_index += read_len;
        if self.pending_index > self.pending_length {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.pending_index, self.pending_length);
            return (true, None);
        }

        if self.pending_index < self.pending_length {
            return (false, None);
        }

        self.pending_length = 0;
        self.pending_index = 0;
        (true, Some(self.pending_data.remove(0)))
    }

    /// Making writer socket out of existing information from TcpReaderConn
    /// This connection later would be shared between one of the TcpWriters
    #[inline(always)]
    pub fn make_writer(&self) -> Option<TcpWriterConn> {
        if self.api_version == 0 || self.value == 0 {
            return None;
        }

        match self.socket.try_clone() {
            Ok(s) => {
                Some(TcpWriterConn{
                    api_version: self.api_version,
                    value: self.value,
                    socket: s,
                    socket_token: self.socket_token,
                    write_queue: vec![],
                    write_queue_element_index: 0
                })
            },
            Err(e) => {
                warn!("Unable to clone socket for making writer service ! -> {}", e);
                None
            }
        }
    }
}

impl TcpWriterConn {
    /// Registering TCP connection to given POLL event loop
    #[inline(always)]
    pub fn register(&self, poll: &Poll) -> bool {
        match poll.register(&self.socket, self.socket_token, Ready::writable(), PollOpt::edge()) {
            Ok(_) => true,
            Err(e) => {
                warn!("Unable to register connection to Poll service as writable ! -> {}", e);
                false
            }
        }
    }

    /// This function will try to write data to socket
    /// if it returns 'false' it means there is still data to be written
    /// if it returns 'true' then it have written all available data
    /// so that we can deregister connection as writable and wait for the next data
    pub fn flush_write_queue(&mut self) -> Option<bool> {
        while self.write_queue.len() > 0 {
            {
                let ref mut data = self.write_queue[0];
                let write_len = match self.socket.write(&mut data[self.write_queue_element_index..]) {
                    Ok(n) => n,
                    Err(e) => {
                        // if we got WouldBlock, then this is Non Blocking socket
                        // and data still not available for this, so it's not a connection error
                        if e.kind() == ErrorKind::WouldBlock {
                            return Some(false);
                        }

                        return None;
                    }
                };
                self.write_queue_element_index += write_len;
                if self.write_queue_element_index < data.len() {
                    return Some(false);
                }
            }

            // if we got here then
            // self.write_queue_element_index == data.len()
            // so this data is written, removing it from Queue
            // and moving to the next data
            self.write_queue.remove(0);
        }

        Some(true)
    }
}
<commit_msg>adding write function for connection<commit_after>#![allow(dead_code)]
extern crate mio;

use self::mio::tcp::TcpStream;
use self::mio::{Token, Poll, Ready, PollOpt};
use std::io::ErrorKind;
use std::io::{Read, Write};
use std::mem;
use std::sync::Arc;

/// Structure for handling TCP connection functionality
pub struct TcpReaderConn {
    // api version for this connection
    pub api_version: u32,
    // TCP stream socket handle
    pub socket: TcpStream,
    // token for event loop
    pub socket_token: Token,
    // unique prime value
    pub value: u64,

    // values for keeping pending data information
    pending_length: usize,
    pending_index: usize,
    pending_data: Vec<Vec<u8>>,
    // for getting number out of bytes we need to read
    // 4 bytes as a u32 (unsigned int32)
    // so we need to keep indexes for that 4 bytes
    endian_bytes: Vec<u8>,
    endian_bytes_index: usize
}

pub struct TcpWriterConn {
    // api version for this connection
    pub api_version: u32,
    // TCP stream socket handle
    pub socket: TcpStream,
    // token for event loop
    pub socket_token: Token,
    // unique prime value
    pub value: u64,

    // values for keeping write queue
    write_queue: Vec<Arc<Vec<u8>>>,
    write_queue_element_index: usize,
}

impl TcpReaderConn {
    pub fn new(socket: TcpStream, token: Token) -> TcpReaderConn {
        TcpReaderConn {
            api_version: 0,
            socket: socket,
            socket_token: token,
            value: 0,
            pending_length: 0,
            pending_index: 0,
            pending_data: vec![],
            endian_bytes: vec![0; 4],
            endian_bytes_index: 0
        }
    }

    #[inline(always)]
    fn read_big_endian(&mut self) -> (bool, Option<u32>) {
        let read_len = match self.socket.read(&mut self.endian_bytes[self.endian_bytes_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return (false, Some(0));
                }

                return (true, None);
            }
        };

        self.endian_bytes_index += read_len;
        if self.endian_bytes_index > 4 {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.endian_bytes_index, 4);
            return (true, None);
        }
        // if we still have some data to read
        if self.endian_bytes_index < 4 {
            return (false, Some(0));
        }

        // if we got here then just setting BigEndian bytes and returning parsed number
        self.endian_bytes_index = 0;
        (true, Some(unsafe {
            let a = [self.endian_bytes[0]
                      , self.endian_bytes[1]
                      , self.endian_bytes[2]
                      , self.endian_bytes[3]];
            let endian_num = mem::transmute::<[u8; 4], u32>(a);
            self.endian_bytes = vec![0; 4];
            u32::from_be(endian_num)
        }))
    }

    /// Registering TCP connection to given POLL event loop
    #[inline(always)]
    pub fn register(&self, poll: &Poll) -> bool {
        match poll.register(&self.socket, self.socket_token, Ready::readable(), PollOpt::edge()) {
            Ok(_) => true,
            Err(e) => {
                warn!("Unable to register connection to Poll service ! -> {}", e);
                false
            }
        }
    }

    /// Reading API version from TCP socket
    /// Function will return 'None' if there is a
    /// problem with connection and it need to be closed
    #[inline(always)]
    pub fn read_api_version(&mut self) -> Option<bool> {
        let (done, version) = self.read_big_endian();
        if !done {
            return Some(false);
        }

        if version.is_none() {
            return None;
        }

        self.api_version = version.unwrap();

        Some(true)
    }

    /// Reading Unique Prime Value from TCP Socket
    /// Function will return 'None' if there is a
    /// problem with connection and it need to be closed
    #[inline(always)]
    pub fn read_prime_value(&mut self) -> Option<bool> {
        // trying to read Prime Value as 8 bytes
        if self.pending_length == 0 {
            self.pending_length = 8;
            self.pending_data = vec![vec![0; 8]];
        }

        let read_len = match self.socket.read(&mut self.pending_data[0][self.pending_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return Some(false);
                }

                return None;
            }
        };

        self.pending_index += read_len;
        if self.pending_index > self.pending_length {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.pending_index, self.pending_length);
            return None;
        }

        if self.pending_index < self.pending_length {
            return Some(false);
        }

        // if we got here then we have all data
        self.value = unsafe {
            let data = self.pending_data.remove(0);
            let a: [u8; 8] = [data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]];
            u64::from_be(mem::transmute::<[u8; 8], u64>(a))
        };

        self.pending_length = 0;
        self.pending_index = 0;
        Some(true)
    }

    /// Reading next chunck of data from TCP socket
    /// It is based on our [BigEndian Length]:[Raw Data] API combination
    #[inline(always)]
    pub fn read_data(&mut self) -> (bool, Option<Vec<u8>>) {
        if self.pending_length == 0 {
            let (done, length) = self.read_big_endian();
            if !done {
                return (false, None);
            }

            // if there is a problem reading big endian number
            // notifying to close connection
            if length.is_none() {
                return (true, None);
            }

            // if we got here then we have pending data length
            self.pending_length = length.unwrap() as usize;
            self.pending_data = vec![vec![0; self.pending_length]];
        }

        let read_len = match self.socket.read(&mut self.pending_data[0][self.pending_index..]) {
            Ok(n) => n,
            Err(e) => {
                // if we got WouldBlock, then this is Non Blocking socket
                // and data still not available for this, so it's not a connection error
                if e.kind() == ErrorKind::WouldBlock {
                    return (false, None);
                }

                return (true, None);
            }
        };

        self.pending_index += read_len;
        if self.pending_index > self.pending_length {
            warn!("Pending data index bigger than pending data length, closing connection -> Pending Index: {} , Data Lenght {}"
            , self.pending_index, self.pending_length);
            return (true, None);
        }

        if self.pending_index < self.pending_length {
            return (false, None);
        }

        self.pending_length = 0;
        self.pending_index = 0;
        (true, Some(self.pending_data.remove(0)))
    }

    /// Making writer socket out of existing information from TcpReaderConn
    /// This connection later would be shared between one of the TcpWriters
    #[inline(always)]
    pub fn make_writer(&self) -> Option<TcpWriterConn> {
        if self.api_version == 0 || self.value == 0 {
            return None;
        }

        match self.socket.try_clone() {
            Ok(s) => {
                Some(TcpWriterConn{
                    api_version: self.api_version,
                    value: self.value,
                    socket: s,
                    socket_token: self.socket_token,
                    write_queue: vec![],
                    write_queue_element_index: 0
                })
            },
            Err(e) => {
                warn!("Unable to clone socket for making writer service ! -> {}", e);
                None
            }
        }
    }
}

impl TcpWriterConn {
    /// Registering TCP connection to given POLL event loop
    #[inline(always)]
    pub fn register(&self, poll: &Poll) -> bool {
        match poll.register(&self.socket, self.socket_token, Ready::writable(), PollOpt::edge()) {
            Ok(_) => true,
            Err(e) => {
                warn!("Unable to register connection to Poll service as writable ! -> {}", e);
                false
            }
        }
    }

    /// This function will try to write data to socket
    /// if it returns 'false' it means there is still data to be written
    /// if it returns 'true' then it have written all available data
    /// so that we can deregister connection as writable and wait for the next data
    #[inline(always)]
    pub fn flush_write_queue(&mut self) -> Option<bool> {
        while self.write_queue.len() > 0 {
            {
                let ref mut data = self.write_queue[0];
                let write_len = match self.socket.write(&data[self.write_queue_element_index..]) {
                    Ok(n) => n,
                    Err(e) => {
                        // if we got WouldBlock, then this is Non Blocking socket
                        // and data still not available for this, so it's not a connection error
                        if e.kind() == ErrorKind::WouldBlock {
                            return Some(false);
                        }

                        return None;
                    }
                };
                self.write_queue_element_index += write_len;
                if self.write_queue_element_index < data.len() {
                    return Some(false);
                }
            }

            // if we got here then
            // self.write_queue_element_index == data.len()
            // so this data is written, removing it from Queue
            // and moving to the next data
            self.write_queue.remove(0);
        }

        Some(true)
    }

    /// Base function for writing data to this connection socket
    /// This will first try to write data directly to socket
    /// Then if it's not successfull it will add data to queue for later write process
    #[inline(always)]
    pub fn write(&mut self, data: Arc<Vec<u8>>) {
        self.write_queue.push(data);
    }
}
<|endoftext|>


<commit_before>use crate::common::util::*;

static INPUT: &'static str = "lorem_ipsum.txt";

#[test]
fn test_stdin_default() {
    new_ucmd!()
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_default.expected");
}

#[test]
fn test_stdin_1_line_obsolete() {
    new_ucmd!()
        .args(&["-1"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_1_line() {
    new_ucmd!()
        .args(&["-n", "1"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_negative_23_line() {
    new_ucmd!()
        .args(&["-n", "-23"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_5_chars() {
    new_ucmd!()
        .args(&["-c", "5"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_5_chars.expected");
}

#[test]
fn test_single_default() {
    new_ucmd!()
        .arg(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_default.expected");
}

#[test]
fn test_single_1_line_obsolete() {
    new_ucmd!()
        .args(&["-1", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_single_1_line() {
    new_ucmd!()
        .args(&["-n", "1", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_single_5_chars() {
    new_ucmd!()
        .args(&["-c", "5", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_5_chars.expected");
}

#[test]
fn test_verbose() {
    new_ucmd!()
        .args(&["-v", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_verbose.expected");
}

#[test]
fn test_spams_newline() {
    new_ucmd!()
        .pipe_in("a")
        .succeeds()
        .stdout_is("a\n");
}

#[test]
fn test_unsupported_byte_syntax() {
    new_ucmd!()
        .args(&["-1c"])
        .pipe_in("abc")
        .fails()
        //GNU head returns "a"
        .stdout_is("")
        .stderr_is("head: error: Unrecognized option: \'1\'");
}

#[test]
fn test_unsupported_line_syntax() {
    new_ucmd!()
        .args(&["-n", "2048m"])
        .pipe_in("a\n")
        .fails()
        //.stdout_is("a\n");  What GNU head returns.
        .stdout_is("")
        .stderr_is("head: error: invalid line count \'2048m\': invalid digit found in string");
}

#[test]
fn test_unsupported_zero_terminated_syntax() {
    new_ucmd!()
        .args(&["-z -n 1"])
        .pipe_in("x\0y")
        .fails()
        //GNU Head returns "x\0"
        .stderr_is("head: error: Unrecognized option: \'z\'");
}

#[test]
fn test_unsupported_zero_terminated_syntax_2() {
    new_ucmd!()
        .args(&["-z -n 2"])
        .pipe_in("x\0y")
        .fails()
        //GNU Head returns "x\0y"
        .stderr_is("head: error: Unrecognized option: \'z\'");
}

#[test]
fn test_unsupported_negative_byte_syntax() {
    new_ucmd!()
        .args(&["--bytes=-2"])
        .pipe_in("a\n")
        .fails()
        //GNU Head returns ""
        .stderr_is("head: error: invalid byte count \'-2\': invalid digit found in string");
}

#[test]
fn test_bug_in_negative_zero_lines() {
    new_ucmd!()
        .args(&["--lines=-0"])
        .pipe_in("a\nb\n")
        .succeeds()
        //GNU Head returns "a\nb\n"
        .stdout_is("");
}
<commit_msg>tests: (head) `cargo fmt`<commit_after>use crate::common::util::*;

static INPUT: &'static str = "lorem_ipsum.txt";

#[test]
fn test_stdin_default() {
    new_ucmd!()
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_default.expected");
}

#[test]
fn test_stdin_1_line_obsolete() {
    new_ucmd!()
        .args(&["-1"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_1_line() {
    new_ucmd!()
        .args(&["-n", "1"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_negative_23_line() {
    new_ucmd!()
        .args(&["-n", "-23"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_stdin_5_chars() {
    new_ucmd!()
        .args(&["-c", "5"])
        .pipe_in_fixture(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_5_chars.expected");
}

#[test]
fn test_single_default() {
    new_ucmd!()
        .arg(INPUT)
        .run()
        .stdout_is_fixture("lorem_ipsum_default.expected");
}

#[test]
fn test_single_1_line_obsolete() {
    new_ucmd!()
        .args(&["-1", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_single_1_line() {
    new_ucmd!()
        .args(&["-n", "1", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_1_line.expected");
}

#[test]
fn test_single_5_chars() {
    new_ucmd!()
        .args(&["-c", "5", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_5_chars.expected");
}

#[test]
fn test_verbose() {
    new_ucmd!()
        .args(&["-v", INPUT])
        .run()
        .stdout_is_fixture("lorem_ipsum_verbose.expected");
}

#[test]
fn test_spams_newline() {
    new_ucmd!().pipe_in("a").succeeds().stdout_is("a\n");
}

#[test]
fn test_unsupported_byte_syntax() {
    new_ucmd!()
        .args(&["-1c"])
        .pipe_in("abc")
        .fails()
        //GNU head returns "a"
        .stdout_is("")
        .stderr_is("head: error: Unrecognized option: \'1\'");
}

#[test]
fn test_unsupported_line_syntax() {
    new_ucmd!()
        .args(&["-n", "2048m"])
        .pipe_in("a\n")
        .fails()
        //.stdout_is("a\n");  What GNU head returns.
        .stdout_is("")
        .stderr_is("head: error: invalid line count \'2048m\': invalid digit found in string");
}

#[test]
fn test_unsupported_zero_terminated_syntax() {
    new_ucmd!()
        .args(&["-z -n 1"])
        .pipe_in("x\0y")
        .fails()
        //GNU Head returns "x\0"
        .stderr_is("head: error: Unrecognized option: \'z\'");
}

#[test]
fn test_unsupported_zero_terminated_syntax_2() {
    new_ucmd!()
        .args(&["-z -n 2"])
        .pipe_in("x\0y")
        .fails()
        //GNU Head returns "x\0y"
        .stderr_is("head: error: Unrecognized option: \'z\'");
}

#[test]
fn test_unsupported_negative_byte_syntax() {
    new_ucmd!()
        .args(&["--bytes=-2"])
        .pipe_in("a\n")
        .fails()
        //GNU Head returns ""
        .stderr_is("head: error: invalid byte count \'-2\': invalid digit found in string");
}

#[test]
fn test_bug_in_negative_zero_lines() {
    new_ucmd!()
        .args(&["--lines=-0"])
        .pipe_in("a\nb\n")
        .succeeds()
        //GNU Head returns "a\nb\n"
        .stdout_is("");
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;

use descriptor::pipeline_layout::PipelineLayoutAbstract;

/// Checks whether descriptor sets are compatible with the pipeline.
pub fn check_descriptor_sets_validity<Pl, D>(pipeline: &Pl, descriptor_sets: &D)
                                             -> Result<(), CheckDescriptorSetsValidityError>
    where Pl: ?Sized + PipelineLayoutAbstract,
          D: ?Sized,
{
    // TODO: implement

    Ok(())
}

/// Error that can happen when checking descriptor sets validity.
#[derive(Debug, Copy, Clone)]
pub enum CheckDescriptorSetsValidityError {
    /// The descriptor sets are incompatible with the pipeline layout.
    IncompatibleDescriptorSets,
}

impl error::Error for CheckDescriptorSetsValidityError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            CheckDescriptorSetsValidityError::IncompatibleDescriptorSets => {
                "the descriptor sets are incompatible with the pipeline layout"
            },
        }
    }
}

impl fmt::Display for CheckDescriptorSetsValidityError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}
<commit_msg>Check the descriptor sets compatibility when drawing (#788)<commit_after>// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;

use descriptor::descriptor_set::DescriptorSetsCollection;
use descriptor::pipeline_layout::PipelineLayoutDesc;

/// Checks whether descriptor sets are compatible with the pipeline.
pub fn check_descriptor_sets_validity<Pl, D>(pipeline: &Pl, descriptor_sets: &D)
                                             -> Result<(), CheckDescriptorSetsValidityError>
    where Pl: ?Sized + PipelineLayoutDesc,
          D: ?Sized + DescriptorSetsCollection,
{
    // What's important is not that the pipeline layout and the descriptor sets *match*. Instead
    // what's important is that the descriptor sets are a superset of the pipeline layout. It's not
    // a problem if the descriptor sets provide more elements than expected.

    for set_num in 0 .. pipeline.num_sets() {
        for binding_num in 0 .. pipeline.num_bindings_in_set(set_num).unwrap_or(0) {
            let set_desc = descriptor_sets.descriptor(set_num, binding_num);
            let pipeline_desc = pipeline.descriptor(set_num, binding_num);

            let (set_desc, pipeline_desc) = match (set_desc, pipeline_desc) {
                (Some(s), Some(p)) => (s, p),
                (None, Some(_)) => return Err(CheckDescriptorSetsValidityError::MissingDescriptor {
                    set_num: set_num,
                    binding_num: binding_num,
                }),
                (Some(_), None) => continue,
                (None, None) => continue,
            };

            if !set_desc.is_superset_of(&pipeline_desc) {
                return Err(CheckDescriptorSetsValidityError::IncompatibleDescriptor {
                    set_num: set_num,
                    binding_num: binding_num,
                });
            }
        }
    }

    Ok(())
}

/// Error that can happen when checking descriptor sets validity.
#[derive(Debug, Copy, Clone)]
pub enum CheckDescriptorSetsValidityError {
    /// A descriptor is missing in the descriptor sets that were provided.
    MissingDescriptor {
        /// The index of the set of the descriptor.
        set_num: usize,
        /// The binding number of the descriptor.
        binding_num: usize,
    },

    /// A descriptor in the provided sets is not compatible with what is expected.
    IncompatibleDescriptor {
        /// The index of the set of the descriptor.
        set_num: usize,
        /// The binding number of the descriptor.
        binding_num: usize,
    },
}

impl error::Error for CheckDescriptorSetsValidityError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            CheckDescriptorSetsValidityError::MissingDescriptor { .. } => {
                "a descriptor is missing in the descriptor sets that were provided"
            },
            CheckDescriptorSetsValidityError::IncompatibleDescriptor { .. } => {
                "a descriptor in the provided sets is not compatible with what is expected"
            },
        }
    }
}

impl fmt::Display for CheckDescriptorSetsValidityError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

// TODO: tests
<|endoftext|>


<commit_before>//
//
//
use prelude::v1::*;
use sync::Mutex;
use core::mem::{align_of,size_of};
use core::ptr::Unique;

#[cfg(arch__amd64)]
const HEAP_LIMITS: (usize,usize) = (0x1000_00000000, 0x7000_00000000);

static S_GLOBAL_HEAP: Mutex<AllocState> = Mutex::new(AllocState { start: 0 as *mut _, past_end: 0 as *mut _ } );

// Used by Box<T>
#[lang="exchange_malloc"]
pub unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8
{
	match S_GLOBAL_HEAP.lock().allocate(size, align)
	{
	Ok(x) => x as *mut u8,
	Err(_) => panic!("exchange_malloc({}, {}) out of memory", size, align),
	}
}
#[lang="exchange_free"]
pub unsafe fn exchange_free(ptr: *mut u8, _size: usize, align: usize)
{
	S_GLOBAL_HEAP.lock().deallocate(ptr as *mut (), /*size,*/ align)
}

pub struct Allocation<T>
{
	ptr: Unique<T>,
}

impl<T> Allocation<T>
{
	pub unsafe fn new(bytes: usize) -> Result<Allocation<T>, ()> {
		assert!(bytes == 0 || bytes >= size_of::<T>());
		S_GLOBAL_HEAP.lock().allocate(bytes, align_of::<T>()).map(|v| Allocation { ptr: Unique::new(v as *mut T) })
	}
	pub unsafe fn try_resize(&mut self, newbytes: usize) -> bool {
		let mut lh = S_GLOBAL_HEAP.lock();
		if *self.ptr == 1 as *mut T {
			self.ptr = Unique::new( lh.allocate(newbytes, align_of::<T>()).unwrap() as *mut T );
			true
		}
		else {
			lh.try_expand( *self.ptr as *mut (), newbytes, align_of::<T>() )
		}
	}
}
impl<T> ::core::ops::Deref for Allocation<T>
{
	type Target = *mut T;
	fn deref(&self) -> &*mut T {
		&*self.ptr
	}
}
impl<T> ::core::ops::Drop for Allocation<T>
{
	fn drop(&mut self) {
		// SAFE: Pointer and size are valid
		unsafe {
			S_GLOBAL_HEAP.lock().deallocate(*self.ptr as *mut (), align_of::<T>());
		}
	}
}

struct AllocState
{
	start: *mut Block,
	past_end: *mut Block,
//	first_free: BlockPointer,
}
unsafe impl Send for AllocState {}

struct Block
{
	size: usize,
	state: BlockState,
}
#[derive(PartialEq)]
enum BlockState
{
	Free,
	Used(usize),
}
struct BlockTail
{
	head_ptr: *mut Block,
}

impl AllocState
{
	pub fn allocate(&mut self, size: usize, align: usize) -> Result<*mut (), ()>
	{
		if size == 0 {
			return Ok( 1 as *mut () );
		}
		if self.start == self.past_end {
			self.extend_reservation(size);

			let block = self.last_block();
			return Ok(block.allocate(size, align));
		}

		for block in self.free_blocks()
		{
			// TODO: Block split
			if block.capacity(align) >= size {
				return Ok(block.allocate(size, align));
			}
		}

		let current_extra = self.last_block().self_free().map(|blk| blk.capacity(align)).unwrap_or(0);
		self.extend_reservation(size - current_extra);

		let block = self.last_block();
		return Ok(block.allocate(size, align));
	}
	/// Returns 'true' if expanding succeeded
	pub unsafe fn try_expand(&mut self, ptr: *mut (), size: usize, align: usize) -> bool {
		if size == 0 {
			// TODO: Resize down to 0?
			true
		}
		else if ptr == 1 as *mut () {
			false
		}
		else {
			let bp = Block::ptr_from_ptr(ptr, align);
			if (*bp).capacity(align) > size {
				(*bp).state = BlockState::Used( size );
				true
			}
			else {
				let n = (*bp).next();
				if n == self.past_end {
					false
				}
				else if (*n).self_free().is_some() {
					todo!("AllocState::try_expand - Next is free");
				}
				else {
					false
				}
			}
		}
	}
	pub unsafe fn deallocate(&mut self, ptr: *mut (), align: usize) {
		if ptr == 1 as *mut () {
			return ;
		}
		else {
			let bp = Block::ptr_from_ptr(ptr, align);
			(*bp).state = BlockState::Free;
			let np = (*bp).next();
			if np == self.past_end {
				// Final block
			}
			else if let Some(next) = (*np).self_free() {
				todo!("AllocState::deallocate - Merge with next block");
			}
			else {
				// Next block isn't free, can't merge
			}
		}
	}

	fn last_block(&mut self) -> &mut Block {
		// SAFE: Mutable borrow prevents any form of aliasing
		unsafe { &mut *(*self.past_end).prev() }
	}
	fn extend_reservation(&mut self, required_space: usize) {
		let npages = (required_space + size_of::<Block>() + size_of::<BlockTail>() + 0xFFF) >> 12;
		assert!(npages > 0);
		assert!(self.past_end != HEAP_LIMITS.1 as *mut Block);
		assert!(self.past_end as usize + npages << 12 <= HEAP_LIMITS.1);	// TODO: This isn't an assert conditon, it's an OOM
		if self.start.is_null() {
			self.start = HEAP_LIMITS.0 as *mut Block;
			self.past_end = HEAP_LIMITS.0 as *mut Block;
		}

		// SAFE: Allocates only in controlled region.
		let cb = unsafe {
			::syscalls::memory::allocate(self.past_end as usize, npages).expect("Heap allocation failure");
			(*self.past_end).initialise(npages << 12);
			&mut *self.past_end
			};
		self.past_end = cb.next();
	}
	fn free_blocks(&mut self) -> FreeBlocks {
		FreeBlocks { cur: self.start, state: self, }
	}
}

struct FreeBlocks<'a>
{
	state: &'a mut AllocState,
	cur: *mut Block,
}
impl<'a> ::std::iter::Iterator for FreeBlocks<'a>
{
	type Item = &'a mut Block;
	fn next(&mut self) -> Option<Self::Item>
	{
		while self.cur != self.state.past_end
		{
			// TODO: Yields &mut to blocks, which have a method to get the next item
			// SAFE: (maybe) Only yields each block once... (but blocks can cursor)
			let block = unsafe {
				let bp = self.cur;
				self.cur = (*self.cur).next();
				&mut *bp
				};
			if let BlockState::Free = block.state {
				return Some(block);
			}
		}
		None
	}
}

impl Block
{
	unsafe fn ptr_from_ptr(ptr: *mut (), /*size: usize,*/ align: usize) -> *mut Block {
		let bp_us = ptr as usize - size_of::<Block>();
		let bp = bp_us as *mut Block;
		//assert!( (*bp).state == BlockState::Used(size) );
		bp
	}

	/// UNSAFE: Code should ensure that self is uninitialised
	unsafe fn initialise(&mut self, size: usize) {
		::core::ptr::write(self, Block {
			state: BlockState::Free,
			size: size,
			});
		::core::ptr::write(self.tail(), BlockTail {
			head_ptr: self,
			});
	}

	fn tail(&mut self) -> &mut BlockTail {
		// SAFE: Mutably borrows self (which is eventually mut borrow of state)
		unsafe {
			&mut *((self as *const Block as usize + self.size - size_of::<BlockTail>()) as *mut BlockTail)
		}
	}
	fn next(&self) -> *mut Block {
		(self as *const Block as usize + self.size) as *mut Block
	}
	/// UNSAFE: Must ensure that 'self' isn't the first
	unsafe fn prev(&self) -> *mut Block {
		let pt = (self as *const Block as usize - size_of::<BlockTail>()) as *mut BlockTail;
		(*pt).head_ptr
	}

	fn self_free(&self) -> Option<&Self> {
		if let BlockState::Free = self.state {
			Some(self)
		}
		else {
			None
		}
	}

	fn get_data_ofs(&self, align: usize) -> usize {
		let myaddr = self as *const _ as usize;
		assert_eq!(myaddr % align_of::<Block>(), 0);
		let alignment_error = (myaddr + size_of::<Block>()) % align;
		// TODO: Asserts that the error is zero, because otherwise going from the data pointer to
		// metadata pointer is very hard
		assert_eq!(alignment_error, 0);
		let padding = if alignment_error > 0 { align - alignment_error } else { 0 };
		size_of::<Block>() + padding
	}

	fn capacity(&self, align: usize) -> usize {
		self.size - self.get_data_ofs(align) - size_of::<BlockTail>()
	}
	fn allocate(&mut self, size: usize, align: usize) -> *mut () {
		let dataofs = self.get_data_ofs(align);
		assert!(dataofs == size_of::<Block>());
		kernel_log!("size = {}, cap = {}", size, self.capacity(align));
		assert!(size <= self.capacity(align));

		self.state = BlockState::Used(size);
		(self as *mut Block as usize + dataofs) as *mut ()
	}
}

<commit_msg>Usermode - Fix introduced bug with heap limit check<commit_after>//
//
//
use prelude::v1::*;
use sync::Mutex;
use core::mem::{align_of,size_of};
use core::ptr::Unique;

#[cfg(arch__amd64)]
const HEAP_LIMITS: (usize,usize) = (0x1000_0000_0000, 0x7000_0000_0000);

static S_GLOBAL_HEAP: Mutex<AllocState> = Mutex::new(AllocState { start: 0 as *mut _, past_end: 0 as *mut _ } );

// Used by Box<T>
#[lang="exchange_malloc"]
pub unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8
{
	match S_GLOBAL_HEAP.lock().allocate(size, align)
	{
	Ok(x) => x as *mut u8,
	Err(_) => panic!("exchange_malloc({}, {}) out of memory", size, align),
	}
}
#[lang="exchange_free"]
pub unsafe fn exchange_free(ptr: *mut u8, _size: usize, align: usize)
{
	S_GLOBAL_HEAP.lock().deallocate(ptr as *mut (), /*size,*/ align)
}

pub struct Allocation<T>
{
	ptr: Unique<T>,
}

impl<T> Allocation<T>
{
	pub unsafe fn new(bytes: usize) -> Result<Allocation<T>, ()> {
		assert!(bytes == 0 || bytes >= size_of::<T>());
		S_GLOBAL_HEAP.lock().allocate(bytes, align_of::<T>()).map(|v| Allocation { ptr: Unique::new(v as *mut T) })
	}
	pub unsafe fn try_resize(&mut self, newbytes: usize) -> bool {
		let mut lh = S_GLOBAL_HEAP.lock();
		if *self.ptr == 1 as *mut T {
			self.ptr = Unique::new( lh.allocate(newbytes, align_of::<T>()).unwrap() as *mut T );
			true
		}
		else {
			lh.try_expand( *self.ptr as *mut (), newbytes, align_of::<T>() )
		}
	}
}
impl<T> ::core::ops::Deref for Allocation<T>
{
	type Target = *mut T;
	fn deref(&self) -> &*mut T {
		&*self.ptr
	}
}
impl<T> ::core::ops::Drop for Allocation<T>
{
	fn drop(&mut self) {
		// SAFE: Pointer and size are valid
		unsafe {
			S_GLOBAL_HEAP.lock().deallocate(*self.ptr as *mut (), align_of::<T>());
		}
	}
}

struct AllocState
{
	start: *mut Block,
	past_end: *mut Block,
//	first_free: BlockPointer,
}
unsafe impl Send for AllocState {}

struct Block
{
	size: usize,
	state: BlockState,
}
#[derive(PartialEq)]
enum BlockState
{
	Free,
	Used(usize),
}
struct BlockTail
{
	head_ptr: *mut Block,
}

impl AllocState
{
	pub fn allocate(&mut self, size: usize, align: usize) -> Result<*mut (), ()>
	{
		if size == 0 {
			return Ok( 1 as *mut () );
		}
		if self.start == self.past_end {
			self.extend_reservation(size);

			let block = self.last_block();
			return Ok(block.allocate(size, align));
		}

		for block in self.free_blocks()
		{
			// TODO: Block split
			if block.capacity(align) >= size {
				return Ok(block.allocate(size, align));
			}
		}

		let current_extra = self.last_block().self_free().map(|blk| blk.capacity(align)).unwrap_or(0);
		self.extend_reservation(size - current_extra);

		let block = self.last_block();
		return Ok(block.allocate(size, align));
	}
	/// Returns 'true' if expanding succeeded
	pub unsafe fn try_expand(&mut self, ptr: *mut (), size: usize, align: usize) -> bool {
		if size == 0 {
			// TODO: Resize down to 0?
			true
		}
		else if ptr == 1 as *mut () {
			false
		}
		else {
			let bp = Block::ptr_from_ptr(ptr, align);
			if (*bp).capacity(align) > size {
				(*bp).state = BlockState::Used( size );
				true
			}
			else {
				let n = (*bp).next();
				if n == self.past_end {
					false
				}
				else if (*n).self_free().is_some() {
					todo!("AllocState::try_expand - Next is free");
				}
				else {
					false
				}
			}
		}
	}
	pub unsafe fn deallocate(&mut self, ptr: *mut (), align: usize) {
		if ptr == 1 as *mut () {
			return ;
		}
		else {
			let bp = Block::ptr_from_ptr(ptr, align);
			(*bp).state = BlockState::Free;
			let np = (*bp).next();
			if np == self.past_end {
				// Final block
			}
			else if let Some(next) = (*np).self_free() {
				todo!("AllocState::deallocate - Merge with next block");
			}
			else {
				// Next block isn't free, can't merge
			}
		}
	}

	fn last_block(&mut self) -> &mut Block {
		// SAFE: Mutable borrow prevents any form of aliasing
		unsafe { &mut *(*self.past_end).prev() }
	}
	fn extend_reservation(&mut self, required_space: usize) {
		let npages = (required_space + size_of::<Block>() + size_of::<BlockTail>() + 0xFFF) >> 12;
		assert!(npages > 0);
		assert!(self.past_end != HEAP_LIMITS.1 as *mut Block);
		assert!(self.past_end as usize + (npages << 12) <= HEAP_LIMITS.1);	// TODO: This isn't an assert conditon, it's an OOM
		if self.start.is_null() {
			self.start = HEAP_LIMITS.0 as *mut Block;
			self.past_end = HEAP_LIMITS.0 as *mut Block;
		}

		// SAFE: Allocates only in controlled region.
		let cb = unsafe {
			::syscalls::memory::allocate(self.past_end as usize, npages).expect("Heap allocation failure");
			(*self.past_end).initialise(npages << 12);
			&mut *self.past_end
			};
		self.past_end = cb.next();
	}
	fn free_blocks(&mut self) -> FreeBlocks {
		FreeBlocks { cur: self.start, state: self, }
	}
}

struct FreeBlocks<'a>
{
	state: &'a mut AllocState,
	cur: *mut Block,
}
impl<'a> ::std::iter::Iterator for FreeBlocks<'a>
{
	type Item = &'a mut Block;
	fn next(&mut self) -> Option<Self::Item>
	{
		while self.cur != self.state.past_end
		{
			// TODO: Yields &mut to blocks, which have a method to get the next item
			// SAFE: (maybe) Only yields each block once... (but blocks can cursor)
			let block = unsafe {
				let bp = self.cur;
				self.cur = (*self.cur).next();
				&mut *bp
				};
			if let BlockState::Free = block.state {
				return Some(block);
			}
		}
		None
	}
}

impl Block
{
	unsafe fn ptr_from_ptr(ptr: *mut (), /*size: usize,*/ align: usize) -> *mut Block {
		let bp_us = ptr as usize - size_of::<Block>();
		let bp = bp_us as *mut Block;
		//assert!( (*bp).state == BlockState::Used(size) );
		bp
	}

	/// UNSAFE: Code should ensure that self is uninitialised
	unsafe fn initialise(&mut self, size: usize) {
		::core::ptr::write(self, Block {
			state: BlockState::Free,
			size: size,
			});
		::core::ptr::write(self.tail(), BlockTail {
			head_ptr: self,
			});
	}

	fn tail(&mut self) -> &mut BlockTail {
		// SAFE: Mutably borrows self (which is eventually mut borrow of state)
		unsafe {
			&mut *((self as *const Block as usize + self.size - size_of::<BlockTail>()) as *mut BlockTail)
		}
	}
	fn next(&self) -> *mut Block {
		(self as *const Block as usize + self.size) as *mut Block
	}
	/// UNSAFE: Must ensure that 'self' isn't the first
	unsafe fn prev(&self) -> *mut Block {
		let pt = (self as *const Block as usize - size_of::<BlockTail>()) as *mut BlockTail;
		(*pt).head_ptr
	}

	fn self_free(&self) -> Option<&Self> {
		if let BlockState::Free = self.state {
			Some(self)
		}
		else {
			None
		}
	}

	fn get_data_ofs(&self, align: usize) -> usize {
		let myaddr = self as *const _ as usize;
		assert_eq!(myaddr % align_of::<Block>(), 0);
		let alignment_error = (myaddr + size_of::<Block>()) % align;
		// TODO: Asserts that the error is zero, because otherwise going from the data pointer to
		// metadata pointer is very hard
		assert_eq!(alignment_error, 0);
		let padding = if alignment_error > 0 { align - alignment_error } else { 0 };
		size_of::<Block>() + padding
	}

	fn capacity(&self, align: usize) -> usize {
		self.size - self.get_data_ofs(align) - size_of::<BlockTail>()
	}
	fn allocate(&mut self, size: usize, align: usize) -> *mut () {
		let dataofs = self.get_data_ofs(align);
		assert!(dataofs == size_of::<Block>());
		kernel_log!("size = {}, cap = {}", size, self.capacity(align));
		assert!(size <= self.capacity(align));

		self.state = BlockState::Used(size);
		(self as *mut Block as usize + dataofs) as *mut ()
	}
}

<|endoftext|>