<commit_before>use image::ImageResult;
use mli::{Backward, Forward, Graph, Train};
use mli_conv::{Conv2, Conv2n, Conv3};
use mli_defconv::DefConv2InternalOffsets;
use mli_ndarray::{Map2One, Map3One, Reshape3to2};
use mli_relu::Blu;
use ndarray::{array, s, Array, Array2, Array3, OwnedRepr};
use ndarray_image::{open_gray_image, save_gray_image, save_image, Colors};
use rand_core::{RngCore, SeedableRng};
use rand_distr::{Distribution, Normal};
use rand_pcg::Pcg64;
use std::path::{Path, PathBuf};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "image", about = "Loads and convolves an image as a test")]
struct Opt {
    /// Number of epochs
    #[structopt(short = "t", default_value = "4000")]
    epochs: usize,
    /// Number of epochs per output image
    #[structopt(short = "s", default_value = "1")]
    show_every: usize,
    /// Pre-start learning rate (for first 10 epochs)
    #[structopt(short = "p", default_value = "0.1")]
    prestart_learning_rate: f32,
    /// Initial learning rate
    #[structopt(short = "i", default_value = "0.3")]
    initial_learning_rate: f32,
    /// Learning rate multiplier per epoch
    #[structopt(short = "m", default_value = "0.999")]
    learning_rate_multiplier: f32,
    /// Seed
    #[structopt(short = "z", default_value = "41")]
    seed: u32,
    /// Beta value for NAG
    #[structopt(short = "b", default_value = "0.9")]
    beta: f32,
    /// File to load
    #[structopt(parse(from_os_str))]
    file: PathBuf,
    /// Output directory
    #[structopt(parse(from_os_str))]
    output_dir: PathBuf,
}

fn sobel(image: &Array2<f32>) -> Array2<f32> {
    let down_filter = array![[-1.0, -2.0, -1.0], [0.0, 0.0, 0.0], [1.0, 2.0, 1.0],];
    let right_filter = down_filter.t().to_owned();
    let down = Conv2::new(down_filter).forward(&image).1;
    let right = Conv2::new(right_filter).forward(&image).1;
    (down.map(|f| f.powi(2)) + right.map(|f| f.powi(2))).map(|f| f.sqrt())
}

fn open_image(path: impl AsRef<Path>) -> ImageResult<Array2<f32>> {
    let image = open_gray_image(path)?;
    Ok(image.map(|&n| f32::from(n) / 255.0))
}

fn save_image_internal(path: impl AsRef<Path>, image: &Array2<f32>) -> ImageResult<()> {
    let image = image.map(|&n| num::clamp(n * 255.0, 0.0, 255.0) as u8);
    save_gray_image(path, image.view())
}

fn save_image_color_internal(path: impl AsRef<Path>, image: &Array3<f32>) -> ImageResult<()> {
    let image = image.map(|&n| num::clamp(n * 255.0, 0.0, 255.0) as u8);
    save_image(path, image.view(), Colors::Rgb)
}

fn main() -> ImageResult<()> {
    let opt = Opt::from_args();
    let image = open_image(opt.file)?;
    let sobel_image = sobel(&image);
    let conv_layers = 4;
    let filter_radius = 1usize;
    let filter_depth = 2usize;
    let filter_area = (filter_radius * 2 + 1).pow(2);
    let filter_volume = filter_area * filter_depth;
    let padding = (conv_layers * filter_radius - 1) as i32;
    #[allow(clippy::deref_addrof)]
    let expected = sobel_image.slice(s![padding..-padding, padding..-padding]);
    save_image_internal(opt.output_dir.join("actual.png"), &expected.to_owned())?;
    let mut prng = make_prng(opt.seed);
    let mut prng_defconv = make_prng(prng.next_u32());
    let mut random_defconv =
        |samples: usize, mean: f32, variance: f32| -> DefConv2InternalOffsets {
            // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
            DefConv2InternalOffsets::new(
                Array::from_iter(
                    Normal::new(mean / samples as f32, variance / samples as f32)
                        .unwrap()
                        .sample_iter(&mut prng_defconv)
                        .take(samples),
                )
                .into_shape(samples)
                .unwrap(),
                Array::from_iter(
                    Normal::new(0.0, 5.0)
                        .unwrap()
                        .sample_iter(&mut prng_defconv)
                        .take(2 * samples),
                )
                .into_shape((samples, 2))
                .unwrap(),
                [image.shape()[0], image.shape()[1]],
            )
        };
    let mut prng_2filter = make_prng(prng.next_u32());
    let mut random_2filter = |mean: f32, variance: f32| -> Conv2<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
        Conv2::new(
            Array::from_iter(
                Normal::new(mean / filter_area as f32, variance / filter_area as f32)
                    .unwrap()
                    .sample_iter(&mut prng_2filter)
                    .take(filter_area),
            )
            .into_shape((filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_2nfilter = make_prng(prng.next_u32());
    let mut random_2nfilter = |mean: f32, variance: f32| -> Conv2n<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
        Conv2n::new(
            Array::from_iter(
                Normal::new(mean / filter_area as f32, variance / filter_area as f32)
                    .unwrap()
                    .sample_iter(&mut prng_2nfilter)
                    .take(filter_volume),
            )
            .into_shape((filter_depth, filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_3filter = make_prng(prng.next_u32());
    let mut random_3filter = |mean: f32, variance: f32| -> Conv3<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the volume of the filter.
        Conv3::new(
            Array::from_iter(
                Normal::new(mean / filter_volume as f32, variance / filter_volume as f32)
                    .unwrap()
                    .sample_iter(&mut prng_3filter)
                    .take(filter_volume),
            )
            .into_shape((filter_depth, filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_blu = make_prng(prng.next_u32());
    let mut random_blu = |mean: f32, variance: f32| -> Blu {
        // Xavier initialize by changing the variance to be 1/N where N is the number of neurons.
        let distr = Normal::new(mean, variance).unwrap();
        Blu::new(distr.sample(&mut prng_blu), distr.sample(&mut prng_blu))
    };
    let mut generate_filter = || {
        random_defconv(10, 1.0, 0.5)
            .map(random_2nfilter(0.0, 4.0))
            .map(Map3One(random_blu(0.0, 0.5)))
            .map(random_3filter(0.0, 4.0))
            .map(Reshape3to2::new())
            .map(Map2One(random_blu(0.0, 0.5)))
            .map(random_2filter(0.0, 4.0))
            .map(Map2One(random_blu(0.0, 0.5)))
            .map(random_2filter(0.0, 4.0))
    };

    loop {
        let mut train_filter = generate_filter();
        let mut learn_rate = opt.initial_learning_rate;
        // Weird hack to initialize the momentum to zero without knowing the shape of the ndarray in advance.
        let mut momentum = {
            let (internal, output) = train_filter.forward(&image);
            train_filter.backward_train(&image, &internal, &output)
        };
        let mut last_loss = 0.0;
        momentum *= 0.0f32;
        for i in 0..opt.epochs {
            // Compute beta * momentum.
            momentum *= opt.beta;
            train_filter.train(&momentum);
            let (internal, output) = train_filter.forward(&image);
            // Show the image if the frame is divisible by show_every.
            if i % opt.show_every == 0 {
                // Plot the sample locations from the deformable conv net in the center.
                let mut splat: Array3<f32> = Array::zeros([1024, 1024, 3]);
                let defconv = &(((((((train_filter.0).0).0).0).0).0).0).0;
                // Get the weight distance so we can normalize the weights.
                let weight_distance = defconv
                    .def_conv
                    .weights
                    .iter()
                    .map(|&n| n.powi(2))
                    .sum::<f32>()
                    .sqrt();
                // Draw all the offsets and weights.
                for (offset, &weight) in defconv
                    .offsets
                    .0
                    .outer_iter()
                    .zip(defconv.def_conv.weights.iter())
                {
                    let splat_offset = [
                        (offset[0] * 32.0 + splat.shape()[0] as f32 / 2.0).round(),
                        (offset[1] * 32.0 + splat.shape()[1] as f32 / 2.0).round(),
                    ];
                    if splat_offset[0] >= 0.0
                        && splat_offset[0] < splat.shape()[0] as f32
                        && splat_offset[1] >= 0.0
                        && splat_offset[1] < splat.shape()[1] as f32
                    {
                        splat[[splat_offset[0] as usize, splat_offset[1] as usize, 0]] =
                            weight / weight_distance;
                        splat[[splat_offset[0] as usize, splat_offset[1] as usize, 1]] =
                            1.0 - weight / weight_distance;
                    }
                }
                // Draw the output for the epoch.
                save_image_internal(opt.output_dir.join(format!("epoch{:04}.png", i)), &output)?;
                // Draw the splat for the epoch.
                save_image_color_internal(
                    opt.output_dir.join(format!("splat{:04}.png", i)),
                    &splat,
                )?;
            }
            if i == opt.epochs - 1 {
                eprintln!("Finished!");
                return Ok(());
            }
            let output_len = output.len() as f32;
            let local_learn_rate = if i < 10 {
                opt.prestart_learning_rate
            } else {
                let llr = learn_rate;
                learn_rate *= opt.learning_rate_multiplier;
                llr
            };
            // Compute the loss for display only (we don't actually need the loss itself for backprop, just its derivative).
            let loss = (output.clone() - expected.view()).map(|n| n.powi(2)).sum() / output_len;
            eprintln!(
                "epoch {:04} loss: {} ({:+}%), learn_rate: {}",
                i,
                loss,
                (loss - last_loss) / last_loss * 100.0,
                local_learn_rate
            );
            last_loss = loss;
            if !loss.is_normal() {
                eprintln!("abnormal loss at epoch {}; starting over", i);
                break;
            }
            // The loss function is (n - t)^2, so 2*(n - t) is dE/df where
            // `E` is loss and `f` is output.
            let delta_loss = (output - expected).map(|n| 2.0 * n / output_len);
            // Compute the output delta.
            let output_delta = -local_learn_rate * delta_loss;
            // Compute the trainable variable delta.
            let mut train_delta = train_filter.backward_train(&image, &internal, &output_delta);
            // Make the train delta a small component.
            train_delta *= 1.0 - opt.beta;
            train_filter.train(&train_delta);
            // Add the small component to the momentum.
            momentum += train_delta;
        }
    }
}

fn make_prng(seed: u32) -> Pcg64 {
    Pcg64::from_seed([
        (seed & 0xFF) as u8,
        ((seed >> 8) & 0xFF) as u8,
        ((seed >> 16) & 0xFF) as u8,
        ((seed >> 24) & 0xFF) as u8,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
    ])
}
<commit_msg>change seed to best seed<commit_after>use image::ImageResult;
use mli::{Backward, Forward, Graph, Train};
use mli_conv::{Conv2, Conv2n, Conv3};
use mli_defconv::DefConv2InternalOffsets;
use mli_ndarray::{Map2One, Map3One, Reshape3to2};
use mli_relu::Blu;
use ndarray::{array, s, Array, Array2, Array3, OwnedRepr};
use ndarray_image::{open_gray_image, save_gray_image, save_image, Colors};
use rand_core::{RngCore, SeedableRng};
use rand_distr::{Distribution, Normal};
use rand_pcg::Pcg64;
use std::path::{Path, PathBuf};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "image", about = "Loads and convolves an image as a test")]
struct Opt {
    /// Number of epochs
    #[structopt(short = "t", default_value = "4000")]
    epochs: usize,
    /// Number of epochs per output image
    #[structopt(short = "s", default_value = "1")]
    show_every: usize,
    /// Pre-start learning rate (for first 10 epochs)
    #[structopt(short = "p", default_value = "0.1")]
    prestart_learning_rate: f32,
    /// Initial learning rate
    #[structopt(short = "i", default_value = "0.3")]
    initial_learning_rate: f32,
    /// Learning rate multiplier per epoch
    #[structopt(short = "m", default_value = "0.999")]
    learning_rate_multiplier: f32,
    /// Seed
    #[structopt(short = "z", default_value = "34")]
    seed: u32,
    /// Beta value for NAG
    #[structopt(short = "b", default_value = "0.9")]
    beta: f32,
    /// File to load
    #[structopt(parse(from_os_str))]
    file: PathBuf,
    /// Output directory
    #[structopt(parse(from_os_str))]
    output_dir: PathBuf,
}

fn sobel(image: &Array2<f32>) -> Array2<f32> {
    let down_filter = array![[-1.0, -2.0, -1.0], [0.0, 0.0, 0.0], [1.0, 2.0, 1.0],];
    let right_filter = down_filter.t().to_owned();
    let down = Conv2::new(down_filter).forward(&image).1;
    let right = Conv2::new(right_filter).forward(&image).1;
    (down.map(|f| f.powi(2)) + right.map(|f| f.powi(2))).map(|f| f.sqrt())
}

fn open_image(path: impl AsRef<Path>) -> ImageResult<Array2<f32>> {
    let image = open_gray_image(path)?;
    Ok(image.map(|&n| f32::from(n) / 255.0))
}

fn save_image_internal(path: impl AsRef<Path>, image: &Array2<f32>) -> ImageResult<()> {
    let image = image.map(|&n| num::clamp(n * 255.0, 0.0, 255.0) as u8);
    save_gray_image(path, image.view())
}

fn save_image_color_internal(path: impl AsRef<Path>, image: &Array3<f32>) -> ImageResult<()> {
    let image = image.map(|&n| num::clamp(n * 255.0, 0.0, 255.0) as u8);
    save_image(path, image.view(), Colors::Rgb)
}

fn main() -> ImageResult<()> {
    let opt = Opt::from_args();
    let image = open_image(opt.file)?;
    let sobel_image = sobel(&image);
    let conv_layers = 4;
    let filter_radius = 1usize;
    let filter_depth = 2usize;
    let filter_area = (filter_radius * 2 + 1).pow(2);
    let filter_volume = filter_area * filter_depth;
    let padding = (conv_layers * filter_radius - 1) as i32;
    #[allow(clippy::deref_addrof)]
    let expected = sobel_image.slice(s![padding..-padding, padding..-padding]);
    save_image_internal(opt.output_dir.join("actual.png"), &expected.to_owned())?;
    let mut prng = make_prng(opt.seed);
    let mut prng_defconv = make_prng(prng.next_u32());
    let mut random_defconv =
        |samples: usize, mean: f32, variance: f32| -> DefConv2InternalOffsets {
            // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
            DefConv2InternalOffsets::new(
                Array::from_iter(
                    Normal::new(mean / samples as f32, variance / samples as f32)
                        .unwrap()
                        .sample_iter(&mut prng_defconv)
                        .take(samples),
                )
                .into_shape(samples)
                .unwrap(),
                Array::from_iter(
                    Normal::new(0.0, 5.0)
                        .unwrap()
                        .sample_iter(&mut prng_defconv)
                        .take(2 * samples),
                )
                .into_shape((samples, 2))
                .unwrap(),
                [image.shape()[0], image.shape()[1]],
            )
        };
    let mut prng_2filter = make_prng(prng.next_u32());
    let mut random_2filter = |mean: f32, variance: f32| -> Conv2<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
        Conv2::new(
            Array::from_iter(
                Normal::new(mean / filter_area as f32, variance / filter_area as f32)
                    .unwrap()
                    .sample_iter(&mut prng_2filter)
                    .take(filter_area),
            )
            .into_shape((filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_2nfilter = make_prng(prng.next_u32());
    let mut random_2nfilter = |mean: f32, variance: f32| -> Conv2n<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the area of the filter.
        Conv2n::new(
            Array::from_iter(
                Normal::new(mean / filter_area as f32, variance / filter_area as f32)
                    .unwrap()
                    .sample_iter(&mut prng_2nfilter)
                    .take(filter_volume),
            )
            .into_shape((filter_depth, filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_3filter = make_prng(prng.next_u32());
    let mut random_3filter = |mean: f32, variance: f32| -> Conv3<OwnedRepr<f32>> {
        // Xavier initialize by changing the variance to be 1/N where N is the volume of the filter.
        Conv3::new(
            Array::from_iter(
                Normal::new(mean / filter_volume as f32, variance / filter_volume as f32)
                    .unwrap()
                    .sample_iter(&mut prng_3filter)
                    .take(filter_volume),
            )
            .into_shape((filter_depth, filter_radius * 2 + 1, filter_radius * 2 + 1))
            .unwrap(),
        )
    };
    let mut prng_blu = make_prng(prng.next_u32());
    let mut random_blu = |mean: f32, variance: f32| -> Blu {
        // Xavier initialize by changing the variance to be 1/N where N is the number of neurons.
        let distr = Normal::new(mean, variance).unwrap();
        Blu::new(distr.sample(&mut prng_blu), distr.sample(&mut prng_blu))
    };
    let mut generate_filter = || {
        random_defconv(10, 1.0, 0.5)
            .map(random_2nfilter(0.0, 4.0))
            .map(Map3One(random_blu(0.0, 0.5)))
            .map(random_3filter(0.0, 4.0))
            .map(Reshape3to2::new())
            .map(Map2One(random_blu(0.0, 0.5)))
            .map(random_2filter(0.0, 4.0))
            .map(Map2One(random_blu(0.0, 0.5)))
            .map(random_2filter(0.0, 4.0))
    };

    loop {
        let mut train_filter = generate_filter();
        let mut learn_rate = opt.initial_learning_rate;
        // Weird hack to initialize the momentum to zero without knowing the shape of the ndarray in advance.
        let mut momentum = {
            let (internal, output) = train_filter.forward(&image);
            train_filter.backward_train(&image, &internal, &output)
        };
        let mut last_loss = 0.0;
        momentum *= 0.0f32;
        for i in 0..opt.epochs {
            // Compute beta * momentum.
            momentum *= opt.beta;
            train_filter.train(&momentum);
            let (internal, output) = train_filter.forward(&image);
            // Show the image if the frame is divisible by show_every.
            if i % opt.show_every == 0 {
                // Plot the sample locations from the deformable conv net in the center.
                let mut splat: Array3<f32> = Array::zeros([1024, 1024, 3]);
                let defconv = &(((((((train_filter.0).0).0).0).0).0).0).0;
                // Get the weight distance so we can normalize the weights.
                let weight_distance = defconv
                    .def_conv
                    .weights
                    .iter()
                    .map(|&n| n.powi(2))
                    .sum::<f32>()
                    .sqrt();
                // Draw all the offsets and weights.
                for (offset, &weight) in defconv
                    .offsets
                    .0
                    .outer_iter()
                    .zip(defconv.def_conv.weights.iter())
                {
                    let splat_offset = [
                        (offset[0] * 32.0 + splat.shape()[0] as f32 / 2.0).round(),
                        (offset[1] * 32.0 + splat.shape()[1] as f32 / 2.0).round(),
                    ];
                    if splat_offset[0] >= 0.0
                        && splat_offset[0] < splat.shape()[0] as f32
                        && splat_offset[1] >= 0.0
                        && splat_offset[1] < splat.shape()[1] as f32
                    {
                        splat[[splat_offset[0] as usize, splat_offset[1] as usize, 0]] =
                            weight / weight_distance;
                        splat[[splat_offset[0] as usize, splat_offset[1] as usize, 1]] =
                            1.0 - weight / weight_distance;
                    }
                }
                // Draw the output for the epoch.
                save_image_internal(opt.output_dir.join(format!("epoch{:04}.png", i)), &output)?;
                // Draw the splat for the epoch.
                save_image_color_internal(
                    opt.output_dir.join(format!("splat{:04}.png", i)),
                    &splat,
                )?;
            }
            if i == opt.epochs - 1 {
                eprintln!("Finished!");
                return Ok(());
            }
            let output_len = output.len() as f32;
            let local_learn_rate = if i < 10 {
                opt.prestart_learning_rate
            } else {
                let llr = learn_rate;
                learn_rate *= opt.learning_rate_multiplier;
                llr
            };
            // Compute the loss for display only (we don't actually need the loss itself for backprop, just its derivative).
            let loss = (output.clone() - expected.view()).map(|n| n.powi(2)).sum() / output_len;
            eprintln!(
                "epoch {:04} loss: {} ({:+}%), learn_rate: {}",
                i,
                loss,
                (loss - last_loss) / last_loss * 100.0,
                local_learn_rate
            );
            last_loss = loss;
            if !loss.is_normal() {
                eprintln!("abnormal loss at epoch {}; starting over", i);
                break;
            }
            // The loss function is (n - t)^2, so 2*(n - t) is dE/df where
            // `E` is loss and `f` is output.
            let delta_loss = (output - expected).map(|n| 2.0 * n / output_len);
            // Compute the output delta.
            let output_delta = -local_learn_rate * delta_loss;
            // Compute the trainable variable delta.
            let mut train_delta = train_filter.backward_train(&image, &internal, &output_delta);
            // Make the train delta a small component.
            train_delta *= 1.0 - opt.beta;
            train_filter.train(&train_delta);
            // Add the small component to the momentum.
            momentum += train_delta;
        }
    }
}

fn make_prng(seed: u32) -> Pcg64 {
    Pcg64::from_seed([
        (seed & 0xFF) as u8,
        ((seed >> 8) & 0xFF) as u8,
        ((seed >> 16) & 0xFF) as u8,
        ((seed >> 24) & 0xFF) as u8,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
    ])
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::process::{Command, Stdio};
use std::io::prelude::*;
use std::io::BufReader;

const PROBLEMS_DIR: &'static str = "problems";
const INPUT_DIR: &'static str = "input";
const OUTPUT_DIR: &'static str = "output";

pub struct Problem {
	str_id: String,
	statement: Vec<String>,
	name: Vec<String>,
	authors: Vec<String>,
}

enum State {
	Default,
	Name,
	Statement,
	Authors,
}

impl Problem {
	pub fn new(str_id: String) -> Problem {
		let mut statement = vec![];
		let mut name = vec![];
		let mut authors = vec![];
		match File::open(format!("{}/{}/info.txt", PROBLEMS_DIR, str_id)) {
			Ok(file) => {
				let buf = BufReader::new(&file);
				let mut state = State::Default;
				for line in buf.lines()  {
					let l = line.unwrap();
					match l.as_ref() {
						"[authors]" => state = State::Authors,
						"[name]" => state = State::Name,
						"[statement]" => state = State::Statement,
						"[end]" => break,
						_ => {
							match state {
								State::Authors => authors.push(l.trim().to_string()),
								State::Name => name.push(l.trim().to_string()),
								State::Statement => statement.push(l.trim().to_string()),
								_ => continue
							}
						}
					}
				}
			},
			Err(err) => println!("Unable to load problem \"{}\" info: {}", str_id, err),
		};
		Problem {
			str_id: str_id,
			statement: statement,
			name: name,
			authors: authors,
		}
	}

	pub fn run(&self) {
		let builder = Command::new("rustc")
			.arg(format!("{}/{}/{}.rs", PROBLEMS_DIR, self.str_id, self.str_id))
			.arg("--out-dir")
			.arg(format!("{}/{}", PROBLEMS_DIR, self.str_id))
			.output()
			.unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

		if builder.status.success() {
			self.run_test(1);
		} else {
			let s = String::from_utf8_lossy(&builder.stderr);
			panic!("rustc failed to build problem {}:\n{}", self.str_id, s);
		}
	}

	fn run_test(&self, test_no: u32) {
		let mut input = File::open(format!("{}/{}/{}/{}.txt", PROBLEMS_DIR, self.str_id, INPUT_DIR, test_no))
			.unwrap_or_else(|e| { panic!("failed to load test \"{}\": {}", test_no, e) });
		let mut s = String::new();
		input.read_to_string(&mut s)
			.unwrap_or_else(|e| { panic!("couldn't read test \"{}\" file: {}", test_no, e) });

		let problem = Command::new(format!("{}/{}/{}", PROBLEMS_DIR, self.str_id, self.str_id))
			.stdin(Stdio::piped())
        	.stdout(Stdio::piped())
			.spawn()
			.unwrap_or_else(|e| { panic!("failed to execute test \"{}\": {}", test_no, e) });

        problem.stdin.unwrap().write_all(s.as_bytes())
        	.unwrap_or_else(|e| { panic!("couldn't write stdin test \"{}\": {}", test_no, e) });
        {
		    let mut s = String::new();
		    problem.stdout.unwrap().read_to_string(&mut s)
		    	.unwrap_or_else(|e| { panic!("couldn't read stdout test \"{}\": {}", test_no, e) });

		    let mut output = File::create(format!("{}/{}/{}/{}.txt", PROBLEMS_DIR, self.str_id, OUTPUT_DIR, test_no))
				.unwrap_or_else(|e| { panic!("failed to save test \"{}\" output: {}", test_no, e) });

			output.write_all(s.as_bytes())
				.unwrap_or_else(|e| { panic!("couldn't write test \"{}\" answer: {}", test_no, e) });
		}
	}
}

#[test]
fn read_test() {
	let p = Problem::new("test".to_string());
	assert_eq!(p.str_id, "test");
	assert_eq!(p.authors, vec!["Testhor", "qwerty"]);
	assert_eq!(p.name, vec!["Test name", "check"]);
	assert_eq!(p.statement, vec!["Test statement", "3", "4"]);
	p.run();
}<commit_msg>Add comments<commit_after>use std::fs::File;
use std::process::{Command, Stdio};
use std::io::prelude::*;
use std::io::BufReader;

const PROBLEMS_DIR: &'static str = "problems";
const INPUT_DIR: &'static str = "input";
const OUTPUT_DIR: &'static str = "output";

pub struct Problem {
	str_id: String,
	statement: Vec<String>,
	name: Vec<String>,
	authors: Vec<String>,
}

enum State {
	Default,
	Name,
	Statement,
	Authors,
}

impl Problem {
	pub fn new(str_id: String) -> Problem {
		let mut statement = vec![];
		let mut name = vec![];
		let mut authors = vec![];
		match File::open(format!("{}/{}/info.txt", PROBLEMS_DIR, str_id)) {
			Ok(file) => {
				let buf = BufReader::new(&file);
				let mut state = State::Default;
				for line in buf.lines()  {
					let l = line.unwrap();
					match l.as_ref() {
						"[authors]" => state = State::Authors,
						"[name]" => state = State::Name,
						"[statement]" => state = State::Statement,
						"[end]" => break,
						_ => {
							match state {
								State::Authors => authors.push(l.trim().to_string()),
								State::Name => name.push(l.trim().to_string()),
								State::Statement => statement.push(l.trim().to_string()),
								_ => continue
							}
						}
					}
				}
			},
			Err(err) => println!("Unable to load problem \"{}\" info: {}", str_id, err),
		};
		Problem {
			str_id: str_id,
			statement: statement,
			name: name,
			authors: authors,
		}
	}

	pub fn run(&self) {
		let builder = Command::new("rustc")
			.arg(format!("{}/{}/{}.rs", PROBLEMS_DIR, self.str_id, self.str_id))
			.arg("--out-dir")
			.arg(format!("{}/{}", PROBLEMS_DIR, self.str_id))
			.output()
			.unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

		if builder.status.success() {
			self.run_test(1);
		} else {
			let s = String::from_utf8_lossy(&builder.stderr);
			panic!("rustc failed to build problem {}:\n{}", self.str_id, s);
		}
	}

	fn run_test(&self, test_no: u32) {
		let mut input = File::open(format!("{}/{}/{}/{}.txt", PROBLEMS_DIR, self.str_id, INPUT_DIR, test_no))
			.unwrap_or_else(|e| { panic!("failed to load test \"{}\": {}", test_no, e) });
		let mut s = String::new(); // passing throw String coz as_slice still unstable
		input.read_to_string(&mut s)
			.unwrap_or_else(|e| { panic!("couldn't read test \"{}\" file: {}", test_no, e) });

		let problem = Command::new(format!("{}/{}/{}", PROBLEMS_DIR, self.str_id, self.str_id))
			.stdin(Stdio::piped())
        	.stdout(Stdio::piped())
			.spawn()
			.unwrap_or_else(|e| { panic!("failed to execute test \"{}\": {}", test_no, e) });

        problem.stdin.unwrap().write_all(s.as_bytes())
        	.unwrap_or_else(|e| { panic!("couldn't write stdin test \"{}\": {}", test_no, e) });
        {
		    let mut s = String::new(); // passing throw String coz as_slice still unstable
		    problem.stdout.unwrap().read_to_string(&mut s)
		    	.unwrap_or_else(|e| { panic!("couldn't read stdout test \"{}\": {}", test_no, e) });

		    let mut output = File::create(format!("{}/{}/{}/{}.txt", PROBLEMS_DIR, self.str_id, OUTPUT_DIR, test_no))
				.unwrap_or_else(|e| { panic!("failed to save test \"{}\" output: {}", test_no, e) });

			output.write_all(s.as_bytes())
				.unwrap_or_else(|e| { panic!("couldn't write test \"{}\" answer: {}", test_no, e) });
		}
	}
}

#[test]
fn read_test() {
	let p = Problem::new("test".to_string());
	assert_eq!(p.str_id, "test");
	assert_eq!(p.authors, vec!["Testhor", "qwerty"]);
	assert_eq!(p.name, vec!["Test name", "check"]);
	assert_eq!(p.statement, vec!["Test statement", "3", "4"]);
	p.run();
}<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]

use core::prelude::*;

bitflags!(
	flags Features: u64 {
		const Fpu = 1 << 0,
		const Virtual8086 = 1 << 1,
		const DebugExtension = 1 << 2,
		const PageSizeExtension = 1 << 3,
		const TimeStampCounter = 1 << 4,
		const ModelSpecificRegister = 1 << 5,
		const PhysicalAddressExtension = 1 << 6,
		const MachineCheckException = 1 << 7,
		const Cx8 = 1 << 8, // CMPXCHG8
		const Apic = 1 << 9,
		const SysEnter = 1 << 11,
		const MemoryTypeRange = 1 << 12,
		const PageGlobal = 1 << 13,
		const MachineCheckArchitecture = 1 << 14,
		const CMov = 1 << 15,
		const PageAttributeTable = 1 << 16,
		const PageSizeExtension36 = 1 << 17,
		const ProcessorSerial = 1 << 18,
		const CacheFlush = 1 << 19,
		const DebugStore = 1 << 21,
		const Acpi = 1 << 22,
		const Mmx = 1 << 23,
		const FxSave = 1 << 24,
		const Sse = 1 << 25,
		const Sse2 = 1 << 26,
		const SelfSnoop = 1 << 27,
		const HyperThreading = 1 << 28,
		const ThermalMonitor = 1 << 29,
		const Ia64 = 1 << 30,
		const PendingBreak = 1 << 31,

		const Sse3 = 1 << (32 + 0),
		const PclMulQdq = 1 << (32 + 1), // what
		const DebugStore64 = 1 << (32 + 2),
		const Monitor = 1 << (32 + 3),
		const CplDebugStore = 1 << (32 + 4),
		const Vmx = 1 << (32 + 5),
		const SaferMode = 1 << (32 + 6),
		const EnhancedSpeedStep = 1 << (32 + 7),
		const ThermalMonitor2 = 1 << (32 + 8),
		const Ssse3 = 1 << (32 + 9),
		const L1ContextId = 1 << (32 + 10),
		const Fma = 1 << (32 + 12),
		const Cx16 = 1 << (32 + 13), // CMPXCHG16B
		const Xtpr = 1 << (32 + 14), // I have no idea what this is
		const PerformanceMonitor = 1 << (32 + 15),
		const ProcessContextId = 1 << (32 + 17),
		const DirectCache = 1 << (32 + 18),
		const Sse41 = 1 << (32 + 19),
		const Sse42 = 1 << (32 + 20),
		const X2Apic = 1 << (32 + 21),
		const MovBe = 1 << (32 + 22),
		const PopulationCount = 1 << (32 + 23),
		const TscDeadline = 1 << (32 + 24),
		const AesNi = 1 << (32 + 25),
		const XSave = 1 << (32 + 26),
		const OsXSave = 1 << (32 + 27),
		const Avx = 1 << (32 + 28),
		const HalfPrecision = 1 << (32 + 29),
		const HwRandom = 1 << (32 + 30)
	}
);

#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
pub enum Exception {
	DivisionByZero = 0,
	Debug = 1,
	Nmi = 2,
	Breakpoint = 3,
	Overflow = 4,
	Bounds = 5,
	InvalidOpcode = 6,
	NotAvailable = 7,
	DoubleFault = 8,
	CoprocessorSegment = 9,
	Tss = 10,
	NotPresent = 11,
	StackSegment = 12,
	GeneralProtection = 13,
	PageFault = 14,
	Fpu = 16,
	Alignment = 17,
	MachineCheck = 18,
	Simd = 19,
	Virtualization = 20,
	Security = 30
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Msr {
	ApicBase = 0x1B
}

#[inline(always)]
pub fn cpuid(function: u32) -> (u32, u32, u32, u32) {
	unsafe {
		let (eax, ebx, ecx, edx): (u32, u32, u32, u32);
		asm!("cpuid" : "={eax}"(eax), "={ebx}"(ebx), "={ecx}"(ecx), "={edx}"(edx) : "{eax}"(function));
		(eax, ebx, ecx, edx)
	}
}

#[inline(always)]
pub fn supports() -> Features {
	let (_, _, feature_ecx, feature_edx) = cpuid(1);
	Features {
		bits: ((feature_ecx as u64) << 32) | (feature_edx as u64)
	}
}

#[inline(always)]
pub unsafe fn read_msr(msr: Msr) -> u64 {
	let (r1, r2): (u32, u32);
	asm!("rdmsr" : "={eax}"(r1), "={edx}"(r2) : "{ecx}"(msr as u32) :: "intel");
	r1 as u64 | ((r2 as u64) << 32)
}

#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
pub enum PrivilegeLevel {
	Ring0 = 0,
	Ring1 = 1,
	Ring2 = 2,
	Ring3 = 3,
}

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(C, packed)]
pub struct SegmentSelector {
	data: u16
}

impl SegmentSelector {
	#[inline(always)]
	pub fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector {
		SegmentSelector {
			data: index << 3 | rpl as u16
		}
	}

	pub fn bits(&self) -> u16 {
		self.data
	}
}

#[inline(always)]
pub unsafe fn set_tr(selector: SegmentSelector) {
	asm!("ltr $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ss(selector: SegmentSelector) {
	asm!("mov ss, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ds(selector: SegmentSelector) {
	asm!("mov ds, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_es(selector: SegmentSelector) {
	asm!("mov es, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_gs(selector: SegmentSelector) {
	asm!("mov gs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_fs(selector: SegmentSelector) {
	asm!("mov fs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_cs(selector: SegmentSelector) {
	asm!("push $0;
		push $$1f
		lret;
		1:" :: "ri"(selector.bits() as u32) :: "volatile");
}

#[inline(always)]
pub unsafe fn enable_interrupts() {
	asm!("sti" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn disable_interrupts() {
	asm!("cli" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn halt() {
	asm!("hlt" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out8(port: u16, value: u8) {
	asm!("out $0, $1" :: "{dx}"(port), "{al}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out16(port: u16, value: u16) {
	asm!("out $0, $1" :: "{dx}"(port), "{ax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out32(port: u16, value: u32) {
	asm!("out $0, $1" :: "{dx}"(port), "{eax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn outs8(port: u16, buf: &[u8]) {
	asm!("rep outsb dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn outs16(port: u16, buf: &[u16]) {
	asm!("rep outsw dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn outs32(port: u16, buf: &[u32]) {
	asm!("rep outsd dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn in8(port: u16) -> u8 {
	let r: u8;
	asm!("in $0, $1" : "={al}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in16(port: u16) -> u16 {
	let r: u16;
	asm!("in $0, $1" : "={ax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in32(port: u16) -> u32 {
	let r: u32;
	asm!("in $0, $1" : "={eax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn ins8(port: u16, buf: &mut [u8]) {
	asm!("rep insb [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn ins16(port: u16, buf: &mut [u16]) {
	asm!("rep insw [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn ins32(port: u16, buf: &mut [u32]) {
	asm!("rep insd [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}
<commit_msg>Fix compiling for x86_64<commit_after>#![allow(non_upper_case_globals)]

use core::prelude::*;

bitflags!(
	flags Features: u64 {
		const Fpu = 1 << 0,
		const Virtual8086 = 1 << 1,
		const DebugExtension = 1 << 2,
		const PageSizeExtension = 1 << 3,
		const TimeStampCounter = 1 << 4,
		const ModelSpecificRegister = 1 << 5,
		const PhysicalAddressExtension = 1 << 6,
		const MachineCheckException = 1 << 7,
		const Cx8 = 1 << 8, // CMPXCHG8
		const Apic = 1 << 9,
		const SysEnter = 1 << 11,
		const MemoryTypeRange = 1 << 12,
		const PageGlobal = 1 << 13,
		const MachineCheckArchitecture = 1 << 14,
		const CMov = 1 << 15,
		const PageAttributeTable = 1 << 16,
		const PageSizeExtension36 = 1 << 17,
		const ProcessorSerial = 1 << 18,
		const CacheFlush = 1 << 19,
		const DebugStore = 1 << 21,
		const Acpi = 1 << 22,
		const Mmx = 1 << 23,
		const FxSave = 1 << 24,
		const Sse = 1 << 25,
		const Sse2 = 1 << 26,
		const SelfSnoop = 1 << 27,
		const HyperThreading = 1 << 28,
		const ThermalMonitor = 1 << 29,
		const Ia64 = 1 << 30,
		const PendingBreak = 1 << 31,

		const Sse3 = 1 << (32 + 0),
		const PclMulQdq = 1 << (32 + 1), // what
		const DebugStore64 = 1 << (32 + 2),
		const Monitor = 1 << (32 + 3),
		const CplDebugStore = 1 << (32 + 4),
		const Vmx = 1 << (32 + 5),
		const SaferMode = 1 << (32 + 6),
		const EnhancedSpeedStep = 1 << (32 + 7),
		const ThermalMonitor2 = 1 << (32 + 8),
		const Ssse3 = 1 << (32 + 9),
		const L1ContextId = 1 << (32 + 10),
		const Fma = 1 << (32 + 12),
		const Cx16 = 1 << (32 + 13), // CMPXCHG16B
		const Xtpr = 1 << (32 + 14), // I have no idea what this is
		const PerformanceMonitor = 1 << (32 + 15),
		const ProcessContextId = 1 << (32 + 17),
		const DirectCache = 1 << (32 + 18),
		const Sse41 = 1 << (32 + 19),
		const Sse42 = 1 << (32 + 20),
		const X2Apic = 1 << (32 + 21),
		const MovBe = 1 << (32 + 22),
		const PopulationCount = 1 << (32 + 23),
		const TscDeadline = 1 << (32 + 24),
		const AesNi = 1 << (32 + 25),
		const XSave = 1 << (32 + 26),
		const OsXSave = 1 << (32 + 27),
		const Avx = 1 << (32 + 28),
		const HalfPrecision = 1 << (32 + 29),
		const HwRandom = 1 << (32 + 30)
	}
);

#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
pub enum Exception {
	DivisionByZero = 0,
	Debug = 1,
	Nmi = 2,
	Breakpoint = 3,
	Overflow = 4,
	Bounds = 5,
	InvalidOpcode = 6,
	NotAvailable = 7,
	DoubleFault = 8,
	CoprocessorSegment = 9,
	Tss = 10,
	NotPresent = 11,
	StackSegment = 12,
	GeneralProtection = 13,
	PageFault = 14,
	Fpu = 16,
	Alignment = 17,
	MachineCheck = 18,
	Simd = 19,
	Virtualization = 20,
	Security = 30
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Msr {
	ApicBase = 0x1B
}

#[inline(always)]
pub fn cpuid(function: u32) -> (u32, u32, u32, u32) {
	unsafe {
		let (eax, ebx, ecx, edx): (u32, u32, u32, u32);
		asm!("cpuid" : "={eax}"(eax), "={ebx}"(ebx), "={ecx}"(ecx), "={edx}"(edx) : "{eax}"(function));
		(eax, ebx, ecx, edx)
	}
}

#[inline(always)]
pub fn supports() -> Features {
	let (_, _, feature_ecx, feature_edx) = cpuid(1);
	Features {
		bits: ((feature_ecx as u64) << 32) | (feature_edx as u64)
	}
}

#[inline(always)]
pub unsafe fn read_msr(msr: Msr) -> u64 {
	let (r1, r2): (u32, u32);
	asm!("rdmsr" : "={eax}"(r1), "={edx}"(r2) : "{ecx}"(msr as u32) :: "intel");
	r1 as u64 | ((r2 as u64) << 32)
}

#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
pub enum PrivilegeLevel {
	Ring0 = 0,
	Ring1 = 1,
	Ring2 = 2,
	Ring3 = 3,
}

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(C, packed)]
pub struct SegmentSelector {
	data: u16
}

impl SegmentSelector {
	#[inline(always)]
	pub fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector {
		SegmentSelector {
			data: index << 3 | rpl as u16
		}
	}

	pub fn bits(&self) -> u16 {
		self.data
	}
}

#[inline(always)]
pub unsafe fn set_tr(selector: SegmentSelector) {
	asm!("ltr $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ss(selector: SegmentSelector) {
	asm!("mov ss, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_ds(selector: SegmentSelector) {
	asm!("mov ds, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_es(selector: SegmentSelector) {
	asm!("mov es, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_gs(selector: SegmentSelector) {
	asm!("mov gs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_fs(selector: SegmentSelector) {
	asm!("mov fs, $0" :: "r"(selector.bits()) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn set_cs(selector: SegmentSelector) {
	asm!("push $0;
		push $$1f
		lret;
		1:" :: "ri"(selector.bits() as usize) :: "volatile");
}

#[inline(always)]
pub unsafe fn enable_interrupts() {
	asm!("sti" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn disable_interrupts() {
	asm!("cli" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn halt() {
	asm!("hlt" :::: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out8(port: u16, value: u8) {
	asm!("out $0, $1" :: "{dx}"(port), "{al}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out16(port: u16, value: u16) {
	asm!("out $0, $1" :: "{dx}"(port), "{ax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn out32(port: u16, value: u32) {
	asm!("out $0, $1" :: "{dx}"(port), "{eax}"(value) :: "volatile", "intel");
}

#[inline(always)]
pub unsafe fn outs8(port: u16, buf: &[u8]) {
	asm!("rep outsb dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn outs16(port: u16, buf: &[u16]) {
	asm!("rep outsw dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn outs32(port: u16, buf: &[u32]) {
	asm!("rep outsd dx, [esi]" :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn in8(port: u16) -> u8 {
	let r: u8;
	asm!("in $0, $1" : "={al}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in16(port: u16) -> u16 {
	let r: u16;
	asm!("in $0, $1" : "={ax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn in32(port: u16) -> u32 {
	let r: u32;
	asm!("in $0, $1" : "={eax}"(r) : "{dx}"(port) :: "intel");
	r
}

#[inline(always)]
pub unsafe fn ins8(port: u16, buf: &mut [u8]) {
	asm!("rep insb [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn ins16(port: u16, buf: &mut [u16]) {
	asm!("rep insw [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}

#[inline(always)]
pub unsafe fn ins32(port: u16, buf: &mut [u32]) {
	asm!("rep insd [edi], dx" :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) : "ecx", "edi" : "intel");
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gl;
extern crate libc;

use log;
use std;
use a = super::attrib;
use std::fmt;
use std::str;
use std::collections::HashSet;

mod rast;
mod shade;

pub type Buffer         = gl::types::GLuint;
pub type ArrayBuffer    = gl::types::GLuint;
pub type Shader         = gl::types::GLuint;
pub type Program        = gl::types::GLuint;
pub type FrameBuffer    = gl::types::GLuint;
pub type Surface        = gl::types::GLuint;
pub type Texture        = gl::types::GLuint;
pub type Sampler        = gl::types::GLuint;

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

unsafe fn get_static_string(name: gl::types::GLenum) -> &'static str {
    let ptr = gl::GetString(name) as *const i8;
    debug_assert!(!ptr.is_null());
    str::raw::c_str_to_static_slice(ptr)
}

#[deriving(Eq, PartialEq)]
pub enum Version {
    Version(uint, uint, Option<uint>, &'static str),
    VersionUnknown(&'static str),
}

impl Version {
    fn parse(src: &'static str) -> Version {
        let (version, vendor_info) = src.find(' ').map_or((src, ""), |i| {
            (src.slice_to(i), src.slice_from(i + 1))
        });

        let mut it = version.split('.');
        let major = it.next().and_then(|x| from_str(x));
        let minor = it.next().and_then(|x| from_str(x));
        let revision = it.next().and_then(|x| from_str(x));
        let tail = it.next();

        match (major, minor, revision, tail) {
            (Some(major), Some(minor), revision, None) =>
                Version(major, minor, revision, vendor_info),
            _ => VersionUnknown(src),
        }
    }

    pub fn is_unknown(&self) -> bool {
        match *self { VersionUnknown(_) => true, _ => false }
    }
}

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

#[deriving(Show)]
pub struct Info {
    pub vendor: &'static str,
    pub renderer: &'static str,
    pub version: Version,
    pub shading_language: Version,
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn new() -> Info {
        let num_exts = get_uint(gl::NUM_EXTENSIONS) as gl::types::GLuint;
        let info = unsafe {
            Info {
                vendor: get_static_string(gl::VENDOR),
                renderer: get_static_string(gl::RENDERER),
                version: Version::parse(get_static_string(gl::VERSION)),
                shading_language: Version::parse(get_static_string(gl::SHADING_LANGUAGE_VERSION)),
                extensions: range(0, num_exts).map(|i| {
                    str::raw::c_str_to_static_slice(
                        gl::GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }).collect(),
            }
        };
        info!("Vendor: {}", info.vendor);
        info!("Renderer: {}", info.renderer);
        info!("Version: {}", info.version);
        info!("Shading Language: {}", info.shading_language);
        info!("Loaded Extensions:")
        for extension in info.extensions.iter() {
            info!("- {}", *extension);
        }
        info
    }

    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(&s)
    }
}

pub struct GlBackEnd {
    caps: super::Capabilities,
    info: Info,
}

impl GlBackEnd {
    pub fn new(provider: &super::GlProvider) -> GlBackEnd {
        gl::load_with(|s| provider.get_proc_address(s));
        let info = Info::new();
        let caps = super::Capabilities {
            shader_model: shade::get_model(),
            max_draw_buffers: get_uint(gl::MAX_DRAW_BUFFERS),
            max_texture_size: get_uint(gl::MAX_TEXTURE_SIZE),
            max_vertex_attributes: get_uint(gl::MAX_VERTEX_ATTRIBS),
            uniform_block_supported: info.is_extension_supported("GL_ARB_uniform_buffer_object"),
            array_buffer_supported: info.is_extension_supported("GL_ARB_vertex_array_object"),
        };
        GlBackEnd {
            caps: caps,
            info: info,
        }
    }

    #[allow(dead_code)]
    fn check(&mut self) {
        debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
    }

    pub fn get_info<'a>(&'a self) -> &'a Info {
        &self.info
    }
}

impl super::ApiBackEnd for GlBackEnd {
    fn get_capabilities<'a>(&'a self) -> &'a super::Capabilities {
        &self.caps
    }

    fn create_buffer(&mut self) -> Buffer {
        let mut name = 0 as Buffer;
        unsafe{
            gl::GenBuffers(1, &mut name);
        }
        info!("\tCreated buffer {}", name);
        name
    }

    fn create_array_buffer(&mut self) -> Result<ArrayBuffer, ()> {
        if self.caps.array_buffer_supported {
            let mut name = 0 as ArrayBuffer;
            unsafe{
                gl::GenVertexArrays(1, &mut name);
            }
            info!("\tCreated array buffer {}", name);
            Ok(name)
        } else {
            error!("\tarray buffer creation unsupported, ignored")
            Err(())
        }
    }

    fn create_shader(&mut self, stage: super::shade::Stage, code: super::shade::ShaderSource) -> Result<Shader, super::shade::CreateShaderError> {
        let (name, info) = shade::create_shader(stage, code, self.get_capabilities().shader_model);
        info.map(|info| {
            let level = if name.is_err() { log::ERROR } else { log::WARN };
            log!(level, "\tShader compile log: {}", info);
        });
        name
    }

    fn create_program(&mut self, shaders: &[Shader]) -> Result<super::shade::ProgramMeta, ()> {
        let (meta, info) = shade::create_program(&self.caps, shaders);
        info.map(|info| {
            let level = if meta.is_err() { log::ERROR } else { log::WARN };
            log!(level, "\tProgram link log: {}", info);
        });
        meta
    }

    fn create_frame_buffer(&mut self) -> FrameBuffer {
        let mut name = 0 as FrameBuffer;
        unsafe{
            gl::GenFramebuffers(1, &mut name);
        }
        info!("\tCreated frame buffer {}", name);
        name
    }

    fn update_buffer<T>(&mut self, buffer: Buffer, data: &[T], usage: super::BufferUsage) {
        gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
        let size = (data.len() * std::mem::size_of::<T>()) as gl::types::GLsizeiptr;
        let raw = data.as_ptr() as *const gl::types::GLvoid;
        let usage = match usage {
            super::UsageStatic  => gl::STATIC_DRAW,
            super::UsageDynamic => gl::DYNAMIC_DRAW,
            super::UsageStream  => gl::STREAM_DRAW,
        };
        unsafe{
            gl::BufferData(gl::ARRAY_BUFFER, size, raw, usage);
        }
    }

    fn process(&mut self, request: super::Request) {
        match request {
            super::CastClear(data) => {
                let mut flags = match data.color {
                    //gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
                    Some(super::target::Color([r,g,b,a])) => {
                        gl::ClearColor(r, g, b, a);
                        gl::COLOR_BUFFER_BIT
                    },
                    None => 0 as gl::types::GLenum
                };
                data.depth.map(|value| {
                    gl::DepthMask(gl::TRUE);
                    gl::ClearDepth(value as gl::types::GLclampd);
                    flags |= gl::DEPTH_BUFFER_BIT;
                });
                data.stencil.map(|value| {
                    gl::StencilMask(-1);
                    gl::ClearStencil(value as gl::types::GLint);
                    flags |= gl::STENCIL_BUFFER_BIT;
                });
                gl::Clear(flags);
            },
            super::CastBindProgram(program) => {
                gl::UseProgram(program);
            },
            super::CastBindArrayBuffer(array_buffer) => {
                if self.caps.array_buffer_supported {
                    gl::BindVertexArray(array_buffer);
                } else {
                    error!("Ignored unsupported GL Request: {}", request)
                }
            },
            super::CastBindAttribute(slot, buffer, count, el_type, stride, offset) => {
                let gl_type = match el_type {
                    a::Int(_, a::U8, a::Unsigned)  => gl::UNSIGNED_BYTE,
                    a::Int(_, a::U8, a::Signed)    => gl::BYTE,
                    a::Int(_, a::U16, a::Unsigned) => gl::UNSIGNED_SHORT,
                    a::Int(_, a::U16, a::Signed)   => gl::SHORT,
                    a::Int(_, a::U32, a::Unsigned) => gl::UNSIGNED_INT,
                    a::Int(_, a::U32, a::Signed)   => gl::INT,
                    a::Float(_, a::F16) => gl::HALF_FLOAT,
                    a::Float(_, a::F32) => gl::FLOAT,
                    a::Float(_, a::F64) => gl::DOUBLE,
                    _ => {
                        error!("Unsupported element type: {}", el_type);
                        return
                    }
                };
                gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
                let offset = offset as *const gl::types::GLvoid;
                match el_type {
                    a::Int(a::IntRaw, _, _) => unsafe {
                        gl::VertexAttribIPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            stride as gl::types::GLint, offset);
                    },
                    a::Int(sub, _, _) => unsafe {
                        gl::VertexAttribPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            if sub == a::IntNormalized {gl::TRUE} else {gl::FALSE},
                            stride as gl::types::GLint, offset);
                    },
                    a::Float(a::FloatDefault, _) => unsafe {
                        gl::VertexAttribPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type, gl::FALSE,
                            stride as gl::types::GLint, offset);
                    },
                    a::Float(a::FloatPrecision, _) => unsafe {
                        gl::VertexAttribLPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            stride as gl::types::GLint, offset);
                    },
                    _ => ()
                }
                gl::EnableVertexAttribArray(slot as gl::types::GLuint);
            },
            super::CastBindIndex(buffer) => {
                gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buffer);
            },
            super::CastBindFrameBuffer(frame_buffer) => {
                gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, frame_buffer);
            },
            super::CastBindTarget(target, plane) => {
                let attachment = match target {
                    super::target::TargetColor(index) =>
                        gl::COLOR_ATTACHMENT0 + (index as gl::types::GLenum),
                    super::target::TargetDepth => gl::DEPTH_ATTACHMENT,
                    super::target::TargetStencil => gl::STENCIL_ATTACHMENT,
                    super::target::TargetDepthStencil => gl::DEPTH_STENCIL_ATTACHMENT,
                };
                match plane {
                    super::target::PlaneEmpty => gl::FramebufferRenderbuffer
                        (gl::DRAW_FRAMEBUFFER, attachment, gl::RENDERBUFFER, 0),
                    super::target::PlaneSurface(name) => gl::FramebufferRenderbuffer
                        (gl::DRAW_FRAMEBUFFER, attachment, gl::RENDERBUFFER, name),
                    super::target::PlaneTexture(name, level) => gl::FramebufferTexture
                        (gl::DRAW_FRAMEBUFFER, attachment, name, level as gl::types::GLint),
                    super::target::PlaneTextureLayer(name, level, layer) => gl::FramebufferTextureLayer
                        (gl::DRAW_FRAMEBUFFER, attachment, name, level as gl::types::GLint, layer as gl::types::GLint),
                }
            },
            super::CastBindUniformBlock(program, index, loc, buffer) => {
                gl::UniformBlockBinding(program, index as gl::types::GLuint, loc as gl::types::GLuint);
                gl::BindBufferBase(gl::UNIFORM_BUFFER, loc as gl::types::GLuint, buffer);
            },
            super::CastBindUniform(loc, uniform) => {
                shade::bind_uniform(loc as gl::types::GLint, uniform);
            },
            super::CastPrimitiveState(prim) => {
                rast::bind_primitive(prim);
            },
            super::CastDepthState(depth) => {
                rast::bind_depth(depth);
            },
            super::CastBlendState(blend) => {
                rast::bind_blend(blend);
            },
            super::CastUpdateBuffer(buffer, data) => {
                self.update_buffer(buffer, data.as_slice(), super::UsageDynamic);
            },
            super::CastDraw(start, count) => {
                gl::DrawArrays(gl::TRIANGLES,
                    start as gl::types::GLsizei,
                    count as gl::types::GLsizei);
                self.check();
            },
            super::CastDrawIndexed(start, count) => {
                let offset = start * (std::mem::size_of::<u16>() as u16);
                unsafe {
                    gl::DrawElements(gl::TRIANGLES,
                        count as gl::types::GLsizei,
                        gl::UNSIGNED_SHORT,
                        offset as *const gl::types::GLvoid);
                }
                self.check();
            },
            _ => fail!("Unknown GL request: {}", request)
        }
    }
}
<commit_msg>Implement fallback for querying extensions when the gl version is < 3.2<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gl;
extern crate libc;

use log;
use std;
use a = super::attrib;
use std::fmt;
use std::str;
use std::collections::HashSet;

mod rast;
mod shade;

pub type Buffer         = gl::types::GLuint;
pub type ArrayBuffer    = gl::types::GLuint;
pub type Shader         = gl::types::GLuint;
pub type Program        = gl::types::GLuint;
pub type FrameBuffer    = gl::types::GLuint;
pub type Surface        = gl::types::GLuint;
pub type Texture        = gl::types::GLuint;
pub type Sampler        = gl::types::GLuint;

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

unsafe fn get_static_string(name: gl::types::GLenum) -> &'static str {
    let ptr = gl::GetString(name) as *const i8;
    debug_assert!(!ptr.is_null());
    str::raw::c_str_to_static_slice(ptr)
}

#[deriving(Eq, PartialEq, Ord, PartialOrd)]
pub struct Version(uint, uint, Option<uint>, &'static str);

impl Version {
    fn parse(src: &'static str) -> Result<Version, &'static str> {
        let (version, vendor_info) = src.find(' ').map_or((src, ""), |i| {
            (src.slice_to(i), src.slice_from(i + 1))
        });

        let mut it = version.split('.');
        let major = it.next().and_then(|x| from_str(x));
        let minor = it.next().and_then(|x| from_str(x));
        let revision = it.next().and_then(|x| from_str(x));
        let tail = it.next();

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

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

#[deriving(Show)]
pub struct Info {
    pub vendor: &'static str,
    pub renderer: &'static str,
    pub version: Version,
    pub shading_language: Version,
    pub extensions: HashSet<&'static str>,
}

impl Info {
    fn new() -> Info {
        let info = unsafe {
            let vendor = get_static_string(gl::VENDOR);
            let renderer = get_static_string(gl::RENDERER);
            let version = Version::parse(get_static_string(gl::VERSION)).unwrap();
            let shading_language = Version::parse(get_static_string(gl::SHADING_LANGUAGE_VERSION)).unwrap();
            let extensions = if version >= Version(3, 2, None, "") {
                let num_exts = get_uint(gl::NUM_EXTENSIONS) as gl::types::GLuint;
                range(0, num_exts).map(|i| {
                    str::raw::c_str_to_static_slice(
                        gl::GetStringi(gl::EXTENSIONS, i) as *const i8,
                    )
                }).collect()
            } else {
                // Fallback
                let bytes = gl::GetString(gl::EXTENSIONS);
                str::raw::c_str_to_static_slice(bytes as *const i8)
                    .split(' ').collect()
            };
            Info {
                vendor: vendor,
                renderer: renderer,
                version: version,
                shading_language: shading_language,
                extensions: extensions,
            }
        };
        info!("Vendor: {}", info.vendor);
        info!("Renderer: {}", info.renderer);
        info!("Version: {}", info.version);
        info!("Shading Language: {}", info.shading_language);
        info!("Loaded Extensions:")
        for extension in info.extensions.iter() {
            info!("- {}", *extension);
        }
        info
    }

    pub fn is_extension_supported(&self, s: &str) -> bool {
        self.extensions.contains_equiv(&s)
    }
}

pub struct GlBackEnd {
    caps: super::Capabilities,
    info: Info,
}

impl GlBackEnd {
    pub fn new(provider: &super::GlProvider) -> GlBackEnd {
        gl::load_with(|s| provider.get_proc_address(s));
        let info = Info::new();
        let caps = super::Capabilities {
            shader_model: shade::get_model(),
            max_draw_buffers: get_uint(gl::MAX_DRAW_BUFFERS),
            max_texture_size: get_uint(gl::MAX_TEXTURE_SIZE),
            max_vertex_attributes: get_uint(gl::MAX_VERTEX_ATTRIBS),
            uniform_block_supported: info.is_extension_supported("GL_ARB_uniform_buffer_object"),
            array_buffer_supported: info.is_extension_supported("GL_ARB_vertex_array_object"),
        };
        GlBackEnd {
            caps: caps,
            info: info,
        }
    }

    #[allow(dead_code)]
    fn check(&mut self) {
        debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
    }

    pub fn get_info<'a>(&'a self) -> &'a Info {
        &self.info
    }
}

impl super::ApiBackEnd for GlBackEnd {
    fn get_capabilities<'a>(&'a self) -> &'a super::Capabilities {
        &self.caps
    }

    fn create_buffer(&mut self) -> Buffer {
        let mut name = 0 as Buffer;
        unsafe{
            gl::GenBuffers(1, &mut name);
        }
        info!("\tCreated buffer {}", name);
        name
    }

    fn create_array_buffer(&mut self) -> Result<ArrayBuffer, ()> {
        if self.caps.array_buffer_supported {
            let mut name = 0 as ArrayBuffer;
            unsafe{
                gl::GenVertexArrays(1, &mut name);
            }
            info!("\tCreated array buffer {}", name);
            Ok(name)
        } else {
            error!("\tarray buffer creation unsupported, ignored")
            Err(())
        }
    }

    fn create_shader(&mut self, stage: super::shade::Stage, code: super::shade::ShaderSource) -> Result<Shader, super::shade::CreateShaderError> {
        let (name, info) = shade::create_shader(stage, code, self.get_capabilities().shader_model);
        info.map(|info| {
            let level = if name.is_err() { log::ERROR } else { log::WARN };
            log!(level, "\tShader compile log: {}", info);
        });
        name
    }

    fn create_program(&mut self, shaders: &[Shader]) -> Result<super::shade::ProgramMeta, ()> {
        let (meta, info) = shade::create_program(&self.caps, shaders);
        info.map(|info| {
            let level = if meta.is_err() { log::ERROR } else { log::WARN };
            log!(level, "\tProgram link log: {}", info);
        });
        meta
    }

    fn create_frame_buffer(&mut self) -> FrameBuffer {
        let mut name = 0 as FrameBuffer;
        unsafe{
            gl::GenFramebuffers(1, &mut name);
        }
        info!("\tCreated frame buffer {}", name);
        name
    }

    fn update_buffer<T>(&mut self, buffer: Buffer, data: &[T], usage: super::BufferUsage) {
        gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
        let size = (data.len() * std::mem::size_of::<T>()) as gl::types::GLsizeiptr;
        let raw = data.as_ptr() as *const gl::types::GLvoid;
        let usage = match usage {
            super::UsageStatic  => gl::STATIC_DRAW,
            super::UsageDynamic => gl::DYNAMIC_DRAW,
            super::UsageStream  => gl::STREAM_DRAW,
        };
        unsafe{
            gl::BufferData(gl::ARRAY_BUFFER, size, raw, usage);
        }
    }

    fn process(&mut self, request: super::Request) {
        match request {
            super::CastClear(data) => {
                let mut flags = match data.color {
                    //gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
                    Some(super::target::Color([r,g,b,a])) => {
                        gl::ClearColor(r, g, b, a);
                        gl::COLOR_BUFFER_BIT
                    },
                    None => 0 as gl::types::GLenum
                };
                data.depth.map(|value| {
                    gl::DepthMask(gl::TRUE);
                    gl::ClearDepth(value as gl::types::GLclampd);
                    flags |= gl::DEPTH_BUFFER_BIT;
                });
                data.stencil.map(|value| {
                    gl::StencilMask(-1);
                    gl::ClearStencil(value as gl::types::GLint);
                    flags |= gl::STENCIL_BUFFER_BIT;
                });
                gl::Clear(flags);
            },
            super::CastBindProgram(program) => {
                gl::UseProgram(program);
            },
            super::CastBindArrayBuffer(array_buffer) => {
                if self.caps.array_buffer_supported {
                    gl::BindVertexArray(array_buffer);
                } else {
                    error!("Ignored unsupported GL Request: {}", request)
                }
            },
            super::CastBindAttribute(slot, buffer, count, el_type, stride, offset) => {
                let gl_type = match el_type {
                    a::Int(_, a::U8, a::Unsigned)  => gl::UNSIGNED_BYTE,
                    a::Int(_, a::U8, a::Signed)    => gl::BYTE,
                    a::Int(_, a::U16, a::Unsigned) => gl::UNSIGNED_SHORT,
                    a::Int(_, a::U16, a::Signed)   => gl::SHORT,
                    a::Int(_, a::U32, a::Unsigned) => gl::UNSIGNED_INT,
                    a::Int(_, a::U32, a::Signed)   => gl::INT,
                    a::Float(_, a::F16) => gl::HALF_FLOAT,
                    a::Float(_, a::F32) => gl::FLOAT,
                    a::Float(_, a::F64) => gl::DOUBLE,
                    _ => {
                        error!("Unsupported element type: {}", el_type);
                        return
                    }
                };
                gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
                let offset = offset as *const gl::types::GLvoid;
                match el_type {
                    a::Int(a::IntRaw, _, _) => unsafe {
                        gl::VertexAttribIPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            stride as gl::types::GLint, offset);
                    },
                    a::Int(sub, _, _) => unsafe {
                        gl::VertexAttribPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            if sub == a::IntNormalized {gl::TRUE} else {gl::FALSE},
                            stride as gl::types::GLint, offset);
                    },
                    a::Float(a::FloatDefault, _) => unsafe {
                        gl::VertexAttribPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type, gl::FALSE,
                            stride as gl::types::GLint, offset);
                    },
                    a::Float(a::FloatPrecision, _) => unsafe {
                        gl::VertexAttribLPointer(slot as gl::types::GLuint,
                            count as gl::types::GLint, gl_type,
                            stride as gl::types::GLint, offset);
                    },
                    _ => ()
                }
                gl::EnableVertexAttribArray(slot as gl::types::GLuint);
            },
            super::CastBindIndex(buffer) => {
                gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, buffer);
            },
            super::CastBindFrameBuffer(frame_buffer) => {
                gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, frame_buffer);
            },
            super::CastBindTarget(target, plane) => {
                let attachment = match target {
                    super::target::TargetColor(index) =>
                        gl::COLOR_ATTACHMENT0 + (index as gl::types::GLenum),
                    super::target::TargetDepth => gl::DEPTH_ATTACHMENT,
                    super::target::TargetStencil => gl::STENCIL_ATTACHMENT,
                    super::target::TargetDepthStencil => gl::DEPTH_STENCIL_ATTACHMENT,
                };
                match plane {
                    super::target::PlaneEmpty => gl::FramebufferRenderbuffer
                        (gl::DRAW_FRAMEBUFFER, attachment, gl::RENDERBUFFER, 0),
                    super::target::PlaneSurface(name) => gl::FramebufferRenderbuffer
                        (gl::DRAW_FRAMEBUFFER, attachment, gl::RENDERBUFFER, name),
                    super::target::PlaneTexture(name, level) => gl::FramebufferTexture
                        (gl::DRAW_FRAMEBUFFER, attachment, name, level as gl::types::GLint),
                    super::target::PlaneTextureLayer(name, level, layer) => gl::FramebufferTextureLayer
                        (gl::DRAW_FRAMEBUFFER, attachment, name, level as gl::types::GLint, layer as gl::types::GLint),
                }
            },
            super::CastBindUniformBlock(program, index, loc, buffer) => {
                gl::UniformBlockBinding(program, index as gl::types::GLuint, loc as gl::types::GLuint);
                gl::BindBufferBase(gl::UNIFORM_BUFFER, loc as gl::types::GLuint, buffer);
            },
            super::CastBindUniform(loc, uniform) => {
                shade::bind_uniform(loc as gl::types::GLint, uniform);
            },
            super::CastPrimitiveState(prim) => {
                rast::bind_primitive(prim);
            },
            super::CastDepthState(depth) => {
                rast::bind_depth(depth);
            },
            super::CastBlendState(blend) => {
                rast::bind_blend(blend);
            },
            super::CastUpdateBuffer(buffer, data) => {
                self.update_buffer(buffer, data.as_slice(), super::UsageDynamic);
            },
            super::CastDraw(start, count) => {
                gl::DrawArrays(gl::TRIANGLES,
                    start as gl::types::GLsizei,
                    count as gl::types::GLsizei);
                self.check();
            },
            super::CastDrawIndexed(start, count) => {
                let offset = start * (std::mem::size_of::<u16>() as u16);
                unsafe {
                    gl::DrawElements(gl::TRIANGLES,
                        count as gl::types::GLsizei,
                        gl::UNSIGNED_SHORT,
                        offset as *const gl::types::GLvoid);
                }
                self.check();
            },
            _ => fail!("Unknown GL request: {}", request)
        }
    }
}
<|endoftext|>


<commit_before>use ordermap::OrderMap;
use std::path::Path;
use utils;
use xmltree::Element;

#[derive(Debug)]
pub struct Device {
    pub name: String,
    pub description: String,
    pub modules: OrderMap<String, Module>,
}

fn get_conflict<'a>(map: &OrderMap<u32, &'a Register>, reg: &Register) -> Option<&'a Register> {
    for i in 0..reg.width {
        let addr = reg.offset + i;
        if let Some(old) = map.get(&addr) {
            return Some(old);
        }
    }
    None
}

pub fn parse_dslite(file_name: &Path) -> Device {
    let el = uw!(utils::load_xml(file_name));

    let name = uw!(el.attributes.get("id")).to_owned();
    let description = uw!(el.attributes.get("description")).to_owned();
    let cpu = uw!(el.children.iter().find(|i| i.name == "cpu"));

    let mut cached_modules = OrderMap::new();
    let mut registers = Vec::new();
    for i in &cpu.children {
        if let Some(mut module) = parse_cpu_instance(i, file_name) {
            // Remove all registers from the module before we cache it, since we will fill in the
            // registers after we process duplicates. This two-step caching process also prevents
            // modules with no registers from entering the output list of modules.
            let module_regs = std::mem::replace(&mut module.registers, Vec::new());
            for r in module_regs {
                registers.push(r);
            }
            cached_modules.insert(module.name.clone(), module);
        }
    }

    registers.sort_by(|a, b| {
        if a.width != b.width {
            a.width.cmp(&b.width)
        } else if a.offset != b.offset {
            a.offset.cmp(&b.offset)
        } else {
            a.name.cmp(&b.name)
        }
    });

    let mut modules: OrderMap<String, Module> = OrderMap::new();
    let mut memory: OrderMap<u32, &Register> = OrderMap::new();
    for r in &registers {
        if let Some(old) = get_conflict(&memory, r) {
            if r.width == 2 && old.width == 1 {
                if !memory.contains_key(&r.offset) || !memory.contains_key(&(r.offset + 1)) {
                    eprintln!(
                        "warning: register {} ({}) has missing parts",
                        old.name, r.name
                    );
                }
                eprintln!("erasing {} (keeping {})", r.name, old.name);
                continue;
            } else if r.width == old.width && r.offset == old.offset {
                if r.module == old.module {
                    // In this case the two registers alias the same address space, such as
                    // UCA0CTLW0 and UCA0CTLW0_SPI, which describe the same addresses differently
                    // depending on whether SPI mode is enabled for the eUSCI_A0. We simply include
                    // both registers and mark the second one with an alternate name.
                    let mut reg = r.clone();
                    reg.alternate = Some(old.name.clone());
                    modules.get_mut(&r.module).unwrap().registers.push(reg);
                } else {
                    modules
                        .entry(r.module.clone())
                        .or_insert(cached_modules.get(&r.module).unwrap().clone())
                        .registers
                        .push(r.clone());
                    continue;
                }
            } else {
                panic!("unexpected registers\nOLD {:#?}\nNEW {:#?}\nPlease file an issue at https://github.com/pftbest/msp430_svd/issues", old, r);
            }
        }
        for i in 0..r.width {
            let addr = r.offset + i;
            memory.insert(addr, r);
        }
        modules
            .entry(r.module.clone())
            .or_insert(cached_modules.get(&r.module).unwrap().clone())
            .registers
            .push(r.clone());
    }

    Device {
        name: name,
        description: description,
        modules: modules,
    }
}

#[derive(Debug, Clone)]
pub struct Module {
    pub name: String,
    pub description: String,
    pub registers: Vec<Register>,
}

fn parse_cpu_instance(el: &Element, root_file: &Path) -> Option<Module> {
    assert_eq!(el.name, "instance");
    let base = uw!(utils::parse_u32(uw!(el.attributes.get("baseaddr"))));

    let href = uw!(el.attributes.get("href")).to_owned();
    let root_path = uw!(root_file.parent());
    let module_path = root_path.join(href);

    parse_dslite_module(&module_path, base)
}

fn parse_dslite_module(file_name: &Path, baseaddr: u32) -> Option<Module> {
    let el = uw!(utils::load_xml(file_name));

    assert_eq!(el.name, "module");
    match el.attributes.get("hidden") {
        Some(val) if val == "true" => return None,
        _ => {}
    }

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = el.attributes.get("description").unwrap_or(&name).to_owned();
    let mut registers = el
        .children
        .iter()
        .map(|r| parse_register(r, &name))
        .collect::<Vec<_>>();

    // Rest of the code assumes that the offset value of each register includes the base address of
    // the module, so we add it here.
    for mut reg in &mut registers {
        reg.offset += baseaddr;
    }

    if description.trim().is_empty() {
        description = name.clone();
    }

    Some(Module {
        name: name,
        description: description,
        registers: registers,
    })
}

#[derive(Debug, Clone)]
pub struct Register {
    pub name: String,
    pub description: String,
    pub module: String,
    pub offset: u32,
    pub width: u32,
    pub fields: Vec<Field>,
    pub alternate: Option<String>,
}

fn parse_register(el: &Element, module: &str) -> Register {
    assert_eq!(el.name, "register");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let offset = uw!(utils::parse_u32(uw!(el.attributes.get("offset"))));
    let width = uw!(utils::parse_u32(uw!(el.attributes.get("width"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    assert!(offset < (1 << 16));
    assert!(width == 8 || width == 16 || width == 32);

    let fields = el
        .children
        .iter()
        .map(|f| parse_field(f))
        .collect::<Vec<_>>();

    Register {
        name: name,
        description: description,
        offset: offset,
        width: width / 8,
        module: module.to_owned(),
        fields: fields,
        alternate: None,
    }
}

#[derive(Debug, Clone)]
pub enum Access {
    ReadOnly,
    WriteOnly,
    ReadWrite,
}

#[derive(Debug, Clone)]
pub struct Field {
    pub name: String,
    pub description: String,
    pub offset: u32,
    pub width: u32,
    pub enums: Vec<EnumValue>,
    pub rwa: Access,
}

fn parse_field(el: &Element) -> Field {
    assert_eq!(el.name, "bitfield");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let begin = uw!(utils::parse_u32(uw!(el.attributes.get("begin"))));
    let end = uw!(utils::parse_u32(uw!(el.attributes.get("end"))));
    let width = uw!(utils::parse_u32(uw!(el.attributes.get("width"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    assert_eq!((begin as isize - end as isize).abs() + 1, width as isize);

    let offset = ::std::cmp::min(begin, end);

    let rwa = uw!(el.attributes.get("rwaccess")).to_owned();
    let rwa = match &rwa[..] {
        "R/W" | "RW" => Access::ReadWrite,
        "R" => Access::ReadOnly,
        "W" => Access::WriteOnly,
        _ => panic!("Unexpected read/write value {}", rwa),
    };

    let enums = el
        .children
        .iter()
        .map(|e| parse_enum(e))
        .collect::<Vec<_>>();

    Field {
        name: name,
        description: description,
        offset: offset,
        width: width,
        enums: enums,
        rwa,
    }
}

#[derive(Debug, Clone)]
pub struct EnumValue {
    pub name: String,
    pub description: String,
    pub value: u32,
}

fn parse_enum(el: &Element) -> EnumValue {
    assert_eq!(el.name, "bitenum");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let value = uw!(utils::parse_u32(uw!(el.attributes.get("value"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    EnumValue {
        name: name,
        description: description,
        value: value,
    }
}
<commit_msg>Fix bug where duplicate registers are added<commit_after>use ordermap::OrderMap;
use std::path::Path;
use utils;
use xmltree::Element;

#[derive(Debug)]
pub struct Device {
    pub name: String,
    pub description: String,
    pub modules: OrderMap<String, Module>,
}

fn get_conflict<'a>(map: &OrderMap<u32, &'a Register>, reg: &Register) -> Option<&'a Register> {
    for i in 0..reg.width {
        let addr = reg.offset + i;
        if let Some(old) = map.get(&addr) {
            return Some(old);
        }
    }
    None
}

pub fn parse_dslite(file_name: &Path) -> Device {
    let el = uw!(utils::load_xml(file_name));

    let name = uw!(el.attributes.get("id")).to_owned();
    let description = uw!(el.attributes.get("description")).to_owned();
    let cpu = uw!(el.children.iter().find(|i| i.name == "cpu"));

    let mut cached_modules = OrderMap::new();
    let mut registers = Vec::new();
    for i in &cpu.children {
        if let Some(mut module) = parse_cpu_instance(i, file_name) {
            // Remove all registers from the module before we cache it, since we will fill in the
            // registers after we process duplicates. This two-step caching process also prevents
            // modules with no registers from entering the output list of modules.
            let module_regs = std::mem::replace(&mut module.registers, Vec::new());
            for r in module_regs {
                registers.push(r);
            }
            cached_modules.insert(module.name.clone(), module);
        }
    }

    registers.sort_by(|a, b| {
        if a.width != b.width {
            a.width.cmp(&b.width)
        } else if a.offset != b.offset {
            a.offset.cmp(&b.offset)
        } else {
            a.name.cmp(&b.name)
        }
    });

    let mut modules: OrderMap<String, Module> = OrderMap::new();
    let mut memory: OrderMap<u32, &Register> = OrderMap::new();
    for r in &registers {
        if let Some(old) = get_conflict(&memory, r) {
            if r.width == 2 && old.width == 1 {
                if !memory.contains_key(&r.offset) || !memory.contains_key(&(r.offset + 1)) {
                    eprintln!(
                        "warning: register {} ({}) has missing parts",
                        old.name, r.name
                    );
                }
                eprintln!("erasing {} (keeping {})", r.name, old.name);
                continue;
            } else if r.width == old.width && r.offset == old.offset {
                if r.module == old.module {
                    // In this case the two registers alias the same address space, such as
                    // UCA0CTLW0 and UCA0CTLW0_SPI, which describe the same addresses differently
                    // depending on whether SPI mode is enabled for the eUSCI_A0. We simply include
                    // both registers and mark the second one with an alternate name.
                    let mut reg = r.clone();
                    reg.alternate = Some(old.name.clone());
                    modules.get_mut(&r.module).unwrap().registers.push(reg);
                    continue;
                } else {
                    modules
                        .entry(r.module.clone())
                        .or_insert(cached_modules.get(&r.module).unwrap().clone())
                        .registers
                        .push(r.clone());
                    continue;
                }
            } else {
                panic!("unexpected registers\nOLD {:#?}\nNEW {:#?}\nPlease file an issue at https://github.com/pftbest/msp430_svd/issues", old, r);
            }
        }
        for i in 0..r.width {
            let addr = r.offset + i;
            memory.insert(addr, r);
        }
        modules
            .entry(r.module.clone())
            .or_insert(cached_modules.get(&r.module).unwrap().clone())
            .registers
            .push(r.clone());
    }

    Device {
        name: name,
        description: description,
        modules: modules,
    }
}

#[derive(Debug, Clone)]
pub struct Module {
    pub name: String,
    pub description: String,
    pub registers: Vec<Register>,
}

fn parse_cpu_instance(el: &Element, root_file: &Path) -> Option<Module> {
    assert_eq!(el.name, "instance");
    let base = uw!(utils::parse_u32(uw!(el.attributes.get("baseaddr"))));

    let href = uw!(el.attributes.get("href")).to_owned();
    let root_path = uw!(root_file.parent());
    let module_path = root_path.join(href);

    parse_dslite_module(&module_path, base)
}

fn parse_dslite_module(file_name: &Path, baseaddr: u32) -> Option<Module> {
    let el = uw!(utils::load_xml(file_name));

    assert_eq!(el.name, "module");
    match el.attributes.get("hidden") {
        Some(val) if val == "true" => return None,
        _ => {}
    }

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = el.attributes.get("description").unwrap_or(&name).to_owned();
    let mut registers = el
        .children
        .iter()
        .map(|r| parse_register(r, &name))
        .collect::<Vec<_>>();

    // Rest of the code assumes that the offset value of each register includes the base address of
    // the module, so we add it here.
    for mut reg in &mut registers {
        reg.offset += baseaddr;
    }

    if description.trim().is_empty() {
        description = name.clone();
    }

    Some(Module {
        name: name,
        description: description,
        registers: registers,
    })
}

#[derive(Debug, Clone)]
pub struct Register {
    pub name: String,
    pub description: String,
    pub module: String,
    pub offset: u32,
    pub width: u32,
    pub fields: Vec<Field>,
    pub alternate: Option<String>,
}

fn parse_register(el: &Element, module: &str) -> Register {
    assert_eq!(el.name, "register");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let offset = uw!(utils::parse_u32(uw!(el.attributes.get("offset"))));
    let width = uw!(utils::parse_u32(uw!(el.attributes.get("width"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    assert!(offset < (1 << 16));
    assert!(width == 8 || width == 16 || width == 32);

    let fields = el
        .children
        .iter()
        .map(|f| parse_field(f))
        .collect::<Vec<_>>();

    Register {
        name: name,
        description: description,
        offset: offset,
        width: width / 8,
        module: module.to_owned(),
        fields: fields,
        alternate: None,
    }
}

#[derive(Debug, Clone)]
pub enum Access {
    ReadOnly,
    WriteOnly,
    ReadWrite,
}

#[derive(Debug, Clone)]
pub struct Field {
    pub name: String,
    pub description: String,
    pub offset: u32,
    pub width: u32,
    pub enums: Vec<EnumValue>,
    pub rwa: Access,
}

fn parse_field(el: &Element) -> Field {
    assert_eq!(el.name, "bitfield");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let begin = uw!(utils::parse_u32(uw!(el.attributes.get("begin"))));
    let end = uw!(utils::parse_u32(uw!(el.attributes.get("end"))));
    let width = uw!(utils::parse_u32(uw!(el.attributes.get("width"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    assert_eq!((begin as isize - end as isize).abs() + 1, width as isize);

    let offset = ::std::cmp::min(begin, end);

    let rwa = uw!(el.attributes.get("rwaccess")).to_owned();
    let rwa = match &rwa[..] {
        "R/W" | "RW" => Access::ReadWrite,
        "R" => Access::ReadOnly,
        "W" => Access::WriteOnly,
        _ => panic!("Unexpected read/write value {}", rwa),
    };

    let enums = el
        .children
        .iter()
        .map(|e| parse_enum(e))
        .collect::<Vec<_>>();

    Field {
        name: name,
        description: description,
        offset: offset,
        width: width,
        enums: enums,
        rwa,
    }
}

#[derive(Debug, Clone)]
pub struct EnumValue {
    pub name: String,
    pub description: String,
    pub value: u32,
}

fn parse_enum(el: &Element) -> EnumValue {
    assert_eq!(el.name, "bitenum");

    let name = uw!(el.attributes.get("id")).to_owned();
    let mut description = uw!(el.attributes.get("description")).to_owned();
    let value = uw!(utils::parse_u32(uw!(el.attributes.get("value"))));

    if description.trim().is_empty() {
        description = name.clone();
    }

    EnumValue {
        name: name,
        description: description,
        value: value,
    }
}
<|endoftext|>


<commit_before><commit_msg>Mark new tests with the correct feature flags<commit_after><|endoftext|>